feat: phase A — backup B2, healthcheck /api/status, vector tiles Tippecanoe
- A1: scripts/backup-postgres.sh — pg_dump quotidien compressé → Backblaze B2 (rclone), rétention 30j - A2: StatusController retourne HTTP 503 + healthy/alertReason si dernière observation > 6h - A4: Dockerfile installe tippecanoe, GenerateTilesCommand génère 5 tilesets (obs + h6/12/24/48), Caddyfile sert /tiles/* sans fallback SPA, SargassesMap.jsx passe en sources vector tiles statiques - chore: backend/public/assets/ ajouté au .gitignore (build artifacts) - chore: setup vitest frontend + ImpactScoreServiceTest (session précédente) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,27 @@ class StatusController extends AbstractController
|
||||
ORDER BY requested_at DESC LIMIT 1"
|
||||
);
|
||||
|
||||
// Healthcheck : données fraîches si dernière observation < 6h
|
||||
$healthy = false;
|
||||
$alertReason = null;
|
||||
|
||||
if ($obs !== false && $obs['detected_at'] !== null) {
|
||||
$detectedAt = new \DateTimeImmutable($obs['detected_at'], new \DateTimeZone('UTC'));
|
||||
$now = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
|
||||
$ageSeconds = $now->getTimestamp() - $detectedAt->getTimestamp();
|
||||
|
||||
if ($ageSeconds <= 6 * 3600) {
|
||||
$healthy = true;
|
||||
} else {
|
||||
$alertReason = 'stale_data';
|
||||
}
|
||||
} else {
|
||||
$alertReason = 'no_data';
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'healthy' => $healthy,
|
||||
'alertReason' => $alertReason,
|
||||
'lastObservation' => $obs !== false ? [
|
||||
'detectedAt' => $obs['detected_at'],
|
||||
'source' => $obs['source'],
|
||||
@@ -43,6 +63,6 @@ class StatusController extends AbstractController
|
||||
'finishedAt' => $job['processed_at'],
|
||||
'retryCount' => (int) $job['retry_count'],
|
||||
] : null,
|
||||
]);
|
||||
], $healthy ? 200 : 503);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user