Documentation

Santé système

Page admin qui ping les intégrations externes (Stripe, Resend, OneSignal, DB) et affiche des métriques 24h.

Page /admin/system — admin uniquement. Vue d'ensemble santé + métriques opérationnelles des dernières 24h.

Checks externes

API GET /api/admin/system/health ping en parallèle :

  • Databaseprisma.$queryRaw\SELECT 1``
  • Stripestripe.balance.retrieve() (si STRIPE_SECRET_KEY configurée)
  • ResendGET https://api.resend.com/domains (si RESEND_API_KEY)
  • OneSignalGET https://api.onesignal.com/apps/<id> (si configuré)

Chaque check tourne avec un timeout 5s via AbortSignal.timeout(5000), en parallèle (Promise.all). Une dépendance qui hang ne bloque pas la route — la page reste utile précisément quand quelque chose va mal.

Service non configuré → ok: true avec detail: "not configured" (pas considéré comme un échec).

Métriques 24h

  • Dernier cron exécuté (lu depuis AuditLogaction LIKE 'cron.%')
  • Broadcasts envoyés / en erreur (depuis Broadcast.sentCount + errorCount)
  • Bounces emails (depuis EmailLog status="bounced")
  • Plaintes spam (depuis EmailLog status="complained")

Tout warning visuel si errors > 0 ou bounces > 0.

Statut global

overall = "healthy" | "degraded" — calculé sur l'agrégat de tous les checks externes. Visible en badge en haut de page.

Bouton Actualiser

fetch("/api/admin/system/health", { cache: "no-store" }) — pas de revalidation automatique, seulement à la demande.

Étendre

Pour ajouter un check (ex. Redis, Sentry, etc.), édite app/api/admin/system/health/route.ts. Le helper timed(fn) wrappe n'importe quelle promise async avec timeout + mesure de latence.

const myCheck = await timed(async (signal) => {
  const res = await fetch("https://api.monservice.com/health", { signal });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
});
checks.push({ name: "monservice", ok: myCheck.ok, latencyMs: myCheck.ms });

On this page