import path from "node:path"; import { staticPlugin } from "@elysiajs/static"; import { Elysia } from "elysia"; import config from "./config.ts"; import { db } from "./db/index.ts"; import { authRoutes } from "./routes/auth.tsx"; import { avatarRoutes } from "./routes/avatars.ts"; import { ciRoutes } from "./routes/ci.tsx"; import { gitRoutes } from "./routes/git.ts"; import { issueRoutes } from "./routes/issues.tsx"; import { patchRoutes } from "./routes/patches.tsx"; import { releasesRoutes } from "./routes/releases.tsx"; import { repoRoutes } from "./routes/repos.tsx"; import { settingsRoutes } from "./routes/settings.tsx"; import { cancelStaleRuns } from "./services/ci.ts"; import { syncStartup } from "./services/repoSync.ts"; const CSP = [ "default-src 'self'", "script-src 'self'", "style-src 'self' 'unsafe-inline'", "img-src 'self'", "connect-src 'self'", "frame-ancestors 'none'", "form-action 'self'", "object-src 'none'", ].join("; "); async function cleanupSessions() { await db .deleteFrom("sessions") .where("expires_at", "<", new Date().toISOString()) .execute(); } export async function createApp(port: number) { await syncStartup(); await cancelStaleRuns(); // Recurring session cleanup — runs every 24 hours setInterval(cleanupSessions, 24 * 60 * 60 * 1000); return new Elysia({ serve: { maxRequestBodySize: config.MAX_UPLOAD_BYTES }, }) .use( staticPlugin({ assets: path.resolve("./public"), prefix: "/", }), ) .onAfterHandle(({ response }) => { if (response instanceof Response) { response.headers.set("Content-Security-Policy", CSP); response.headers.set("X-Frame-Options", "DENY"); } }) .get("/health", async () => { try { await db.selectFrom("users").select("id").limit(1).execute(); return new Response(JSON.stringify({ ok: true }), { headers: { "Content-Type": "application/json" }, }); } catch (e) { return new Response( JSON.stringify({ ok: false, error: e instanceof Error ? e.message : "DB error", }), { status: 503, headers: { "Content-Type": "application/json" }, }, ); } }) .use(gitRoutes) .use(settingsRoutes) .use(authRoutes) .use(repoRoutes) .use(issueRoutes) .use(patchRoutes) .use(releasesRoutes) .use(ciRoutes) .use(avatarRoutes) .listen(port); }