import { cleanupOrphanedFiles, cleanupStaging, processFileDeletions } from "./files.js"; import { loadConfig, prepareDataDirectories, acquireInstanceLock } from "./config.js"; import { openDatabase } from "./db/index.js"; import { buildApp } from "./app.js"; import { cleanupOrphanedExports, expireExports, resumeExports } from "./exporter.js"; const config = loadConfig(); prepareDataDirectories(config); const releaseLock = acquireInstanceLock(config); const database = openDatabase(config); async function start() { await cleanupStaging(config); await cleanupOrphanedFiles(database.sqlite, config); database.sqlite.prepare("DELETE FROM login_attempts WHERE window_start < ? AND (blocked_until IS NULL OR blocked_until < ?)").run(Date.now() - 24 * 60 * 60 * 1000, Date.now()); database.sqlite.prepare("DELETE FROM sessions WHERE idle_expires_at <= ? OR absolute_expires_at <= ?").run(Date.now(), Date.now()); await processFileDeletions(database.sqlite, config); await expireExports(database.sqlite, config); await cleanupOrphanedExports(database.sqlite, config); await resumeExports(database.sqlite, config); const app = await buildApp(database, config); const janitor = setInterval(() => { void cleanupStaging(config); void cleanupOrphanedFiles(database.sqlite, config); database.sqlite.prepare("DELETE FROM login_attempts WHERE window_start < ? AND (blocked_until IS NULL OR blocked_until < ?)").run(Date.now() - 24 * 60 * 60 * 1000, Date.now()); database.sqlite.prepare("DELETE FROM sessions WHERE idle_expires_at <= ? OR absolute_expires_at <= ?").run(Date.now(), Date.now()); void processFileDeletions(database.sqlite, config); void expireExports(database.sqlite, config); void cleanupOrphanedExports(database.sqlite, config); }, 60_000); const shutdown = async () => { clearInterval(janitor); await app.close().catch(() => undefined); database.sqlite.close(); releaseLock(); }; process.once("SIGINT", () => void shutdown().finally(() => process.exit(0))); process.once("SIGTERM", () => void shutdown().finally(() => process.exit(0))); try { await app.listen({ host: config.host, port: config.port }); } catch (error) { clearInterval(janitor); await app.close().catch(() => undefined); throw error; } app.log.info(`TallyNote running at ${config.publicOrigin}`); } start().catch((error) => { console.error(error); database.sqlite.close(); releaseLock(); process.exitCode = 1; });