88 lines
2.7 KiB
TypeScript
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;
|
|
});
|