initial relay scaffold
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Recap Relay — operator-side credit-metered proxy in front of Gemini
|
||||
// (and optionally a co-located Parakeet+Gemma setup).
|
||||
//
|
||||
// Two public endpoints:
|
||||
// POST /relay/transcribe — audio → text (Gemini File API)
|
||||
// POST /relay/analyze — text → topic sections JSON (Gemini Pro)
|
||||
// Plus admin endpoints under /admin/* gated by an HTTP session cookie.
|
||||
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { initConfig } from "./config.js";
|
||||
import { initCredits } from "./credits.js";
|
||||
import {
|
||||
setupAdminAuthMiddleware,
|
||||
setupAdminAuthRoutes,
|
||||
} from "./admin-auth.js";
|
||||
import { transcribeRouter } from "./routes/transcribe.js";
|
||||
import { analyzeRouter } from "./routes/analyze.js";
|
||||
import { healthRouter } from "./routes/health.js";
|
||||
import { adminRouter } from "./routes/admin.js";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// DATA_DIR is /data on StartOS, project root in dev.
|
||||
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "..");
|
||||
const PORT = parseInt(process.env.PORT || "3002", 10);
|
||||
|
||||
await initConfig({ dataDir: DATA_DIR });
|
||||
await initCredits({ dataDir: DATA_DIR });
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(cookieParser());
|
||||
|
||||
// Admin auth must run BEFORE the admin routes register so the cookie
|
||||
// check applies to /admin/usage, /admin/config, etc. /admin/login and
|
||||
// /admin/status are explicitly exempted inside the middleware.
|
||||
setupAdminAuthMiddleware(app);
|
||||
setupAdminAuthRoutes(app);
|
||||
|
||||
// Public relay endpoints. No app-level auth — each route handler
|
||||
// authenticates per-call via headers (X-Recap-Install-Id required,
|
||||
// Authorization optional).
|
||||
app.use("/relay", healthRouter());
|
||||
app.use("/relay", transcribeRouter());
|
||||
app.use("/relay", analyzeRouter());
|
||||
|
||||
// Admin dashboard endpoints (cookie-gated).
|
||||
app.use("/admin", adminRouter({ dataDir: DATA_DIR }));
|
||||
|
||||
// Static admin UI (v0.2 will flesh out public/admin.html). For v0.1
|
||||
// the dashboard is JSON-only; serve any static assets dropped into
|
||||
// public/ but don't error if the directory is empty.
|
||||
app.use(express.static(path.join(__dirname, "..", "public")));
|
||||
|
||||
// Root: redirect to /admin/ for operator convenience, or show a tiny
|
||||
// placeholder for Recap clients that hit the root by mistake.
|
||||
app.get("/", (_req, res) => {
|
||||
res.type("text/plain").send(
|
||||
"Recap Relay\n" +
|
||||
"===========\n" +
|
||||
"Public endpoints: POST /relay/transcribe, POST /relay/analyze, GET /relay/health\n" +
|
||||
"Operator dashboard: /admin/\n"
|
||||
);
|
||||
});
|
||||
|
||||
const HOSTNAME = process.env.HOSTNAME || "0.0.0.0";
|
||||
app.listen(PORT, HOSTNAME, () => {
|
||||
console.log(`[relay] listening on http://${HOSTNAME}:${PORT}`);
|
||||
console.log(`[relay] data directory: ${DATA_DIR}`);
|
||||
});
|
||||
Reference in New Issue
Block a user