Skip to content
File

Blob: src/worker/routes/api/index.ts

typescript63 lines
1import { Hono } from "hono";
2import { swaggerUI } from "@hono/swagger-ui";
3 
4import { readApiSession } from "@/worker/auth/session-cookie";
5import { createAddressbookApiRoutes } from "@/worker/routes/api/addressbooks";
6import { createAuthApiRoutes } from "@/worker/routes/api/auth";
7import { createCalendarApiRoutes } from "@/worker/routes/api/calendars";
8import { createFileApiRoutes } from "@/worker/routes/api/files";
9import { createMeApiRoutes } from "@/worker/routes/api/me";
10import { createPatApiRoutes } from "@/worker/routes/api/pats";
11import type { AppContext, AppEnv } from "@/worker/types";
12import { jsonError } from "@/worker/util/response";
13 
14const publicPaths = new Set(["/auth/oidc/login", "/auth/oidc/callback", "/openapi.json", "/openapi.yaml", "/docs"]);
15const safeMethods = new Set(["GET", "HEAD", "OPTIONS"]);
16 
17function requestOrigin(c: AppContext): string {
18 return new URL(c.req.url).origin;
19}
20 
21function apiPath(c: AppContext): string {
22 const pathname = new URL(c.req.url).pathname;
23 return pathname.startsWith("/api/v1/") ? pathname.slice("/api/v1".length) : pathname;
24}
25 
26function enforceSameOrigin(c: AppContext): Response | null {
27 if (safeMethods.has(c.req.method)) return null;
28 const origin = c.req.header("origin");
29 if (!origin || origin !== requestOrigin(c)) {
30 return jsonError("forbidden_origin", "Mutating API requests must be same-origin.", 403);
31 }
32 return null;
33}
34 
35export function createApiRoutes(): Hono<AppEnv> {
36 const api = new Hono<AppEnv>();
37 
38 api.use("*", async (c, next) => {
39 if (c.get("hostInfo").kind !== "control") return c.notFound();
40 
41 const sameOriginError = enforceSameOrigin(c);
42 if (sameOriginError) return sameOriginError;
43 
44 if (!publicPaths.has(apiPath(c))) {
45 const session = await readApiSession(c);
46 if (!session) return jsonError("unauthorized", "API session required.", 401);
47 c.set("session", session);
48 }
49 
50 await next();
51 });
52 
53 api.get("/docs", swaggerUI({ url: "/api/v1/openapi.json", title: "dab API docs" }));
54 api.route("/auth", createAuthApiRoutes());
55 api.route("/me", createMeApiRoutes());
56 api.route("/pats", createPatApiRoutes());
57 api.route("/calendars", createCalendarApiRoutes());
58 api.route("/addressbooks", createAddressbookApiRoutes());
59 api.route("/files", createFileApiRoutes());
60 
61 return api;
62}