2025-06-08 15:12:04 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-06-15 11:35:28 -07:00
|
|
|
export async function main() {
|
|
|
|
const key = crypto.randomUUID();
|
|
|
|
await fs.writeMkdir(".clover/admin-token.txt", key);
|
|
|
|
const start = ({
|
|
|
|
win32: "start",
|
|
|
|
darwin: "open",
|
|
|
|
} as Record<string, string>)[process.platform] ?? "xdg-open";
|
|
|
|
child_process.exec(`${start} http://[::1]:3000/admin/login?key=${key}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
import * as fs from "#sitegen/fs";
|
2025-06-08 15:12:04 -07:00
|
|
|
import type { Context, Next } from "hono";
|
|
|
|
import { serveAsset } from "#sitegen/assets";
|
2025-06-15 11:35:28 -07:00
|
|
|
import * as child_process from "node:child_process";
|