feat(api): GET /api/health — status do backend + banco + cache
- Público, sem auth. Checa Postgres (crítico → HTTP 503 se down) e Dragonfly/Redis (opcional → "degraded" mas 200; "disabled" se cache off). Inclui version/commit/ environment (de BUILD_INFO) e uptime_s. Sem vazar mensagens de erro. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -181,6 +181,41 @@ const BUILD_INFO = {
|
||||
};
|
||||
app.get('/api/version', (req, res) => res.json(BUILD_INFO));
|
||||
|
||||
// Health check (público, sem auth) — status do backend + dependências.
|
||||
// Banco é crítico (down => HTTP 503); cache é opcional (down => "degraded", mas 200).
|
||||
app.get('/api/health', async (req, res) => {
|
||||
const checks = {};
|
||||
try {
|
||||
const t = Date.now();
|
||||
await pool.query('SELECT 1');
|
||||
checks.database = { status: 'ok', latency_ms: Date.now() - t };
|
||||
} catch {
|
||||
checks.database = { status: 'down' };
|
||||
}
|
||||
try {
|
||||
if (redis) {
|
||||
const t = Date.now();
|
||||
await redis.ping();
|
||||
checks.cache = { status: 'ok', latency_ms: Date.now() - t };
|
||||
} else {
|
||||
checks.cache = { status: 'disabled' };
|
||||
}
|
||||
} catch {
|
||||
checks.cache = { status: 'down' };
|
||||
}
|
||||
const dbOk = checks.database.status === 'ok';
|
||||
const cacheDegraded = checks.cache.status === 'down';
|
||||
const status = !dbOk ? 'down' : (cacheDegraded ? 'degraded' : 'ok');
|
||||
res.status(dbOk ? 200 : 503).json({
|
||||
status,
|
||||
version: BUILD_INFO.version,
|
||||
commit: BUILD_INFO.commit,
|
||||
environment: BUILD_INFO.environment,
|
||||
uptime_s: Math.round(process.uptime()),
|
||||
checks,
|
||||
});
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// SECURITY MIDDLEWARE: Tenant Guard
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user