From 6f0aa43eb0a44aad8263aef341075be80fd4b65a Mon Sep 17 00:00:00 2001 From: kodster28 Date: Fri, 7 Mar 2025 14:59:47 -0600 Subject: [PATCH 1/2] [Chore] Tag cleanup --- .../access-api-examples/access-group.mdx | 9 +- .../access-api-examples/any-service-token.mdx | 5 +- .../authentication-method.mdx | 9 +- .../access-api-examples/common-name.mdx | 9 +- .../access-api-examples/country-code.mdx | 9 +- .../access-api-examples/email-domain.mdx | 9 +- .../access-api-examples/email.mdx | 9 +- .../access-api-examples/entra-group.mdx | 2 - .../access-api-examples/everyone.mdx | 5 +- .../access-api-examples/github-org.mdx | 2 - .../access-api-examples/gsuite-group.mdx | 2 - .../access-api-examples/ip-range.mdx | 9 +- .../access-api-examples/mtls-certificate.mdx | 5 +- .../access-api-examples/okta-group.mdx | 2 - .../access-api-examples/saml-attribute.mdx | 13 +- .../access-api-examples/service-token.mdx | 9 +- src/content/docs/d1/examples/d1-and-hono.mdx | 56 ++-- src/content/docs/d1/examples/d1-and-remix.mdx | 8 +- .../docs/d1/examples/d1-and-sveltekit.mdx | 65 ++-- .../examples/query-d1-from-python-workers.mdx | 1 - .../pages/functions/examples/cors-headers.mdx | 30 +- .../migrations/migrating-from-firebase.mdx | 5 +- .../migrating-jekyll-from-github-pages.mdx | 4 +- ...ating-turnstile-waf-and-bot-management.mdx | 123 ++++---- .../docs/workflows/examples/backup-d1.mdx | 147 ++++----- src/content/docs/workflows/examples/index.mdx | 5 +- .../docs/workflows/examples/send-invoices.mdx | 282 +++++++++--------- .../docs/workflows/examples/twilio.mdx | 17 +- 28 files changed, 405 insertions(+), 446 deletions(-) diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/access-group.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/access-group.mdx index 12ec3662c3b0164..3c39aa065310287 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/access-group.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/access-group.mdx @@ -1,20 +1,17 @@ --- type: example summary: Use a pre-existing rule group. -tags: - - Rule group title: Rule group pcx_content_type: example sidebar: order: 4 description: Use a pre-existing rule group. - --- ```json { - "group": { - "id": "aa0a4aab-672b-4bdb-bc33-a59f1130a11f" - } + "group": { + "id": "aa0a4aab-672b-4bdb-bc33-a59f1130a11f" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/any-service-token.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/any-service-token.mdx index 35f51d16168c0ea..d96d19cfe9c4235 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/any-service-token.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/any-service-token.mdx @@ -2,21 +2,18 @@ type: example summary: The request will need to present the headers for any service token created for this account. -tags: - - Any valid service token title: Any valid service token pcx_content_type: example sidebar: order: 4 description: The request will need to present the headers for any service token created for this account. - --- The request will need to present the headers for any service token created for this account ```json { - "any_valid_service_token": {} + "any_valid_service_token": {} } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/authentication-method.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/authentication-method.mdx index 3e3f465c4eface3..90e41b2c4b725f4 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/authentication-method.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/authentication-method.mdx @@ -1,20 +1,17 @@ --- type: example summary: Allow access based on the "amr" identifier. -tags: - - Authentication Method title: Authentication method pcx_content_type: example sidebar: order: 4 description: Allow access based on the "amr" identifier. - --- ```json { - "auth_method": { - "auth_method": "hwk" - } + "auth_method": { + "auth_method": "hwk" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/common-name.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/common-name.mdx index bc0ba9723d14aa3..77d8db31ea214b9 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/common-name.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/common-name.mdx @@ -2,21 +2,18 @@ type: example summary: The request will need to present a valid certificate with an expected common name. -tags: - - Common name title: Common name pcx_content_type: example sidebar: order: 4 description: The request will need to present a valid certificate with an expected common name. - --- ```json { - "common_name": { - "common_name": "james@example.com" - } + "common_name": { + "common_name": "james@example.com" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/country-code.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/country-code.mdx index 8d53041ab7b7e45..1d69829dca08382 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/country-code.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/country-code.mdx @@ -1,20 +1,17 @@ --- type: example summary: Allow a specific country. -tags: - - Country Code title: Country Code pcx_content_type: example sidebar: order: 4 description: Allow a specific country. - --- ```json { - "geo": { - "country_code": "US" - } + "geo": { + "country_code": "US" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email-domain.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email-domain.mdx index dc80389e2b57229..63ee5d6dfc24e55 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email-domain.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email-domain.mdx @@ -1,20 +1,17 @@ --- type: example summary: Allow an entire email domain. -tags: - - Email domain title: Email domain pcx_content_type: example sidebar: order: 4 description: Allow an entire email domain. - --- ```json { - "email_domain": { - "domain": "cloudflare.com" - } + "email_domain": { + "domain": "cloudflare.com" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email.mdx index dd06a7db4f7b0f6..e6f5d3b7c7c9a63 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/email.mdx @@ -1,20 +1,17 @@ --- type: example summary: Allow a specific email address. -tags: - - Email title: Email pcx_content_type: example sidebar: order: 4 description: Allow a specific email address. - --- ```json { - "email": { - "email": "james@example.com" - } + "email": { + "email": "james@example.com" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/entra-group.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/entra-group.mdx index 0bfd23afa61a5f1..15fc60dab496b71 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/entra-group.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/entra-group.mdx @@ -1,8 +1,6 @@ --- type: example summary: Allow members of a Microsoft Entra group. The ID is the group UUID (`id`) in Microsoft Entra ID. -tags: - - Microsoft Entra Group title: Microsoft Entra Group pcx_content_type: example sidebar: diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/everyone.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/everyone.mdx index 74dd0cff4324680..b35cdab96372dfb 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/everyone.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/everyone.mdx @@ -1,18 +1,15 @@ --- type: example summary: Allow anyone to log in. -tags: - - Everyone title: Everyone pcx_content_type: example sidebar: order: 4 description: Allow anyone to log in. - --- ```json { - "everyone": {} + "everyone": {} } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/github-org.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/github-org.mdx index ee43dba8acc0e24..b6fac385d910677 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/github-org.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/github-org.mdx @@ -1,8 +1,6 @@ --- type: example summary: Allow members of a specific GitHub organization. -tags: - - GitHub Organization title: GitHub™ Organization pcx_content_type: example sidebar: diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/gsuite-group.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/gsuite-group.mdx index 6879330cf99b38b..6407ec064f42a3b 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/gsuite-group.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/gsuite-group.mdx @@ -1,8 +1,6 @@ --- type: example summary: Allow members of a specific G Suite group. -tags: - - G Suite Group title: G Suite Group pcx_content_type: example sidebar: diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/ip-range.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/ip-range.mdx index 5f5f0f00ed90ee4..b5fd679eeda17b2 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/ip-range.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/ip-range.mdx @@ -1,20 +1,17 @@ --- type: example summary: Allow an IP range. -tags: - - IP range title: IP range pcx_content_type: example sidebar: order: 4 description: Allow an IP range. - --- ```json { - "ip": { - "ip": "127.0.0.1/32" - } + "ip": { + "ip": "127.0.0.1/32" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/mtls-certificate.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/mtls-certificate.mdx index 2535e33ae764193..58565971d8c779e 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/mtls-certificate.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/mtls-certificate.mdx @@ -1,18 +1,15 @@ --- type: example summary: The request will need to present a valid certificate. -tags: - - mTLS certificate title: mTLS certificate pcx_content_type: example sidebar: order: 4 description: The request will need to present a valid certificate. - --- ```json { - "certificate": {} + "certificate": {} } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/okta-group.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/okta-group.mdx index d51cd6ca3cea3a9..943ab43e2223a7e 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/okta-group.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/okta-group.mdx @@ -1,8 +1,6 @@ --- type: example summary: Allow members of an Okta Group. -tags: - - Okta Group title: Okta Group pcx_content_type: example sidebar: diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/saml-attribute.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/saml-attribute.mdx index 102cd109de82780..5a4b15abbf44537 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/saml-attribute.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/saml-attribute.mdx @@ -1,22 +1,19 @@ --- type: example summary: Allow users with specific SAML attributes. -tags: - - SAML Attribute title: SAML Attribute pcx_content_type: example sidebar: order: 4 description: Allow users with specific SAML attributes. - --- ```json { - "saml": { - "attribute_name": "group", - "attribute_value": "admins", - "identity_provider_id": "ca298b82-93b5-41bf-bc2d-10493f09b761" - } + "saml": { + "attribute_name": "group", + "attribute_value": "admins", + "identity_provider_id": "ca298b82-93b5-41bf-bc2d-10493f09b761" + } } ``` diff --git a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/service-token.mdx b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/service-token.mdx index 5437ba78b56790d..2b449f6b54a7f93 100644 --- a/src/content/docs/cloudflare-one/api-terraform/access-api-examples/service-token.mdx +++ b/src/content/docs/cloudflare-one/api-terraform/access-api-examples/service-token.mdx @@ -1,20 +1,17 @@ --- type: example summary: The request will need to present the correct service token headers. -tags: - - Service token title: Service token pcx_content_type: example sidebar: order: 4 description: The request will need to present the correct service token headers. - --- ```json { - "service_token": { - "token_id": "e9808c3a-705c-4afc-a507-6e4b083ff399" - } + "service_token": { + "token_id": "e9808c3a-705c-4afc-a507-6e4b083ff399" + } } ``` diff --git a/src/content/docs/d1/examples/d1-and-hono.mdx b/src/content/docs/d1/examples/d1-and-hono.mdx index 52fe30888509aa2..0552626134e1460 100644 --- a/src/content/docs/d1/examples/d1-and-hono.mdx +++ b/src/content/docs/d1/examples/d1-and-hono.mdx @@ -3,24 +3,22 @@ type: example summary: Query D1 from the Hono web framework tags: - Hono - - D1 pcx_content_type: example title: Query D1 from Hono sidebar: order: 3 description: Query D1 from the Hono web framework - --- -import { TabItem, Tabs } from "~/components" +import { TabItem, Tabs } from "~/components"; Hono is a fast web framework for building API-first applications, and it includes first-class support for both [Workers](/workers/) and [Pages](/pages/). When using Workers: -* Ensure you have configured your [Wrangler configuration file](/d1/get-started/#3-bind-your-worker-to-your-d1-database) to bind your D1 database to your Worker. -* You can access your D1 databases via Hono's [`Context`](https://hono.dev/api/context) parameter: [bindings](https://hono.dev/getting-started/cloudflare-workers#bindings) are exposed on `context.env`. If you configured a [binding](/pages/functions/bindings/#d1-databases) named `DB`, then you would access [D1 Workers Binding API](/d1/worker-api/prepared-statements/) methods via `c.env.DB`. -* Refer to the Hono documentation for [Cloudflare Workers](https://hono.dev/getting-started/cloudflare-workers). +- Ensure you have configured your [Wrangler configuration file](/d1/get-started/#3-bind-your-worker-to-your-d1-database) to bind your D1 database to your Worker. +- You can access your D1 databases via Hono's [`Context`](https://hono.dev/api/context) parameter: [bindings](https://hono.dev/getting-started/cloudflare-workers#bindings) are exposed on `context.env`. If you configured a [binding](/pages/functions/bindings/#d1-databases) named `DB`, then you would access [D1 Workers Binding API](/d1/worker-api/prepared-statements/) methods via `c.env.DB`. +- Refer to the Hono documentation for [Cloudflare Workers](https://hono.dev/getting-started/cloudflare-workers). If you are using [Pages Functions](/pages/functions/): @@ -37,24 +35,24 @@ import { Hono } from "hono"; // This ensures c.env.DB is correctly typed type Bindings = { - DB: D1Database; + DB: D1Database; }; const app = new Hono<{ Bindings: Bindings }>(); // Accessing D1 is via the c.env.YOUR_BINDING property app.get("/query/users/:id", async (c) => { - const userId = c.req.param("id"); - try { - let { results } = await c.env.DB.prepare( - "SELECT * FROM users WHERE user_id = ?", - ) - .bind(userId) - .all(); - return c.json(results); - } catch (e) { - return c.json({ err: e.message }, 500); - } + const userId = c.req.param("id"); + try { + let { results } = await c.env.DB.prepare( + "SELECT * FROM users WHERE user_id = ?", + ) + .bind(userId) + .all(); + return c.json(results); + } catch (e) { + return c.json({ err: e.message }, 500); + } }); // Export our Hono app: Hono automatically exports a @@ -72,17 +70,17 @@ const app = new Hono().basePath("/api"); // Accessing D1 is via the c.env.YOUR_BINDING property app.get("/query/users/:id", async (c) => { - const userId = c.req.param("id"); - try { - let { results } = await c.env.DB.prepare( - "SELECT * FROM users WHERE user_id = ?", - ) - .bind(userId) - .all(); - return c.json(results); - } catch (e) { - return c.json({ err: e.message }, 500); - } + const userId = c.req.param("id"); + try { + let { results } = await c.env.DB.prepare( + "SELECT * FROM users WHERE user_id = ?", + ) + .bind(userId) + .all(); + return c.json(results); + } catch (e) { + return c.json({ err: e.message }, 500); + } }); // Export the Hono instance as a Pages onRequest function diff --git a/src/content/docs/d1/examples/d1-and-remix.mdx b/src/content/docs/d1/examples/d1-and-remix.mdx index 884afaad5fc09f4..f61b7bb83ad2a0f 100644 --- a/src/content/docs/d1/examples/d1-and-remix.mdx +++ b/src/content/docs/d1/examples/d1-and-remix.mdx @@ -3,16 +3,14 @@ type: example summary: Query your D1 database from a Remix application. tags: - Remix - - D1 pcx_content_type: example title: Query D1 from Remix sidebar: order: 2 description: Query your D1 database from a Remix application. - --- -import { TabItem, Tabs } from "~/components" +import { TabItem, Tabs } from "~/components"; Remix is a full-stack web framework that operates on both client and server. You can query your D1 database(s) from Remix using Remix's [data loading](https://remix.run/docs/en/main/guides/data-loading) API with the [`useLoaderData`](https://remix.run/docs/en/main/hooks/use-loader-data) hook. @@ -24,8 +22,8 @@ To set up a new Remix site on Cloudflare Pages that can query D1: The following example shows you how to define a Remix [`loader`](https://remix.run/docs/en/main/route/loader) that has a binding to a D1 database. -* Bindings are passed through on the `context.env` parameter passed to a `LoaderFunction`. -* If you configured a [binding](/pages/functions/bindings/#d1-databases) named `DB`, then you would access [D1 Workers Binding API](/d1/worker-api/prepared-statements/) methods via `context.env.DB`. +- Bindings are passed through on the `context.env` parameter passed to a `LoaderFunction`. +- If you configured a [binding](/pages/functions/bindings/#d1-databases) named `DB`, then you would access [D1 Workers Binding API](/d1/worker-api/prepared-statements/) methods via `context.env.DB`. diff --git a/src/content/docs/d1/examples/d1-and-sveltekit.mdx b/src/content/docs/d1/examples/d1-and-sveltekit.mdx index c7ae97a184f723c..6482cb36ce087fc 100644 --- a/src/content/docs/d1/examples/d1-and-sveltekit.mdx +++ b/src/content/docs/d1/examples/d1-and-sveltekit.mdx @@ -4,16 +4,14 @@ summary: Query a D1 database from a SvelteKit application. tags: - SvelteKit - Svelte - - D1 pcx_content_type: example title: Query D1 from SvelteKit sidebar: order: 3 description: Query a D1 database from a SvelteKit application. - --- -import { TabItem, Tabs } from "~/components" +import { TabItem, Tabs } from "~/components"; [SvelteKit](https://kit.svelte.dev/) is a full-stack framework that combines the Svelte front-end framework with Vite for server-side capabilities and rendering. You can query D1 from SvelteKit by configuring a [server endpoint](https://kit.svelte.dev/docs/routing#server) with a binding to your D1 database(s). @@ -26,8 +24,8 @@ To set up a new SvelteKit site on Cloudflare Pages that can query D1: The following example shows you how to create a server endpoint configured to query D1. -* Bindings are available on the `platform` parameter passed to each endpoint, via `platform.env.BINDING_NAME`. -* With SvelteKit's [file-based routing](https://kit.svelte.dev/docs/routing), the server endpoint defined in `src/routes/api/users/+server.ts` is available at `/api/users` within your SvelteKit app. +- Bindings are available on the `platform` parameter passed to each endpoint, via `platform.env.BINDING_NAME`. +- With SvelteKit's [file-based routing](https://kit.svelte.dev/docs/routing), the server endpoint defined in `src/routes/api/users/+server.ts` is available at `/api/users` within your SvelteKit app. The example also shows you how to configure both your app-wide types within `src/app.d.ts` to recognize your `D1Database` binding, import the `@sveltejs/adapter-cloudflare` adapter into `svelte.config.js`, and configure it to apply to all of your routes. @@ -38,10 +36,10 @@ import type { RequestHandler } from "@sveltejs/kit"; /** @type {import('@sveltejs/kit').RequestHandler} */ export async function GET({ request, platform }) { - let result = await platform.env.DB.prepare( - "SELECT * FROM users LIMIT 5" - ).run(); - return new Response(JSON.stringify(result)); + let result = await platform.env.DB.prepare( + "SELECT * FROM users LIMIT 5", + ).run(); + return new Response(JSON.stringify(result)); } ``` @@ -49,40 +47,39 @@ export async function GET({ request, platform }) { // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - interface Platform { - env: { - DB: D1Database; - }; - context: { - waitUntil(promise: Promise): void; - }; - caches: CacheStorage & { default: Cache }; - } - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + interface Platform { + env: { + DB: D1Database; + }; + context: { + waitUntil(promise: Promise): void; + }; + caches: CacheStorage & { default: Cache }; + } + } } export {}; ``` ```js -import adapter from '@sveltejs/adapter-cloudflare'; +import adapter from "@sveltejs/adapter-cloudflare"; export default { - kit: { - adapter: adapter({ - // See below for an explanation of these options - routes: { - include: ['/*'], - exclude: [''] - } - }) - } + kit: { + adapter: adapter({ + // See below for an explanation of these options + routes: { + include: ["/*"], + exclude: [""], + }, + }), + }, }; - ``` diff --git a/src/content/docs/d1/examples/query-d1-from-python-workers.mdx b/src/content/docs/d1/examples/query-d1-from-python-workers.mdx index 112aaebd81e5144..910cbaa0f62ffaa 100644 --- a/src/content/docs/d1/examples/query-d1-from-python-workers.mdx +++ b/src/content/docs/d1/examples/query-d1-from-python-workers.mdx @@ -3,7 +3,6 @@ type: example summary: Learn how to query D1 from a Python Worker tags: - Python - - D1 pcx_content_type: example title: Query D1 from Python Workers sidebar: diff --git a/src/content/docs/pages/functions/examples/cors-headers.mdx b/src/content/docs/pages/functions/examples/cors-headers.mdx index 94fc04159c2e658..3e1e9656bbe0f34 100644 --- a/src/content/docs/pages/functions/examples/cors-headers.mdx +++ b/src/content/docs/pages/functions/examples/cors-headers.mdx @@ -2,13 +2,12 @@ type: example summary: A Pages Functions for appending CORS headers. tags: - - CORS + - Headers pcx_content_type: configuration title: Adding CORS headers sidebar: order: 1002 description: A Pages Functions for appending CORS headers. - --- This example is a snippet from our Cloudflare Pages Template repo. @@ -16,23 +15,22 @@ This example is a snippet from our Cloudflare Pages Template repo. ```ts // Respond to OPTIONS method export const onRequestOptions: PagesFunction = async () => { - return new Response(null, { - status: 204, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': '*', - 'Access-Control-Allow-Methods': 'GET, OPTIONS', - 'Access-Control-Max-Age': '86400', - }, - }); + return new Response(null, { + status: 204, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "*", + "Access-Control-Allow-Methods": "GET, OPTIONS", + "Access-Control-Max-Age": "86400", + }, + }); }; // Set CORS to all /api responses export const onRequest: PagesFunction = async (context) => { - const response = await context.next(); - response.headers.set('Access-Control-Allow-Origin', '*'); - response.headers.set('Access-Control-Max-Age', '86400'); - return response; + const response = await context.next(); + response.headers.set("Access-Control-Allow-Origin", "*"); + response.headers.set("Access-Control-Max-Age", "86400"); + return response; }; - ``` diff --git a/src/content/docs/pages/migrations/migrating-from-firebase.mdx b/src/content/docs/pages/migrations/migrating-from-firebase.mdx index 4b6b91bacb7cf34..1459f28382f5b29 100644 --- a/src/content/docs/pages/migrations/migrating-from-firebase.mdx +++ b/src/content/docs/pages/migrations/migrating-from-firebase.mdx @@ -3,9 +3,6 @@ updated: 2020-09-28 difficulty: Beginner pcx_content_type: tutorial title: Migrating from Firebase -tags: - - Firebase - --- In this tutorial, you will learn how to migrate an existing Firebase application to Cloudflare Pages. You should already have an existing project deployed on Firebase that you would like to host on Cloudflare Pages. @@ -18,7 +15,7 @@ You will use these to tell Cloudflare Pages how to deploy your project. If you h ```json title="firebase.json" { - "public": "public" + "public": "public" } ``` diff --git a/src/content/docs/pages/migrations/migrating-jekyll-from-github-pages.mdx b/src/content/docs/pages/migrations/migrating-jekyll-from-github-pages.mdx index 9b25d5811d93e52..a0e1b5ec7ef1b10 100644 --- a/src/content/docs/pages/migrations/migrating-jekyll-from-github-pages.mdx +++ b/src/content/docs/pages/migrations/migrating-jekyll-from-github-pages.mdx @@ -3,8 +3,6 @@ updated: 2021-07-27 difficulty: Beginner pcx_content_type: tutorial title: Migrating a Jekyll-based site from GitHub Pages -tags: - - Jekyll languages: - Ruby --- @@ -49,7 +47,7 @@ Your existing Jekyll-based repository must specify a `Gemfile` (Ruby's dependenc Specifically, you will need to create a `Gemfile` and install the `github-pages` gem, which includes all of the dependencies that the GitHub Pages environment assumes. -[Version 2 of the Pages build environment](/pages/configuration/build-image/#languages-and-runtime) will use Ruby 3.2.2 for the default Jekyll build. Please make sure your local development environment is compatible. +[Version 2 of the Pages build environment](/pages/configuration/build-image/#languages-and-runtime) will use Ruby 3.2.2 for the default Jekyll build. Please make sure your local development environment is compatible. ```sh title="Set Ruby Version" brew install ruby@3.2 diff --git a/src/content/docs/turnstile/tutorials/integrating-turnstile-waf-and-bot-management.mdx b/src/content/docs/turnstile/tutorials/integrating-turnstile-waf-and-bot-management.mdx index 87bff40cff208df..4cfe3db44b46f70 100644 --- a/src/content/docs/turnstile/tutorials/integrating-turnstile-waf-and-bot-management.mdx +++ b/src/content/docs/turnstile/tutorials/integrating-turnstile-waf-and-bot-management.mdx @@ -4,15 +4,13 @@ pcx_content_type: tutorial updated: 2024-09-17 difficulty: Beginner content_type: 📝 Tutorial -tags: - - Turnstile +products: - Web Application Firewall - Bot Management languages: - JavaScript sidebar: order: 3 - --- This tutorial will guide you on how to integrate Cloudflare Turnstile, [Web Application Firewall (WAF)](/waf/), and [Bot Management](/bots/get-started/bm-subscription/) into an existing authentication system. This combination creates a robust defense against various threats, including automated attacks and malicious login attempts. @@ -46,11 +44,10 @@ If your site is on Cloudflare's network and subscribed to an Enterprise plan, yo 2. Go to **Security** > **WAF**. 3. Create a new custom WAF rule by selecting "Edit expression": - Field: "Bot Score" - - Operator: "less than or equal to" - - Value: "30" + - Operator: "less than or equal to" + - Value: "30" - Action: "Managed Challenge" - This configuration challenges requests with a low bot score, leveraging network signals to identify potential threats before they reach your application. You may customize the score threshold based on your specific use case. ## Set up Cloudflare Turnstile @@ -65,7 +62,6 @@ Turnstile can be used on any site, regardless of whether it is on Cloudflare's n Turnstile adds an extra layer of security by analyzing browser and client-side signals, complementing the server-side checks performed by WAF and Bot Management. - ### Enable the option to use the existing clearance cookie If your site is on Cloudflare, you can enable the option to use the existing [clearance cookie](/turnstile/concepts/pre-clearance-support/) in Turnstile's settings. This integration allows Turnstile to use the clearance cookie as part of its determination of whether a user should receive a challenge. This integration is optional, but recommended if you already are using WAF and Bot Management. @@ -80,72 +76,85 @@ Add the Turnstile widget to your existing login form: ```html
- - -
- + + +
+
- + ``` Replace `YOUR_SITE_KEY` with your actual Turnstile site key. - ## Handle the login request In your existing authentication route, add Turnstile validation: ```typescript -async function validateTurnstileToken(ip: string, token: string, secret: string): Promise { - const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ ip, secret, response: token }) - }); - - const outcome = await response.json(); - return outcome.success; +async function validateTurnstileToken( + ip: string, + token: string, + secret: string, +): Promise { + const response = await fetch( + "https://challenges.cloudflare.com/turnstile/v0/siteverify", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ip, secret, response: token }), + }, + ); + + const outcome = await response.json(); + return outcome.success; } // Assume that this is a TypeScript route handler. // You may replace this with a different implementation, // based on your language or framework export async function onRequestPost(context) { - const { request, env } = context; - const { username, password, token } = await request.json(); - - // Validate Turnstile token - const secretKey = env.TURNSTILE_SECRET_KEY; - const ip = request.headers.get('CF-Connecting-IP'); - const turnstileValid = await validateTurnstileToken(ip, token, secretKey); - if (!turnstileValid) { - // Return back to the login page with an error message - return Response.redirect('/login', 302, { - headers: { - 'Location': '/login?error=invalid-turnstile-token' - } - }); - } - - // Perform your existing authentication logic here - const isValidLogin = await checkCredentials(username, password); - - if (isValidLogin) { - return new Response(JSON.stringify({ message: 'Login successful' }), { - status: 200, - headers: { 'Content-Type': 'application/json' } - }); - } else { - return new Response(JSON.stringify({ error: 'Invalid credentials' }), { - status: 401, - headers: { 'Content-Type': 'application/json' } - }); - } + const { request, env } = context; + const { username, password, token } = await request.json(); + + // Validate Turnstile token + const secretKey = env.TURNSTILE_SECRET_KEY; + const ip = request.headers.get("CF-Connecting-IP"); + const turnstileValid = await validateTurnstileToken(ip, token, secretKey); + if (!turnstileValid) { + // Return back to the login page with an error message + return Response.redirect("/login", 302, { + headers: { + Location: "/login?error=invalid-turnstile-token", + }, + }); + } + + // Perform your existing authentication logic here + const isValidLogin = await checkCredentials(username, password); + + if (isValidLogin) { + return new Response(JSON.stringify({ message: "Login successful" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } else { + return new Response(JSON.stringify({ error: "Invalid credentials" }), { + status: 401, + headers: { "Content-Type": "application/json" }, + }); + } } -async function checkCredentials(username: string, password: string): Promise { - // Your existing credential checking logic +async function checkCredentials( + username: string, + password: string, +): Promise { + // Your existing credential checking logic } ``` @@ -155,8 +164,8 @@ This setup ensures that the Turnstile token is validated on the server-side befo After deployment, you will want to test your integration. Because your bot score will be low, you will probably not receive a challenge. However, you can add additional rules as needed to force a redirect to the challenge page. Some options to do this are: -1) Add a WAF rule that always forwards your IP address to the challenge page. -2) Add a WAF rule that checks for the presence of a query parameter, such as `?challenge=true`. +1. Add a WAF rule that always forwards your IP address to the challenge page. +2. Add a WAF rule that checks for the presence of a query parameter, such as `?challenge=true`. ## Best practices @@ -172,4 +181,4 @@ If you are interested in customizing Turnstile, refer to the resources below for - [Client-side rendering](/turnstile/get-started/client-side-rendering/). Learn how to customize how and when Turnstile renders in your user interface, to better fit your application's needs and user experience. - [Server-side validation](/turnstile/get-started/server-side-validation/). Learn how Turnstile's API works, including request parameters, as well as how to handle different types of responses, including error codes. -- [Turnstile Analytics](/turnstile/turnstile-analytics/). Learn how to view Turnstile's analytics in the Cloudflare dashboard. This includes metrics on the number of challenges issued, as well as the CSR (Challenge Solve Rate). \ No newline at end of file +- [Turnstile Analytics](/turnstile/turnstile-analytics/). Learn how to view Turnstile's analytics in the Cloudflare dashboard. This includes metrics on the number of challenges issued, as well as the CSR (Challenge Solve Rate). diff --git a/src/content/docs/workflows/examples/backup-d1.mdx b/src/content/docs/workflows/examples/backup-d1.mdx index 3bbcb77ecb38671..e128095dabf6182 100644 --- a/src/content/docs/workflows/examples/backup-d1.mdx +++ b/src/content/docs/workflows/examples/backup-d1.mdx @@ -1,21 +1,20 @@ --- type: example summary: Export a D1 database into R2 storage with Workflows -tags: +products: - Workflows - D1 - R2 languages: - Typescript -pcx_content_type: configuration +pcx_content_type: example title: Export and save D1 database sidebar: order: 3 description: Send invoice when shopping cart is checked out and paid for - --- -import { TabItem, Tabs, WranglerConfig } from "~/components" +import { TabItem, Tabs, WranglerConfig } from "~/components"; In this example, we implement a Workflow periodically triggered by a [Cron Trigger](/workers/configuration/cron-triggers). That Workflow initiates a backup for a D1 database using the REST API, and then stores the SQL dump in an [R2](/r2) bucket. @@ -33,79 +32,93 @@ This example provides simplified steps for backing up a [D1](/d1) database to he ```ts import { - WorkflowEntrypoint, - WorkflowStep, - WorkflowEvent, + WorkflowEntrypoint, + WorkflowStep, + WorkflowEvent, } from "cloudflare:workers"; - // We are using R2 to store the D1 backup type Env = { - BACKUP_WORKFLOW: Workflow; - D1_REST_API_TOKEN: string; - BACKUP_BUCKET: R2Bucket; + BACKUP_WORKFLOW: Workflow; + D1_REST_API_TOKEN: string; + BACKUP_BUCKET: R2Bucket; }; // Workflow parameters: we expect accountId and databaseId type Params = { - accountId: string; - databaseId: string; + accountId: string; + databaseId: string; }; // Workflow logic export class backupWorkflow extends WorkflowEntrypoint { - async run(event: WorkflowEvent, step: WorkflowStep) { - const { accountId, databaseId } = event.payload; - - const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`; - const method = "POST"; - const headers = new Headers(); - headers.append("Content-Type", "application/json"); - headers.append("Authorization", `Bearer ${this.env.D1_REST_API_TOKEN}`); - - const bookmark = await step.do(`Starting backup for ${databaseId}`, async () => { - const payload = { output_format: "polling" }; - - const res = await fetch(url, { method, headers, body: JSON.stringify(payload) }); - const { result } = (await res.json()) as any; - - // If we don't get `at_bookmark` we throw to retry the step - if (!result?.at_bookmark) throw new Error("Missing `at_bookmark`"); - - return result.at_bookmark; - }); - - await step.do("Check backup status and store it on R2", async () => { - const payload = { current_bookmark: bookmark }; - - const res = await fetch(url, { method, headers, body: JSON.stringify(payload) }); - const { result } = (await res.json()) as any; - - // The endpoint sends `signed_url` when the backup is ready to download. - // If we don't get `signed_url` we throw to retry the step. - if (!result?.signed_url) throw new Error("Missing `signed_url`"); - - const dumpResponse = await fetch(result.signed_url); - if (!dumpResponse.ok) throw new Error("Failed to fetch dump file"); - - // Finally, stream the file directly to R2 - await this.env.BACKUP_BUCKET.put(result.filename, dumpResponse.body); - }); - } + async run(event: WorkflowEvent, step: WorkflowStep) { + const { accountId, databaseId } = event.payload; + + const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`; + const method = "POST"; + const headers = new Headers(); + headers.append("Content-Type", "application/json"); + headers.append("Authorization", `Bearer ${this.env.D1_REST_API_TOKEN}`); + + const bookmark = await step.do( + `Starting backup for ${databaseId}`, + async () => { + const payload = { output_format: "polling" }; + + const res = await fetch(url, { + method, + headers, + body: JSON.stringify(payload), + }); + const { result } = (await res.json()) as any; + + // If we don't get `at_bookmark` we throw to retry the step + if (!result?.at_bookmark) throw new Error("Missing `at_bookmark`"); + + return result.at_bookmark; + }, + ); + + await step.do("Check backup status and store it on R2", async () => { + const payload = { current_bookmark: bookmark }; + + const res = await fetch(url, { + method, + headers, + body: JSON.stringify(payload), + }); + const { result } = (await res.json()) as any; + + // The endpoint sends `signed_url` when the backup is ready to download. + // If we don't get `signed_url` we throw to retry the step. + if (!result?.signed_url) throw new Error("Missing `signed_url`"); + + const dumpResponse = await fetch(result.signed_url); + if (!dumpResponse.ok) throw new Error("Failed to fetch dump file"); + + // Finally, stream the file directly to R2 + await this.env.BACKUP_BUCKET.put(result.filename, dumpResponse.body); + }); + } } export default { - async fetch(req: Request, env: Env): Promise { - return new Response("Not found", { status: 404 }); - }, - async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext) { - const params: Params = { - accountId: "{accountId}", - databaseId: "{databaseId}", - }; - const instance = await env.BACKUP_WORKFLOW.create({ params }); - console.log(`Started workflow: ${instance.id}`); - }, + async fetch(req: Request, env: Env): Promise { + return new Response("Not found", { status: 404 }); + }, + async scheduled( + controller: ScheduledController, + env: Env, + ctx: ExecutionContext, + ) { + const params: Params = { + accountId: "{accountId}", + databaseId: "{databaseId}", + }; + const instance = await env.BACKUP_WORKFLOW.create({ params }); + console.log(`Started workflow: ${instance.id}`); + }, }; ``` @@ -113,10 +126,10 @@ Here is a minimal package.json: ```json { - "devDependencies": { - "@cloudflare/workers-types": "^4.20241224.0", - "wrangler": "^3.99.0" - } + "devDependencies": { + "@cloudflare/workers-types": "^4.20241224.0", + "wrangler": "^3.99.0" + } } ``` @@ -143,4 +156,4 @@ bucket_name = "d1-backups" crons = [ "0 0 * * *" ] ``` - \ No newline at end of file + diff --git a/src/content/docs/workflows/examples/index.mdx b/src/content/docs/workflows/examples/index.mdx index 8765b2d0643a420..f958454afc93463 100644 --- a/src/content/docs/workflows/examples/index.mdx +++ b/src/content/docs/workflows/examples/index.mdx @@ -7,11 +7,10 @@ sidebar: order: 6 group: hideIndex: true - --- -import { GlossaryTooltip, ListExamples } from "~/components" +import { GlossaryTooltip, ListExamples } from "~/components"; Explore the following examples for Workflows. - + diff --git a/src/content/docs/workflows/examples/send-invoices.mdx b/src/content/docs/workflows/examples/send-invoices.mdx index e3dfe27279f3f59..a746f2d49304ea3 100644 --- a/src/content/docs/workflows/examples/send-invoices.mdx +++ b/src/content/docs/workflows/examples/send-invoices.mdx @@ -1,19 +1,18 @@ --- type: example summary: Send invoice when shopping cart is checked out and paid for -tags: +products: - Workflows - D1 - Email Routing -pcx_content_type: configuration +pcx_content_type: example title: Pay cart and send invoice sidebar: order: 3 description: Send invoice when shopping cart is checked out and paid for - --- -import { TabItem, Tabs, WranglerConfig } from "~/components" +import { TabItem, Tabs, WranglerConfig } from "~/components"; In this example, we implement a Workflow for an e-commerce website that is triggered every time a shopping cart is created. @@ -25,23 +24,23 @@ This is a simplified example of processing a shopping cart. We would assume more ```ts import { - WorkflowEntrypoint, - WorkflowStep, - WorkflowEvent, + WorkflowEntrypoint, + WorkflowStep, + WorkflowEvent, } from "cloudflare:workers"; import { EmailMessage } from "cloudflare:email"; import { createMimeMessage } from "mimetext"; // We are using Email Routing to send emails out and D1 for our cart database type Env = { - CART_WORKFLOW: Workflow; - SEND_EMAIL: any; - DB: any; + CART_WORKFLOW: Workflow; + SEND_EMAIL: any; + DB: any; }; // Workflow parameters: we expect a cartId type Params = { - cartId: string; + cartId: string; }; // Adjust this to your Cloudflare zone using Email Routing @@ -49,141 +48,140 @@ const merchantEmail = "merchant@example.com"; // Uses mimetext npm to generate Email const genEmail = (email: string, amount: number) => { - const msg = createMimeMessage(); - msg.setSender({ name: "Pet shop", addr: merchantEmail }); - msg.setRecipient(email); - msg.setSubject("You invoice"); - msg.addMessage({ - contentType: "text/plain", - data: `Your invoice for ${amount} has been paid. Your products will be shipped shortly.`, - }); - - return new EmailMessage(merchantEmail, email, msg.asRaw()); + const msg = createMimeMessage(); + msg.setSender({ name: "Pet shop", addr: merchantEmail }); + msg.setRecipient(email); + msg.setSubject("You invoice"); + msg.addMessage({ + contentType: "text/plain", + data: `Your invoice for ${amount} has been paid. Your products will be shipped shortly.`, + }); + + return new EmailMessage(merchantEmail, email, msg.asRaw()); }; // Workflow logic export class cartInvoicesWorkflow extends WorkflowEntrypoint { - async run(event: WorkflowEvent, step: WorkflowStep) { - await step.sleep("sleep for a while", "10 seconds"); - - // Retrieve the cart from the D1 database - // if the cart hasn't been checked out yet retry every 2 minutes, 10 times, otherwise give up - const cart = await step.do( - "retrieve cart", - { - retries: { - limit: 10, - delay: 2000 * 60, - backoff: "constant", - }, - timeout: "30 seconds", - }, - async () => { - const { results } = await this.env.DB.prepare( - `SELECT * FROM cart WHERE id = ?`, - ) - .bind(event.payload.cartId) - .all(); - // should return { checkedOut: true, amount: 250 , account: { email: "celsomartinho@gmail.com" }}; - if(results[0].checkedOut === false) { - throw new Error("cart hasn't been checked out yet"); - } - return results[0]; - }, - ); - - // Proceed to payment, retry 10 times every minute or give up - const payment = await step.do( - "payment", - { - retries: { - limit: 10, - delay: 1000 * 60, - backoff: "constant", - }, - timeout: "30 seconds", - }, - async () => { - let resp = await fetch("https://payment-processor.example.com/", { - method: "POST", - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - body: JSON.stringify({ amount: cart.amount }), - }); - - if (!resp.ok) { - throw new Error("payment has failed"); - } - - return { success: true, amount: cart.amount }; - }, - ); - - // Send invoice to the customer, retry 10 times every 5 minutes or give up + async run(event: WorkflowEvent, step: WorkflowStep) { + await step.sleep("sleep for a while", "10 seconds"); + + // Retrieve the cart from the D1 database + // if the cart hasn't been checked out yet retry every 2 minutes, 10 times, otherwise give up + const cart = await step.do( + "retrieve cart", + { + retries: { + limit: 10, + delay: 2000 * 60, + backoff: "constant", + }, + timeout: "30 seconds", + }, + async () => { + const { results } = await this.env.DB.prepare( + `SELECT * FROM cart WHERE id = ?`, + ) + .bind(event.payload.cartId) + .all(); + // should return { checkedOut: true, amount: 250 , account: { email: "celsomartinho@gmail.com" }}; + if (results[0].checkedOut === false) { + throw new Error("cart hasn't been checked out yet"); + } + return results[0]; + }, + ); + + // Proceed to payment, retry 10 times every minute or give up + const payment = await step.do( + "payment", + { + retries: { + limit: 10, + delay: 1000 * 60, + backoff: "constant", + }, + timeout: "30 seconds", + }, + async () => { + let resp = await fetch("https://payment-processor.example.com/", { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + body: JSON.stringify({ amount: cart.amount }), + }); + + if (!resp.ok) { + throw new Error("payment has failed"); + } + + return { success: true, amount: cart.amount }; + }, + ); + + // Send invoice to the customer, retry 10 times every 5 minutes or give up // Requires that cart.account.email has previously been validated in Email Routing, // See https://developers.cloudflare.com/email-routing/email-workers/ - await step.do( - "send invoice", - { - retries: { - limit: 10, - delay: 5000 * 60, - backoff: "constant", - }, - timeout: "30 seconds", - }, - async () => { - const message = genEmail(cart.account.email, payment.amount); - try { - await this.env.SEND_EMAIL.send(message); - } catch (e) { - throw new Error("failed to send invoice"); - } - }, - ); - - } + await step.do( + "send invoice", + { + retries: { + limit: 10, + delay: 5000 * 60, + backoff: "constant", + }, + timeout: "30 seconds", + }, + async () => { + const message = genEmail(cart.account.email, payment.amount); + try { + await this.env.SEND_EMAIL.send(message); + } catch (e) { + throw new Error("failed to send invoice"); + } + }, + ); + } } // Default page for admin // Remove in production export default { - async fetch(req: Request, env: Env): Promise { - let url = new URL(req.url); - - let id = new URL(req.url).searchParams.get("instanceId"); - - // Get the status of an existing instance, if provided - if (id) { - let instance = await env.CART_WORKFLOW.get(id); - return Response.json({ - status: await instance.status(), - }); - } - - if (url.pathname.startsWith("/new")) { - let instance = await env.CART_WORKFLOW.create({ - params: { - cartId: "123" - }, - }); - return Response.json({ - id: instance.id, - details: await instance.status(), - }); - } - - return new Response( - `new instance or add ?instanceId=...`, - { - headers: { - "content-type": "text/html;charset=UTF-8", - }, - }, - ); - }, + async fetch(req: Request, env: Env): Promise { + let url = new URL(req.url); + + let id = new URL(req.url).searchParams.get("instanceId"); + + // Get the status of an existing instance, if provided + if (id) { + let instance = await env.CART_WORKFLOW.get(id); + return Response.json({ + status: await instance.status(), + }); + } + + if (url.pathname.startsWith("/new")) { + let instance = await env.CART_WORKFLOW.create({ + params: { + cartId: "123", + }, + }); + return Response.json({ + id: instance.id, + details: await instance.status(), + }); + } + + return new Response( + `new instance or add ?instanceId=...`, + { + headers: { + "content-type": "text/html;charset=UTF-8", + }, + }, + ); + }, }; ``` @@ -191,13 +189,13 @@ Here's a minimal package.json: ```json { - "devDependencies": { - "@cloudflare/workers-types": "^4.20241022.0", - "wrangler": "^3.83.0" - }, - "dependencies": { - "mimetext": "^3.0.24" - } + "devDependencies": { + "@cloudflare/workers-types": "^4.20241022.0", + "wrangler": "^3.83.0" + }, + "dependencies": { + "mimetext": "^3.0.24" + } } ``` @@ -220,4 +218,4 @@ class_name = "cartInvoicesWorkflow" name = "SEND_EMAIL" ``` - \ No newline at end of file + diff --git a/src/content/docs/workflows/examples/twilio.mdx b/src/content/docs/workflows/examples/twilio.mdx index c6eafab8d2ac251..f42e993bb15e57b 100644 --- a/src/content/docs/workflows/examples/twilio.mdx +++ b/src/content/docs/workflows/examples/twilio.mdx @@ -1,22 +1,21 @@ --- type: example summary: Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks. -tags: - - Workflows +products: - Workers -pcx_content_type: configuration +pcx_content_type: example title: Integrate Workflows with Twilio sidebar: order: 4 description: Integrate Workflows with Twilio. Learn how to receive and send text messages and phone calls via APIs and Webhooks. --- -import { Stream } from "~/components" +import { Stream } from "~/components"; Using the following [repository](https://github.com/craigsdennis/twilio-cloudflare-workflow), learn how to integrate Cloudflare Workflows with Twilio, a popular cloud communications platform that enables developers to integrate messaging, voice, video, and authentication features into applications via APIs. By the end of the video tutorial, you will become familiarized with the process of setting up Cloudflare Workflows to seamlessly interact with Twilio's APIs, enabling you to build interesting communication features directly into your applications. - - - - - + From db503d92b202ebefac797725c4e52580ef8c3250 Mon Sep 17 00:00:00 2001 From: Kody Jackson Date: Mon, 10 Mar 2025 08:36:39 -0500 Subject: [PATCH 2/2] Update src/content/docs/d1/examples/query-d1-from-python-workers.mdx --- src/content/docs/d1/examples/query-d1-from-python-workers.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/d1/examples/query-d1-from-python-workers.mdx b/src/content/docs/d1/examples/query-d1-from-python-workers.mdx index 910cbaa0f62ffaa..712e9929c7d8a94 100644 --- a/src/content/docs/d1/examples/query-d1-from-python-workers.mdx +++ b/src/content/docs/d1/examples/query-d1-from-python-workers.mdx @@ -1,7 +1,7 @@ --- type: example summary: Learn how to query D1 from a Python Worker -tags: +languages: - Python pcx_content_type: example title: Query D1 from Python Workers