personal-site/src/middleware.ts
2026-07-10 12:53:00 -07:00

88 lines
2.7 KiB
TypeScript

import { defineMiddleware } from "astro:middleware";
import { getActionContext } from "astro:actions";
import type { APIContext, MiddlewareNext } from "astro";
// htmz -> frame-ancestors 'self' & X-Frame-Options SAMEORIGIN
// astro -> 'unsafe-inline'
const SECURITY_HEADERS: Record<string, string> = {
"Content-Security-Policy": [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https://badblocks.goatcounter.com",
"font-src 'self'",
"connect-src 'self' https://badblocks.goatcounter.com https://api.iconify.design",
"object-src 'none'",
"frame-ancestors 'self'",
"base-uri 'self'",
"form-action 'self'",
].join("; "),
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "SAMEORIGIN",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
};
async function handle(context: APIContext, next: MiddlewareNext) {
if (context.isPrerendered) return next();
const { action, setActionResult, serializeActionResult } =
getActionContext(context);
const currentAction = await context.session?.get("currentAction");
if (currentAction) {
context.session?.delete("currentAction");
try {
const { actionName, actionResult } = JSON.parse(currentAction);
setActionResult(actionName, actionResult);
} catch {}
return next();
}
if (action?.calledFrom === "form") {
const formData = await context.request.clone().formData();
const actionResult = await action.handler();
context.session?.set(
"currentAction",
JSON.stringify({
actionName: action.name,
actionResult: serializeActionResult(actionResult),
}),
);
if (actionResult.error) {
const draft = {
action: formData.get("action")?.toString() ?? "",
name: formData.get("name")?.toString() ?? "",
phone: formData.get("phone")?.toString() ?? "",
msg: formData.get("msg")?.toString() ?? "",
};
context.session?.set("contactFormDraft", draft);
let redirectPath = context.originPathname;
try {
const referer = new URL(context.request.headers.get("Referer") ?? "");
if (referer.origin === context.url.origin) {
redirectPath = referer.pathname;
}
} catch {}
return context.redirect(redirectPath);
}
context.session?.delete("contactFormDraft");
return context.redirect(context.originPathname);
}
return next();
}
export const onRequest = defineMiddleware(async (context, next) => {
const response = await handle(context, next);
for (const [header, value] of Object.entries(SECURITY_HEADERS)) {
response.headers.set(header, value);
}
return response;
});