feat(api): add versioned REST API with OpenAPI 3.1 spec

Expose /api/v1/* for feed and email management (feeds CRUD, email
list/get/delete, stats) so the service can be automated without scraping
the admin UI. Built on @hono/zod-openapi; the OpenAPI 3.1 spec is served at
/api/openapi.json with a Scalar reference at /api/docs.

Auth is token-based (Authorization: Bearer <ADMIN_PASSWORD>) plus the
existing reverse-proxy headers — no cookie, no CSRF. Extracted the auth
primitives into src/lib/auth.ts and the feed create/update/delete
orchestration into src/lib/feed-service.ts so the admin UI and the REST API
share a single source of truth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-23 23:01:15 +02:00
parent 7f5b913576
commit 45d2a14a12
14 changed files with 1398 additions and 234 deletions
+3
View File
@@ -10,6 +10,7 @@ import { handle as handleStats } from "./routes/stats";
import { handle as handleHome } from "./routes/home";
import { handle as handleFavicon, handleFeedFavicon } from "./routes/favicon";
import { hubRouter } from "./routes/hub";
import { apiApp } from "./routes/api";
import { handleCloudflareEmail } from "./lib/cloudflare-email";
import { Env } from "./types";
import { logger } from "./lib/logger";
@@ -168,6 +169,8 @@ admin.route("/", handleAdmin);
// Mount the route groups
app.route("/api", api);
// Versioned REST API + OpenAPI spec/docs (/api/v1/*, /api/openapi.json, /api/docs)
app.route("/api", apiApp);
app.route("/rss", rss);
app.route("/atom", atom);
app.route("/entries", entries);