/// Internal endpoints used by the Rust monitor runner. /// Protected by MONITOR_TOKEN — not exposed to users. import { Elysia } from "elysia"; import sql from "../db"; export async function pruneOldPings(retentionDays = 90) { const result = await sql`DELETE FROM pings WHERE checked_at < now() - ${retentionDays + ' days'}::interval`; return result.count; } // Run retention cleanup every hour setInterval(() => { const days = Number(process.env.PING_RETENTION_DAYS ?? 90); pruneOldPings(days).catch((err) => console.error("Retention cleanup failed:", err)); }, 60 * 60 * 1000); export const internal = new Elysia({ prefix: "/internal", detail: { hide: true } }) .derive(({ headers, error }) => { if (headers["x-monitor-token"] !== process.env.MONITOR_TOKEN) return error(401, { error: "Unauthorized" }); return {}; }) // Returns monitors that are due for a check, with scheduled_at = now() .get("/due", async () => { const scheduled_at = new Date().toISOString(); const monitors = await sql` SELECT m.id, m.url, m.method, m.request_headers, m.request_body, m.timeout_ms, m.interval_s, m.query FROM monitors m LEFT JOIN LATERAL ( SELECT checked_at FROM pings WHERE monitor_id = m.id ORDER BY checked_at DESC LIMIT 1 ) last ON true WHERE m.enabled = true AND (last.checked_at IS NULL OR last.checked_at < now() - (m.interval_s || ' seconds')::interval) `; // Attach scheduled_at to each monitor so the runner can report jitter return monitors.map((m: any) => ({ ...m, scheduled_at })); }) // Manual retention cleanup trigger .post("/prune", async () => { const days = Number(process.env.PING_RETENTION_DAYS ?? 90); const deleted = await pruneOldPings(days); return { deleted, retention_days: days }; });