50 lines
1.8 KiB
TypeScript
50 lines
1.8 KiB
TypeScript
/// 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 };
|
|
});
|