const cookieAge = 60 * 60 * 24 * 365; // 1 year let lastKnownToken: string | null = null; function compareToken(token: string) { if (token === lastKnownToken) return true; lastKnownToken = fs.readFileSync(".clover/admin-token.txt", "utf8").trim(); return token === lastKnownToken; } export async function middleware(c: Context, next: Next) { if (c.req.path.startsWith("/admin")) { return adminInner(c, next); } return next(); } export function adminInner(c: Context, next: Next) { const token = c.req.header("Cookie")?.match(/admin-token=([^;]+)/)?.[1]; if (c.req.path === "/admin/login") { const key = c.req.query("key"); if (key) { if (compareToken(key)) { return c.body(null, 303, { "Location": "/admin", "Set-Cookie": `admin-token=${key}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${cookieAge}`, }); } return serveAsset(c, "/admin/login/fail", 403); } if (token && compareToken(token)) { return c.redirect("/admin", 303); } if (c.req.method === "POST") { return serveAsset(c, "/admin/login/fail", 403); } else { return serveAsset(c, "/admin/login", 200); } } if (c.req.path === "/admin/logout") { return c.body(null, 303, { "Location": "/admin/login", "Set-Cookie": `admin-token=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0`, }); } if (token && compareToken(token)) { return next(); } return c.redirect("/admin/login", 303); } export function hasAdminToken(c: Context) { const token = c.req.header("Cookie")?.match(/admin-token=([^;]+)/)?.[1]; return token && compareToken(token); } export async function main() { const key = crypto.randomUUID(); await fs.writeMkdir(".clover/admin-token.txt", key); const start = ({ win32: "start", darwin: "open", } as Record)[process.platform] ?? "xdg-open"; child_process.exec(`${start} http://[::1]:3000/admin/login?key=${key}`); } import * as fs from "#sitegen/fs"; import type { Context, Next } from "hono"; import { serveAsset } from "#sitegen/assets"; import * as child_process from "node:child_process";