import { Hono } from "hono"; import { serveStatic } from "hono/bun"; import { getCookie, setCookie, deleteCookie } from "hono/cookie"; import { authRoutes } from "./routes/auth"; import { publicationRoutes } from "./routes/publication"; import { documentRoutes } from "./routes/documents"; import { layout } from "./views/layouts/main"; import { homePage } from "./views/home"; import { getSession } from "./lib/session"; import { getClientMetadata, getJwks } from "./lib/oauth"; import { csrfProtection, getCSRFToken } from "./lib/csrf"; import type { AppVariables } from "./types"; export const app = new Hono<{ Variables: AppVariables }>(); // Static files app.use("/public/*", serveStatic({ root: "./" })); // OAuth metadata endpoints at root level // These MUST be publicly accessible (no authentication) app.get("/client-metadata.json", async (c) => { try { const metadata = await getClientMetadata(); // Set appropriate cache headers c.header("Cache-Control", "public, max-age=600"); // Cache for 10 minutes c.header("Access-Control-Allow-Origin", "*"); return c.json(metadata); } catch (error) { console.error("Error getting client metadata:", error); return c.json({ error: "Failed to get client metadata" }, 500); } }); app.get("/jwks.json", async (c) => { try { const jwks = await getJwks(); // Set appropriate cache headers c.header("Cache-Control", "public, max-age=600"); // Cache for 10 minutes c.header("Access-Control-Allow-Origin", "*"); return c.json(jwks); } catch (error) { console.error("Error getting JWKS:", error); return c.json({ error: "Failed to get JWKS" }, 500); } }); // Session middleware - adds session and CSRF token to context app.use("*", async (c, next) => { const session = await getSession(c); c.set("session", session); // Generate CSRF token for all requests (sets cookie if not present) const csrfToken = getCSRFToken(c); c.set("csrfToken", csrfToken); await next(); }); // CSRF protection for state-changing requests // Applied after session middleware but before routes app.use("/auth/*", csrfProtection); app.use("/publication/*", csrfProtection); app.use("/documents/*", csrfProtection); // Home page app.get("/", async (c) => { const session = c.get("session"); return c.html(layout(homePage(session), { session })); }); // Mount routes app.route("/auth", authRoutes); app.route("/publication", publicationRoutes); app.route("/documents", documentRoutes); const port = parseInt(process.env.PORT || "8000"); console.log(`Starting server on http://localhost:${port}`); console.log( `Public URL: ${process.env.PUBLIC_URL || "http://localhost:" + port}`, ); export default { port, fetch: app.fetch, };