feat(server): REST API, auth, sources, changes, export

createApp() serves /api/v1 from a typed route table (zod bodies and queries), with
session-cookie and Bearer app-token auth, a CSRF header for cookie writes, login/register
rate limiting, body caps, JSON error mapping, OpenAPI generation and a 404 /ingest mount.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-30 21:15:01 +01:00
co-authored by Claude Opus 5.5
parent 7b0a6738dd
commit 6963e297b0
19 changed files with 1599 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
import { apiHandler, errorResponse, type AppDeps, type RouteDef } from "./http.ts";
import { attachmentRoutes } from "./routes/attachments.ts";
import { authRoutes } from "./routes/auth.ts";
import { changeRoutes } from "./routes/changes.ts";
import { copyRoutes } from "./routes/copies.ts";
import { customFieldRoutes } from "./routes/custom-fields.ts";
import { engagementRoutes } from "./routes/engagements.ts";
import { entryRoutes } from "./routes/entries.ts";
import { exportRoutes } from "./routes/export.ts";
import { listRoutes } from "./routes/lists.ts";
import { metaRoutes } from "./routes/meta.ts";
import { sourceRoutes } from "./routes/sources.ts";
import { workRoutes } from "./routes/works.ts";
export interface App {
/** Handle `/api/v1/*` and `/ingest/*`. Anything else is a JSON 404 (the web app is served by `Bun.serve` routes). */
fetch(req: Request, server?: { requestIP(req: Request): { address: string } | null }): Promise<Response>;
routes: RouteDef[];
}
/** The HTTP API, testable without a listening socket. */
export function createApp(deps: AppDeps): App {
const routes: RouteDef[] = [
...authRoutes,
...entryRoutes,
...engagementRoutes,
...workRoutes,
...listRoutes,
...copyRoutes,
...customFieldRoutes,
...attachmentRoutes,
...sourceRoutes,
...changeRoutes,
...exportRoutes,
];
routes.push(...metaRoutes(() => routes));
const api = apiHandler(routes, deps);
return {
routes,
async fetch(req, server) {
const { pathname } = new URL(req.url);
if (pathname.startsWith("/api/v1/")) return api(req, server?.requestIP(req)?.address ?? "unknown");
// Phase 1: the ingest mount exists but no plugin has the `ingest` capability.
if (pathname.startsWith("/ingest/")) return errorResponse(404, "not_found", "No ingest endpoint for this plugin.");
return errorResponse(404, "not_found", "No such endpoint.");
},
};
}