feat(observability): add /healthz to Platform API and Gateway

Unauthenticated liveness/readiness probes for uptime monitoring and Ansible
deploy smoke tests (Phase 4). API's checks Postgres reachability via
SELECT 1; Gateway's answers locally without proxying anywhere, so it can
distinguish 'Gateway itself is down' from 'something behind it is down'.
No student/instructor data exposed.

Verified live against the real dev stack: both return 200 with the
expected JSON body. Full regression suite re-run after this change:
e2e-phase2a (10/10), e2e-phase2b (12/12), e2e-phase3a (21/21), e2e-phase3b
(24/24) - all passing, no regressions. Frontend build and the Lab image
(both the multi-service dev compose and the standalone single-container
Dockerfile) also confirmed still building/booting correctly.
This commit is contained in:
2026-08-18 17:13:42 -05:00
parent 51a09241ad
commit 1d3afc736b
2 changed files with 18 additions and 0 deletions
+12
View File
@@ -76,6 +76,18 @@ function wrap(fn) {
return (req, res, next) => fn(req, res, next).catch(next); 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 // STUDENT-FACING: roster / login / session
// ═══════════════════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════════════════
+6
View File
@@ -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) { function parseCookies(req) {
const header = req.headers.cookie; const header = req.headers.cookie;
if (!header) return {}; if (!header) return {};