├── basic-auth ├── src │ ├── env.d.ts │ ├── pages │ │ └── index.astro │ └── middleware.ts ├── tsconfig.json ├── astro.config.mjs ├── package.json ├── public │ └── favicon.svg └── README.md ├── hello-world ├── src │ ├── env.d.ts │ ├── middleware.ts │ └── pages │ │ └── index.astro ├── tsconfig.json ├── astro.config.mjs ├── package.json ├── public │ └── favicon.svg └── README.md ├── jwt-auth ├── src │ ├── env.d.ts │ ├── constant.ts │ ├── pages │ │ ├── api │ │ │ ├── protected.ts │ │ │ ├── logout.ts │ │ │ └── login.ts │ │ ├── protected.astro │ │ └── index.astro │ ├── layouts │ │ └── Layout.astro │ └── middleware.ts ├── tsconfig.json ├── astro.config.mjs ├── package.json ├── public │ └── favicon.svg └── README.md ├── redirect ├── src │ ├── env.d.ts │ ├── middleware.ts │ ├── layouts │ │ └── Layout.astro │ └── pages │ │ ├── redirected.astro │ │ └── index.astro ├── tsconfig.json ├── astro.config.mjs ├── .gitignore ├── package.json ├── public │ └── favicon.svg └── README.md ├── feature-flag-posthog ├── src │ ├── env.d.ts │ ├── layouts │ │ └── Layout.astro │ ├── components │ │ └── Card.astro │ ├── middleware.ts │ └── pages │ │ ├── post-hog.astro │ │ └── index.astro ├── tsconfig.json ├── astro.config.mjs ├── .gitignore ├── package.json ├── public │ └── favicon.svg └── README.md ├── locals-object ├── tsconfig.json ├── astro.config.mjs ├── src │ ├── env.d.ts │ ├── middleware.ts │ ├── layouts │ │ └── Layout.astro │ └── pages │ │ └── index.astro ├── .gitignore ├── package.json ├── public │ └── favicon.svg └── README.md ├── multiple-middleware ├── src │ ├── env.d.ts │ ├── middleware │ │ ├── auth.ts │ │ ├── index.ts │ │ └── validate.ts │ ├── layouts │ │ └── Layout.astro │ └── pages │ │ └── index.astro ├── tsconfig.json ├── astro.config.mjs ├── .gitignore ├── package.json ├── public │ └── favicon.svg └── README.md ├── .gitignore └── README.md /basic-auth/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /hello-world/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /jwt-auth/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /redirect/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /jwt-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /basic-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strictest" 3 | } -------------------------------------------------------------------------------- /feature-flag-posthog/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /hello-world/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /locals-object/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /multiple-middleware/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /redirect/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strictest" 3 | } -------------------------------------------------------------------------------- /feature-flag-posthog/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /multiple-middleware/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /jwt-auth/src/constant.ts: -------------------------------------------------------------------------------- 1 | export const TOKEN = "token"; 2 | export const PUBLIC_ROUTES = ["/", "/api/login", "/api/logout"]; 3 | -------------------------------------------------------------------------------- /hello-world/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /redirect/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /locals-object/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /feature-flag-posthog/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /multiple-middleware/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /basic-auth/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({ 5 | output: "server", 6 | }); 7 | -------------------------------------------------------------------------------- /locals-object/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare namespace App { 3 | interface Locals { 4 | stringValue: string; 5 | functionValue: () => string; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /multiple-middleware/src/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | export const auth = defineMiddleware((context, next) => { 4 | console.log("In auth middleware"); 5 | return next(); 6 | }); 7 | -------------------------------------------------------------------------------- /multiple-middleware/src/middleware/index.ts: -------------------------------------------------------------------------------- 1 | import { sequence } from "astro/middleware"; 2 | 3 | import { auth } from "./auth"; 4 | import { validate } from "./validate"; 5 | 6 | export const onRequest = sequence(auth, validate); 7 | -------------------------------------------------------------------------------- /multiple-middleware/src/middleware/validate.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | export const validate = defineMiddleware((context, next) => { 4 | console.log("In validate middleware"); 5 | return next(); 6 | }); 7 | -------------------------------------------------------------------------------- /jwt-auth/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | 3 | import netlify from "@astrojs/netlify/functions"; 4 | 5 | // https://astro.build/config 6 | export default defineConfig({ 7 | output: "server", 8 | adapter: netlify() 9 | }); -------------------------------------------------------------------------------- /jwt-auth/src/pages/api/protected.ts: -------------------------------------------------------------------------------- 1 | import type { APIRoute } from "astro"; 2 | 3 | export const get: APIRoute = (ctx) => { 4 | return new Response( 5 | JSON.stringify({ 6 | message: "You're logged in!", 7 | }), 8 | { status: 200 } 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /redirect/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /redirect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redirect", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "astro": "^2.10.14" 14 | } 15 | } -------------------------------------------------------------------------------- /locals-object/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /feature-flag-posthog/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "astro": "^2.10.14" 14 | } 15 | } -------------------------------------------------------------------------------- /locals-object/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "locals-object", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "astro": "^2.10.14" 14 | } 15 | } -------------------------------------------------------------------------------- /multiple-middleware/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /hello-world/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | export const onRequest = defineMiddleware((context, next) => { 4 | console.log("MIDDLEWARE"); 5 | 6 | return new Response( 7 | JSON.stringify({ 8 | message: "Hello world", 9 | }), 10 | { 11 | status: 200, 12 | } 13 | ); 14 | }); 15 | -------------------------------------------------------------------------------- /multiple-middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multiple-middleware", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "astro": "^2.10.14" 14 | } 15 | } -------------------------------------------------------------------------------- /basic-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-middleware-examples-basic-auth", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "astro": "^2.10.7" 14 | } 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .netlify/ 4 | 5 | # generated types 6 | .astro/ 7 | 8 | # dependencies 9 | node_modules/ 10 | 11 | # logs 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # environment variables 18 | .env 19 | .env.production 20 | 21 | # macOS-specific files 22 | .DS_Store 23 | 24 | # code editor(s) 25 | .vscode/ -------------------------------------------------------------------------------- /basic-auth/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Astro 11 | 12 | 13 |

Astro

14 | 15 | 16 | -------------------------------------------------------------------------------- /locals-object/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | export const onRequest = defineMiddleware((context, next) => { 4 | // add a string value to the locals object 5 | context.locals.stringValue = "Hello Middleware"; 6 | 7 | // add a method to the locals object 8 | context.locals.functionValue = () => "This is a function return value"; 9 | 10 | return next(); 11 | }); 12 | -------------------------------------------------------------------------------- /jwt-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jwt-auth", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/netlify": "^2.6.0", 14 | "astro": "^2.10.9", 15 | "jose": "^4.14.4", 16 | "nanoid": "^4.0.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /hello-world/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | console.log("Rendering the index page"); 3 | --- 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Astro 12 | 13 | 14 |

Astro

15 | 16 | 17 | -------------------------------------------------------------------------------- /feature-flag-posthog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "--template-basic", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@growthbook/growthbook": "^0.28.0", 14 | "astro": "^3.0.12", 15 | "eventsource": "^2.0.2", 16 | "posthog-node": "^3.1.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jwt-auth/src/pages/api/logout.ts: -------------------------------------------------------------------------------- 1 | import type { APIRoute } from "astro"; 2 | import { TOKEN } from "../../constant"; 3 | 4 | export const post: APIRoute = async (ctx) => { 5 | try { 6 | // unset cookies 7 | ctx.cookies.set(TOKEN, "", { 8 | httpOnly: true, 9 | maxAge: 0, 10 | path: "/", 11 | }); 12 | 13 | return new Response( 14 | JSON.stringify({ 15 | message: "You're logged out!", 16 | }), 17 | { 18 | status: 200, 19 | } 20 | ); 21 | } catch (error) { 22 | console.debug(error); 23 | 24 | return new Response( 25 | JSON.stringify({ 26 | message: "Logout failed", 27 | }), 28 | { 29 | status: 500, 30 | } 31 | ); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /jwt-auth/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /redirect/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /basic-auth/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /hello-world/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /locals-object/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /multiple-middleware/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /feature-flag-posthog/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /redirect/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | const INDEX_PATH = "/"; 4 | 5 | export const onRequest = defineMiddleware((context, next) => { 6 | /** 7 | * The middleware runs every time a page or endpoint is about to be rendered. 8 | * Only redirect if this is the home page 9 | */ 10 | if (context.url.pathname === INDEX_PATH) { 11 | return Response.redirect(new URL("/redirected", context.url), 302); 12 | 13 | /** 14 | * You may also redirect using `context.redirect` as shown below: 15 | * ========================================= 16 | * context.redirect("/redirected", 302); 17 | * ========================================= 18 | * Note that this only works in SSR mode 19 | */ 20 | } 21 | 22 | return next(); 23 | }); 24 | -------------------------------------------------------------------------------- /jwt-auth/README.md: -------------------------------------------------------------------------------- 1 | # Astro JWT Middleware 2 | 3 | ![Demo](https://i.imgur.com/gmrMxHp.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /locals-object/README.md: -------------------------------------------------------------------------------- 1 | # Astro Locals Object 2 | 3 | ![Demo](https://i.imgur.com/FDAgI2z.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /redirect/README.md: -------------------------------------------------------------------------------- 1 | # Astro Redirect Middleware 2 | 3 | ![Demo](https://i.imgur.com/vVZr6W5.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /basic-auth/README.md: -------------------------------------------------------------------------------- 1 | # Astro Basic auth Middleware 2 | 3 | ![Demo](https://i.imgur.com/sKm7KCW.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Astro Hello World Middleware 2 | 3 | ![Demo](https://i.imgur.com/8UpU9jF.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /multiple-middleware/README.md: -------------------------------------------------------------------------------- 1 | # Astro Multiple Middleware 2 | 3 | ![Demo](https://i.imgur.com/EFDzPA4.png) 4 | 5 | ## 🧞 Commands 6 | 7 | All commands are run from the root of the project, from a terminal: 8 | 9 | | Command | Action | 10 | | :------------------------ | :----------------------------------------------- | 11 | | `npm install` | Installs dependencies | 12 | | `npm run dev` | Starts local dev server at `localhost:3000` | 13 | | `npm run build` | Build your production site to `./dist/` | 14 | | `npm run preview` | Preview your build locally, before deploying | 15 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 16 | | `npm run astro -- --help` | Get help using the Astro CLI | 17 | -------------------------------------------------------------------------------- /basic-auth/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro/middleware"; 2 | 3 | export const onRequest = defineMiddleware((context, next) => { 4 | // If a basic auth header is present, it wil take the string form: "Basic authValue" 5 | const basicAuth = context.request.headers.get("authorization"); 6 | 7 | if (basicAuth) { 8 | // Get the auth value from string "Basic authValue" 9 | const authValue = basicAuth.split(" ")[1] ?? "username:password"; 10 | 11 | // Decode the Base64 encoded string via atob (https://developer.mozilla.org/en-US/docs/Web/API/atob) 12 | // Get the username and password. NB: the decoded string is in the form "username:password" 13 | const [username, pwd] = atob(authValue).split(":"); 14 | 15 | // check if the username and password are valid 16 | if (username === "admin" && pwd === "admin") { 17 | // forward request 18 | return next(); 19 | } 20 | } 21 | 22 | return new Response("Auth required", { 23 | status: 401, 24 | headers: { 25 | "WWW-authenticate": 'Basic realm="Secure Area"', 26 | }, 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /redirect/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 40 | -------------------------------------------------------------------------------- /locals-object/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 40 | -------------------------------------------------------------------------------- /multiple-middleware/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 40 | -------------------------------------------------------------------------------- /jwt-auth/src/pages/api/login.ts: -------------------------------------------------------------------------------- 1 | import { nanoid } from "nanoid"; 2 | import { SignJWT } from "jose"; 3 | import type { APIRoute } from "astro"; 4 | import { TOKEN } from "../../constant"; 5 | 6 | const secret = new TextEncoder().encode(import.meta.env.JWT_SECRET_KEY); 7 | 8 | export const post: APIRoute = async (ctx) => { 9 | try { 10 | const token = await new SignJWT({}) 11 | .setProtectedHeader({ alg: "HS256" }) 12 | .setJti(nanoid()) 13 | .setIssuedAt() 14 | .setExpirationTime("2h") 15 | .sign(secret); 16 | 17 | // set cookies 18 | ctx.cookies.set(TOKEN, token, { 19 | httpOnly: true, 20 | path: "/", 21 | maxAge: 60 * 60 * 2, // 2 hours in seconds 22 | }); 23 | 24 | return new Response( 25 | JSON.stringify({ 26 | message: "You're logged in!", 27 | }), 28 | { 29 | status: 200, 30 | } 31 | ); 32 | } catch (error) { 33 | console.debug(error); 34 | 35 | return new Response( 36 | JSON.stringify({ 37 | message: "Login failed", 38 | }), 39 | { 40 | status: 500, 41 | } 42 | ); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /jwt-auth/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 45 | -------------------------------------------------------------------------------- /feature-flag-posthog/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 52 | -------------------------------------------------------------------------------- /feature-flag-posthog/src/components/Card.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | body: string; 5 | href: string; 6 | } 7 | 8 | const { href, title, body } = Astro.props; 9 | --- 10 | 11 | 22 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Demo](https://i.imgur.com/dKpBx5G.png) 2 | 3 | # Astro Middleware Examples 4 | 5 | - [Hello world](https://github.com/understanding-astro/astro-middleware-examples/tree/master/hello-world) 6 | - [Locals object](https://github.com/understanding-astro/astro-middleware-examples/tree/master/locals-object) 7 | - [Basic Auth](https://github.com/understanding-astro/astro-middleware-examples/tree/master/basic-auth) 8 | - [JWT Auth](https://github.com/understanding-astro/astro-middleware-examples/tree/master/jwt-auth) 9 | - [Redirects](https://github.com/understanding-astro/astro-middleware-examples/tree/master/redirect) 10 | - [Multiple middleware](https://github.com/understanding-astro/astro-middleware-examples/tree/master/multiple-middleware) 11 | - [Feature flag (PostHog)](https://github.com/understanding-astro/astro-middleware-examples/tree/master/feature-flag-posthog) 12 | - Coming soon ... 13 | 14 | ## 🧞 Commands 15 | 16 | Change directory to the required example and run any of the following commands from a terminal: 17 | 18 | | Command | Action | 19 | | :------------------------ | :----------------------------------------------- | 20 | | `npm install` | Installs dependencies | 21 | | `npm run dev` | Starts local dev server at `localhost:3000` | 22 | | `npm run build` | Build your production site to `./dist/` | 23 | | `npm run preview` | Preview your build locally, before deploying | 24 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 25 | | `npm run astro -- --help` | Get help using the Astro CLI | 26 | -------------------------------------------------------------------------------- /feature-flag-posthog/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro:middleware"; 2 | 3 | import { PostHog } from "posthog-node"; 4 | 5 | const HOME_PAGE_PATH = "/"; 6 | 7 | /** 8 | * Create the Post hog client and pass your project API key 9 | * Add POSTHOG_CLIENT to "src/.env" 10 | */ 11 | const client = new PostHog(import.meta.env.POSTHOG_CLIENT); 12 | 13 | export const onRequest = defineMiddleware(async (context, next) => { 14 | /** 15 | * Early return. We will only check the feature flag with requests 16 | * to the homepage 17 | */ 18 | if (context.url.pathname !== HOME_PAGE_PATH) { 19 | return next(); 20 | } 21 | 22 | try { 23 | /** 24 | * Retrieve the feature toggle for your feature flag 25 | * In this case, "astro-middleware-demo" 26 | */ 27 | const isEnabled = await client.isFeatureEnabled( 28 | "astro-middleware-demo", 29 | "" // Pass a static-user-id 30 | ); 31 | 32 | console.log({ isEnabled }); 33 | 34 | if (isEnabled) { 35 | console.log("Feature is ENABLED!"); 36 | 37 | /** 38 | * When the feature flag is toggled on, redirect users who access the homepage, 39 | * to the "/post-hog" page 40 | */ 41 | return Response.redirect(new URL("/post-hog", context.url), 302); 42 | 43 | /** 44 | * Otherwise, handle the request as usual 45 | */ 46 | return next(); 47 | } 48 | 49 | /** 50 | * Feature flag NOT enabled? Handle the request as usual 51 | */ 52 | 53 | console.log("Feature is DISABLED!"); 54 | return next(); 55 | } catch (error) { 56 | console.error("Failed to load feature flag from PostHog"); 57 | 58 | /** 59 | * Handle the request as usual 60 | */ 61 | return next(); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /jwt-auth/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { errors, jwtVerify } from "jose"; 2 | import { defineMiddleware } from "astro/middleware"; 3 | import { TOKEN, PUBLIC_ROUTES } from "./constant"; 4 | 5 | const secret = new TextEncoder().encode(import.meta.env.JWT_SECRET_KEY); 6 | 7 | const verifyAuth = async (token?: string) => { 8 | if (!token) { 9 | return { 10 | status: "unauthorized", 11 | msg: "please pass a request token", 12 | } as const; 13 | } 14 | 15 | try { 16 | const jwtVerifyResult = await jwtVerify(token, secret); 17 | 18 | return { 19 | status: "authorized", 20 | payload: jwtVerifyResult.payload, 21 | msg: "successfully verified auth token", 22 | } as const; 23 | } catch (err) { 24 | if (err instanceof errors.JOSEError) { 25 | return { status: "error", msg: err.message } as const; 26 | } 27 | 28 | console.debug(err); 29 | return { status: "error", msg: "could not validate auth token" } as const; 30 | } 31 | }; 32 | 33 | export const onRequest = defineMiddleware(async (context, next) => { 34 | // Ignore auth validation for public routes 35 | if (PUBLIC_ROUTES.includes(context.url.pathname)) { 36 | return next(); 37 | } 38 | 39 | const token = context.cookies.get(TOKEN).value; 40 | const validationResult = await verifyAuth(token); 41 | 42 | console.log(validationResult); 43 | 44 | switch (validationResult.status) { 45 | case "authorized": 46 | return next(); 47 | 48 | case "error": 49 | case "unauthorized": 50 | if (context.url.pathname.startsWith("/api/")) { 51 | return new Response(JSON.stringify({ message: validationResult.msg }), { 52 | status: 401, 53 | }); 54 | } 55 | // otherwise, redirect to the root page for the user to login 56 | else { 57 | return Response.redirect(new URL("/", context.url)); 58 | } 59 | 60 | default: 61 | return Response.redirect(new URL("/", context.url)); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /feature-flag-posthog/README.md: -------------------------------------------------------------------------------- 1 | # Astro Starter Kit: Basics 2 | 3 | ``` 4 | npm create astro@latest -- --template basics 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) 8 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) 9 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) 10 | 11 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 12 | 13 | ![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) 14 | 15 | ## 🚀 Project Structure 16 | 17 | Inside of your Astro project, you'll see the following folders and files: 18 | 19 | ``` 20 | / 21 | ├── public/ 22 | │ └── favicon.svg 23 | ├── src/ 24 | │ ├── components/ 25 | │ │ └── Card.astro 26 | │ ├── layouts/ 27 | │ │ └── Layout.astro 28 | │ └── pages/ 29 | │ └── index.astro 30 | └── package.json 31 | ``` 32 | 33 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. 34 | 35 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. 36 | 37 | Any static assets, like images, can be placed in the `public/` directory. 38 | 39 | ## 🧞 Commands 40 | 41 | All commands are run from the root of the project, from a terminal: 42 | 43 | | Command | Action | 44 | | :------------------------ | :----------------------------------------------- | 45 | | `npm install` | Installs dependencies | 46 | | `npm run dev` | Starts local dev server at `localhost:4321` | 47 | | `npm run build` | Build your production site to `./dist/` | 48 | | `npm run preview` | Preview your build locally, before deploying | 49 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 50 | | `npm run astro -- --help` | Get help using the Astro CLI | 51 | 52 | ## 👀 Want to learn more? 53 | 54 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). 55 | -------------------------------------------------------------------------------- /feature-flag-posthog/src/pages/post-hog.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | import Card from "../components/Card.astro"; 4 | --- 5 | 6 | 7 |
8 |

Welcome to the PostHog AB test

9 |

10 | If you're seeing this page, it means the feature flag is enabled and 11 | you've been automatically redirected!
12 | 13 | 14 | Code Challenge: Tweak the feature flag implementation 15 | in src/middleware.ts 16 | 17 |

18 | 30 |
31 |
32 | 33 | 94 | -------------------------------------------------------------------------------- /redirect/src/pages/redirected.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 | 35 |

You've been Redirected

36 |

37 | The redirect is handled by the middleware in src/middleware.ts 39 |
40 | Code Challenge: Tweak the redirect URL to www.google.com 43 |

44 |
45 |
46 | 47 | 102 | -------------------------------------------------------------------------------- /multiple-middleware/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 | 35 |

36 | Welcome to Astro multiple middleware example 37 |

38 |

39 | Check the server logs and you should see logs from multiple middleware
41 | Code Challenge: Tweak the server "log" messages. 42 |

43 |
44 |
45 | 46 | 107 | -------------------------------------------------------------------------------- /redirect/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 | 35 |

Welcome to Astro

36 |

37 | To get started, open the directory src/pages in your project.
39 | Code Challenge: Tweak the "Welcome to Astro" message above. 40 |

41 | 42 |
43 |
44 | 45 | 106 | -------------------------------------------------------------------------------- /locals-object/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | 4 | const data = Astro.locals; 5 | 6 | console.log({ res: data.functionValue() }); 7 | --- 8 | 9 | 10 |
11 | 39 |

40 | Welcome to Astro locals middleware example 41 |

42 | 43 |

The data below came from a middleware:

44 |
 45 | 		{JSON.stringify(data, null, 2)}
 46 | 	
47 |

48 | Code Challenge: Tweak the locals object in src/middleware.ts 51 |

52 |
53 |
54 | 55 | 116 | -------------------------------------------------------------------------------- /feature-flag-posthog/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | import Card from "../components/Card.astro"; 4 | --- 5 | 6 | 7 |
8 | 36 |

Welcome to Astro

37 |

38 | To get started, open the directory src/pages in your project.
40 | Code Challenge: Tweak the "Welcome to Astro" message 41 | above. 42 |

43 | 65 |
66 |
67 | 68 | 129 | -------------------------------------------------------------------------------- /jwt-auth/src/pages/protected.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 | 35 |

36 | This page is protected 37 |

38 |
39 |

You're logged in!

40 |

41 | This page cannot reach it unless you have the{" "} 42 | user token JWT Cookie. Don't have the cookie? You will be redirected 43 | to the{" "} 44 | index page. 45 | 46 |
47 |
48 | 49 | Click the logout button to unset the JWT cookie 50 | 51 | 52 |

53 |
54 |
55 |
56 | 57 | 124 | 125 | 144 | -------------------------------------------------------------------------------- /jwt-auth/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 | 35 |

36 | Welcome to the Astro JWT Middleware example 37 |

38 | 39 |
40 |

41 | Authentication is done via a JWT in your project. 46 | 47 |
48 | 49 | 50 |

51 | 52 |

53 | If you're not logged in i.e., without a valid token set, you wont be 54 | able to vist the protected resources below: 55 |

56 | 57 | 65 |
66 |
67 |
68 | 69 | 136 | 137 | 156 | --------------------------------------------------------------------------------