import { App, Context, staticFiles } from "fresh"; import { type State } from "./utils.ts"; import "$std/dotenv/load.ts"; import { checkDatabaseConnection } from "./db/db.ts"; const isDevelopment = Deno.env.get("DEVELOPMENT") === "true"; //console.log("Development mode:", isDevelopment); /** Error page HTML for service unavailability */ const SERVICE_UNAVAILABLE_HTML = ` Service Unavailable
🔧

The problem is on our side!

We will fix this as soon as we can.

`; /** Check if required KV cache files exist */ function checkKvCacheFiles(): boolean { // const requiredFiles = ["./data/kv-cache-shm", "./data/kv-cache-wal"]; // for (const file of requiredFiles) { // try { // Deno.statSync(file); // } catch { // console.error(`⚠️ Required file missing: ${file}`); // return false; // } // } return true; } /** Perform all startup checks */ async function performStartupChecks(): Promise { //console.log("Checking startup requirements..."); // Check KV cache files const kvFilesExist = checkKvCacheFiles(); if (!kvFilesExist) { console.error( "⚠️ KV cache files missing - app will display maintenance page", ); return false; } //console.log("✅ KV cache files found"); // Check database connection //console.log("Checking database connection..."); const dbAvailable = await checkDatabaseConnection(); if (!dbAvailable) { console.error( "⚠️ Database connection failed - app will display maintenance page", ); return false; } //console.log("✅ Database connection successful"); return true; } // Run startup checks first const isAppReady = await performStartupChecks(); // Create app based on startup check result export const app = new App(); if (isAppReady) { // Full app with all routes and middleware const { InitiPGSessionMiddleware } = await import( "./services/stateMiddleware.ts" ); app.use(staticFiles()); app.get("/health", (_ctx: Context) => { console.log("Health check"); return new Response("OK"); }); // app.post("/graph-update", async (ctx: Context) => { // console.log("Graph update"); // const body = await ctx.req.json(); // //console.log(body); // //const { userId, roles, organization } = body; // // await inMemoryCacheKv.set(["ciam", userId], { // // userId, // // roles, // // organization, // // lastSynced: Date.now(), // // }); // //console.log(`Graph update applied for user ${userId}`); // return new Response("Graph update stored in cache", { status: 202 }); // }); // TODO: add a well-known endpoint // app.get("/.well-known/acme-challenge/:token", (ctx: Context) => { // const token = ctx.params.token; // const content = `${token}.EXAMPLE_ACCOUNT_THUMBPRINT`; // return new Response(content, { // status: 200, // headers: { // "content-type": "text/plain; charset=utf-8", // "cache-control": "public, max-age=0, must-revalidate", // }, // }); // }); app.use(InitiPGSessionMiddleware()); app.fsRoutes(); //console.log("✅ Full application loaded"); } else { // Maintenance mode - serve error page for all routes app.use(staticFiles()); app.get("/health", () => { return new Response("Service Unavailable", { status: 503 }); }); app.use(() => { return new Response(SERVICE_UNAVAILABLE_HTML, { status: 503, headers: { "Content-Type": "text/html; charset=utf-8" }, }); }); console.log("⚠️ Maintenance mode - serving error page only"); // Start periodic database connection check (every 60 seconds) setInterval(async () => { console.log("🔄 Checking if database connection is restored..."); const dbAvailable = await checkDatabaseConnection(); if (dbAvailable) { console.log("✅ Database connection restored! Restarting app..."); // Check if running in development mode const restartCmd = isDevelopment ? "exec deno task dev" : "exec ./server"; // Use exec to replace shell with new process, keeping terminal connection const command = new Deno.Command("sh", { args: [ "-c", `lsof -ti:8080 | xargs kill -9 2>/dev/null || true; ${restartCmd}`, ], cwd: Deno.cwd(), stdin: "inherit", stdout: "inherit", stderr: "inherit", }); command.spawn(); Deno.exit(0); } else { console.log("❌ Database still unavailable, will retry in 60 seconds"); } }, 60_000); } // if (import.meta.main) { // await app.listen(); // }