diff --git a/platform/api/src/server.js b/platform/api/src/server.js index 0db3766..4cf1e18 100644 --- a/platform/api/src/server.js +++ b/platform/api/src/server.js @@ -76,6 +76,18 @@ function wrap(fn) { return (req, res, next) => fn(req, res, next).catch(next); } +// GET /healthz — unauthenticated liveness/readiness probe for uptime monitoring and Ansible +// deploy smoke tests (Phase 4, docs/OBSERVABILITY.md §"uptime checks"). Confirms the process is +// up AND can reach Postgres — deliberately minimal, no student/instructor data, no auth. +app.get('/healthz', async (req, res) => { + try { + await getPool().query('SELECT 1'); + res.json({ ok: true, db: 'ok' }); + } catch (e) { + res.status(503).json({ ok: false, db: 'unreachable' }); + } +}); + // ═══════════════════════════════════════════════════════════════════════════════════════ // STUDENT-FACING: roster / login / session // ═══════════════════════════════════════════════════════════════════════════════════════ diff --git a/platform/gateway/src/server.js b/platform/gateway/src/server.js index 86dc6db..09c230c 100644 --- a/platform/gateway/src/server.js +++ b/platform/gateway/src/server.js @@ -49,6 +49,12 @@ proxy.on('error', (err, req, res) => { } }); +// GET /healthz — the Gateway's own liveness check, answered locally without proxying anywhere +// (Phase 4, docs/OBSERVABILITY.md §"uptime checks"). Deliberately doesn't depend on Platform +// API/Provisioner being reachable, so it can distinguish "Gateway itself is down" from +// "something behind it is down" for alerting. +app.get('/healthz', (req, res) => res.json({ ok: true })); + function parseCookies(req) { const header = req.headers.cookie; if (!header) return {};