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 12ec3662c3b016..3c39aa06531028 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 35f51d16168c0e..d96d19cfe9c423 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 3e3f465c4eface..90e41b2c4b725f 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 bc0ba9723d14aa..77d8db31ea214b 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 8d53041ab7b7e4..1d69829dca0838 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 dc80389e2b5722..63ee5d6dfc24e5 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 dd06a7db4f7b0f..e6f5d3b7c7c9a6 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 0bfd23afa61a5f..15fc60dab496b7 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 74dd0cff432468..b35cdab96372df 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 ee43dba8acc0e2..b6fac385d91067 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 6879330cf99b38..6407ec064f42a3 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 5f5f0f00ed90ee..b5fd679eeda17b 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 2535e33ae76419..58565971d8c779 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 d51cd6ca3cea3a..943ab43e2223a7 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 102cd109de8278..5a4b15abbf4453 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 5437ba78b56790..2b449f6b54a7f9 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 52fe30888509aa..0552626134e146 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 884afaad5fc09f..f61b7bb83ad2a0 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 c7ae97a184f723..6482cb36ce087f 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 112aaebd81e514..712e9929c7d8a9 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,9 +1,8 @@ --- type: example summary: Learn how to query D1 from a Python Worker -tags: +languages: - 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 94fc04159c2e65..3e1e9656bbe0f3 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 4b6b91bacb7cf3..1459f28382f5b2 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 9b25d5811d93e5..a0e1b5ec7ef1b1 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 87bff40cff208d..4cfe3db44b46f7 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 3bbcb77ecb3867..e128095dabf618 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 8765b2d0643a42..f958454afc9346 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 e3dfe27279f3f5..a746f2d49304ea 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 c6eafab8d2ac25..f42e993bb15e57 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. - - - - - +