import { Hono } from "hono"; import { swaggerUI } from "@hono/swagger-ui"; import { readApiSession } from "@/worker/auth/session-cookie"; import { createAddressbookApiRoutes } from "@/worker/routes/api/addressbooks"; import { createAuthApiRoutes } from "@/worker/routes/api/auth"; import { createCalendarApiRoutes } from "@/worker/routes/api/calendars"; import { createFileApiRoutes } from "@/worker/routes/api/files"; import { createMeApiRoutes } from "@/worker/routes/api/me"; import { createPatApiRoutes } from "@/worker/routes/api/pats"; import type { AppContext, AppEnv } from "@/worker/types"; import { jsonError } from "@/worker/util/response"; const publicPaths = new Set(["/auth/oidc/login", "/auth/oidc/callback", "/openapi.json", "/openapi.yaml", "/docs"]); const safeMethods = new Set(["GET", "HEAD", "OPTIONS"]); function requestOrigin(c: AppContext): string { return new URL(c.req.url).origin; } function apiPath(c: AppContext): string { const pathname = new URL(c.req.url).pathname; return pathname.startsWith("/api/v1/") ? pathname.slice("/api/v1".length) : pathname; } function enforceSameOrigin(c: AppContext): Response | null { if (safeMethods.has(c.req.method)) return null; const origin = c.req.header("origin"); if (!origin || origin !== requestOrigin(c)) { return jsonError("forbidden_origin", "Mutating API requests must be same-origin.", 403); } return null; } export function createApiRoutes(): Hono { const api = new Hono(); api.use("*", async (c, next) => { if (c.get("hostInfo").kind !== "control") return c.notFound(); const sameOriginError = enforceSameOrigin(c); if (sameOriginError) return sameOriginError; if (!publicPaths.has(apiPath(c))) { const session = await readApiSession(c); if (!session) return jsonError("unauthorized", "API session required.", 401); c.set("session", session); } await next(); }); api.get("/docs", swaggerUI({ url: "/api/v1/openapi.json", title: "dab API docs" })); api.route("/auth", createAuthApiRoutes()); api.route("/me", createMeApiRoutes()); api.route("/pats", createPatApiRoutes()); api.route("/calendars", createCalendarApiRoutes()); api.route("/addressbooks", createAddressbookApiRoutes()); api.route("/files", createFileApiRoutes()); return api; }