import type { MiddlewareHandler } from "astro"; import { getOptions } from "./runtime.js"; import { verifyAndDecode } from "./lib/sign.js"; function parseCookies(cookieHeader: string | null): Record { const out: Record = {}; if (!cookieHeader) return out; const parts = cookieHeader.split(/;\s*/); for (const part of parts) { const idx = part.indexOf("="); if (idx === -1) continue; const name = decodeURIComponent(part.slice(0, idx)); const val = decodeURIComponent(part.slice(idx + 1)); out[name] = val; } return out; } function escapeRegex(str: string): string { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function globToRegExp(pattern: string): RegExp { const escaped = pattern.split("*").map(escapeRegex).join(".*"); return new RegExp(`^${escaped}$`); } function isProtected(pathname: string, patterns: string[]): boolean { return patterns.some((p) => globToRegExp(p).test(pathname)); } export const onRequest: MiddlewareHandler = async (context, next) => { const { request, locals, url } = context; const options = getOptions(); const cookieName = options.cookie.name; const cookies = parseCookies(request.headers.get("cookie")); const token = cookies[cookieName]; locals.user = null; if (token) { const res = await verifyAndDecode<{ sub: string; email?: string }>( token, options.cookie.signingSecret, ); if (res.valid && res.payload && typeof res.payload.sub === "string") { locals.user = { sub: res.payload.sub, email: res.payload.email }; } } if (isProtected(url.pathname, options.protected) && !locals.user) { const returnTo = url.pathname + (url.search || ""); const loginUrl = new URL(options.routes.login, url); loginUrl.searchParams.set("return_to", returnTo); return Response.redirect(loginUrl, 302); } return next(); };