Administration
Administration
Périmètre : console super-admin Pulse (gestion multi-tenant des organisations clientes), paramètres applicatifs globaux et par organisation, release notes, webhooks sortants, impersonation, audit log cross-org et supervision temps réel. Ce domaine est strictement réservé à l'équipe Pulse Group ; aucun utilisateur d'organisation cliente n'y a accès.
/admin/*) affiche un banner rouge persistant et exige
un MFA récent (< 24 h) pour toute action destructive. Routes API sous
/api/admin/*. Source de cette page : docs/16-domain-admin.md./api/admin/* réellement implémentées : GET stats, GET audit,
GET/POST organizations, GET organizations/[id], GET organizations/[id]/roles,
GET roles, GET users, POST invitations. Tout le reste décrit ci-dessous
(suspension/archivage/export d'org, impersonation, webhooks, release notes, banner,
paramètres applicatifs, supervision, et l'espace /workspace/admin/*) relève de la
cible non encore implémentée et est signalé en tant que tel section par section.Vocabulaire & entités
| Terme | Entité | Définition | Statut |
|---|---|---|---|
| Super-admin Pulse | — | Membre de l'équipe Pulse Group exploitant la plateforme. | — |
| Organisation | Organization | Tenant (client, bailleur ou partenaire) ; isolation complète des données. | implémenté |
| Organization Setting | OrganizationSetting | Paramètre propre à un tenant (marge par défaut, numérotation custom…). | modèle présent |
| Application Setting | ApplicationSetting | Paramètre global clé/valeur (chiffré si préfixe secret:). | cible (modèle absent) |
| Release Note | ReleaseNote | Notice de version exposée aux utilisateurs à la prochaine connexion. | cible (modèle absent) |
| Webhook | Webhook | URL HTTPS externe déclenchée sur événement métier. | cible (modèle absent) |
| Webhook Delivery | WebhookDelivery | Tentative d'envoi d'un webhook (statut, retry, payload). | cible (modèle absent) |
Organization et OrganizationSetting existent. ApplicationSetting,
ReleaseNote, Webhook et WebhookDelivery ne sont pas encore des modèles.La structure complète est dans
docs/04-data-model.md§14.
Écrans
Console super-admin (/admin/*)
| Écran | Route | Contenu | Statut |
|---|---|---|---|
| Dashboard | /admin | Stats globales (orgs actives, MAU, volume API, DB, stockage), courbe MAU 12 mois, alertes système. | partiel (GET /api/admin/stats) |
| Organisations | /admin/organizations | DataTable : nom, slug, SIREN, plan, statut, membres, assets, contrats actifs. | implémenté (liste + création) |
| Fiche organisation | /admin/organizations/[id] | Onglets : Synthèse, Membres, Paramètres, Quotas, Webhooks, Audit, Actions. | partiel (synthèse + rôles ; onglets Paramètres/Quotas/Webhooks/Actions = cible) |
| Utilisateurs globaux | /admin/users | Tous les users cross-org. (Suspension, reset password, impersonation = cible.) | partiel (liste seule) |
| Paramètres applicatifs | /admin/settings | Édition des ApplicationSetting par section. | cible non implémenté |
| Release Notes | /admin/release-notes | CRUD + publication, audience, KPI d'accusé de lecture. | cible non implémenté |
| Webhooks | /admin/webhooks | Liste + création + historique des livraisons, action Resend. | cible non implémenté |
| Audit log global | /admin/audit | Cross-org, filtres acteur/action/target/période. | implémenté (GET /api/admin/audit) |
| Supervision | /admin/supervision | Queues, sessions DB, latence HTTP P95, lien Grafana/Loki. | cible non implémenté |
| Banner global | — (API) | Message affiché sur toutes les pages, audience configurable. | cible non implémenté |
ApplicationSetting / paramètres applicatifs,
release notes, webhooks sortants, supervision et banner global ne disposent
d'aucune route /api/admin/* à ce jour. Les écrans listés ci-dessus n'existent
que pour les lignes marquées « implémenté » ou « partiel ».Paramètres workspace (/workspace/admin/settings/*)
/api/workspace/admin/* n'existe
dans le code. La gestion par l'admin d'organisation (utilisateurs, rôles, API keys,
paramètres) passe aujourd'hui par les routes /api/workspace/members,
/api/workspace/roles, /api/workspace/api-keys et /api/workspace/invitations
(cf. domaine Identity). Le tableau ci-dessous décrit la cible.| Onglet | Contenu |
|---|---|
| Général | OrganizationSetting éditables par l'admin de l'org. |
| Intégrations | Webhooks sortants de l'org, lien vers /admin/webhooks. |
Modèle de données (points clés)
ApplicationSetting, ReleaseNote, ReleaseNoteAcknowledgement,
Webhook et WebhookDelivery ne sont pas encore au schéma, et Organization
ne porte pas de cycle SUSPENDED/ARCHIVED à ce jour.ApplicationSetting.valueest JSON ; les secrets sont chiffrés au repos (préfixe clésecret:).ReleaseNote→ReleaseNoteAcknowledgement(table de liaison user/release) : la modale "Nouveautés" est masquée dès qu'unackexiste.Webhook.secretest rotatable avec une période de grâce de 7 jours (dual-sign : ancien et nouveau secret acceptés pendant la transition).WebhookDelivery.status:PENDING→DELIVERED|FAILED(retry exponentiel, 10 tentatives max).Organization.status:ACTIVE→SUSPENDED→ARCHIVED(irréversible après la période de grâce de 90 j).
API
Les routes /api/admin/* sont déclarées avec requireOrganization: false et une
permission dédiée portée par le rôle SUPERADMIN_PROVIDER dans le seed.
Routes implémentées
Permissions vérifiées directement dans chaque handler.
| Méthode | Route | Permission |
|---|---|---|
GET | /api/admin/stats | admin.org:read |
GET | /api/admin/audit | admin.audit:read |
GET | /api/admin/organizations | admin.org:read |
POST | /api/admin/organizations | admin.org:create |
GET | /api/admin/organizations/[id] | admin.org:read |
GET | /api/admin/organizations/[id]/roles | admin.org:read |
GET | /api/admin/roles | identity.role:manage |
GET | /api/admin/users | identity.user:read |
POST | /api/admin/invitations | admin.org:read |
POST /api/admin/invitations crée une invitation depuis la console super-admin
(corps : organizationId, email, role). C'est aujourd'hui la seule mutation
admin au-delà de la création d'organisation.Routes cible (non implémentées)
docs/16-domain-admin.md.| Méthode | Route | Permission cible |
|---|---|---|
GET | /api/admin/dashboard | admin.org:read |
PATCH | /api/admin/organizations/[id] | admin.org:update |
POST | /api/admin/organizations/[id]/suspend | admin.org:suspend |
POST | /api/admin/organizations/[id]/archive | admin.org:suspend |
POST | /api/admin/organizations/[id]/export | admin.org:read |
POST | /api/admin/users/[id]/impersonate | identity.user:impersonate |
GET / PATCH | /api/admin/settings | admin.settings:read / :update |
GET / POST | /api/admin/release-notes | admin.release_notes:manage |
POST | /api/admin/release-notes/[id]/publish | admin.release_notes:manage |
POST | /api/account/release-notes/[id]/acknowledge | (authentifié) |
GET / POST / PATCH / DELETE | /api/admin/webhooks[/...] | admin.webhooks:manage |
GET | /api/admin/supervision | admin.audit:read |
GET / POST | /api/admin/banner | admin.settings:update (POST) |
/api/admin/organizations/[id]/suspendAuth Cible — non encore implémenté. Suspendrait une organisation : invalide toutes les sessions actives, refuse les connexions ultérieures et conserve les données en lecture seule.
Corps (JSON)
Requête
curl -s -X POST "$API/api/admin/organizations/$ORG_ID/suspend" \
-H "Content-Type: application/json" -H "Cookie: $SESSION" \
-d '{"reason":"Impayés — suspension préventive"}'
Réponse
{ "id": "org_…", "status": "SUSPENDED", "suspendedAt": "2026-06-16T09:00:00Z" }
Workflows
Suspension d'organisation
ACTIVE
└──→ SUSPENDED (raison obligatoire + double confirmation)
├─ sessions membres invalidées
├─ login refusé (page dédiée)
├─ API rejette toutes requêtes sauf endpoints admin
├─ audit log + notification email admin org + super-admin
└──→ ARCHIVED après 90 j de grâce (irréversible)
├─ tables purgées (sauf audit, factures, contrats — obligation légale)
└─ zip JSON archivé S3 (10 ans)
Webhooks sortants
Événement métier (ex : lease.contract.signed)
└──→ webhooks.service.dispatch(event, payload)
└──→ pour chaque Webhook souscrit
├─ crée WebhookDelivery (PENDING)
└─ job send-webhook-deliveries (toutes les 30 s)
├─ charge 50 deliveries PENDING
├─ signe body HMAC
├─ POST → si 2xx → DELIVERED
└─ sinon retry exponentiel (max 10) → FAILED + alerte
Format payload standard :
{
"event": "lease.contract.signed",
"deliveredAt": "2026-06-16T10:30:00Z",
"data": { "...": "..." },
"organizationId": "org_…",
"webhookId": "wh_…"
}
Headers envoyés :
X-Pulse-Event: lease.contract.signed
X-Pulse-Delivery: <delivery-id>
X-Pulse-Signature: t=<timestamp>,v1=<hex-hmac>
Release Notes
Création (draft) → Publication → Modal "Nouveautés" à la prochaine connexion
└──→ bouton "OK, lu" → ReleaseNoteAcknowledgement
└──→ KPI : % users ayant accusé réception
Backup & restauration
- Snapshot DB horaire (rétention 7 j), quotidien (rétention 30 j), PITR 7 j.
- Versioning S3 activé ; Object Lock sur buckets légaux.
- DR drill trimestriel documenté.
Règles métier
Séparation console vs workspace
/admin/* = exclusif SUPERADMIN_PROVIDER (routes /api/admin/* avec
requireOrganization: false). Les admins d'organisation gèrent leur tenant via
les routes /api/workspace/* (members, roles, api-keys, invitations). L'espace
/workspace/admin/* cible n'existe pas encore. Aucune route /api/admin/* n'est
accessible depuis un contexte workspace.
Verrous de sécurité
- Création d'org réservée à
SUPERADMIN_PROVIDER(admin.org:create). Implémenté. - Le plugin admin Better Auth est branché (
admin({ impersonationSessionDuration: 3600 })) maisadminRolesn'est pas configuré : l'autorisation passe par le RBAC maison Pulse, pas par les rôles globaux Better Auth. - Cible : au moins 1
SUPERADMIN_PROVIDERactif en permanence, impersonation auditée en lecture seule, MFA récent (< 24 h) pour les actions destructives.
Webhooks (cible)
Cible — non encore implémenté.
- URL de destination en HTTPS uniquement.
- Secret rotatable : dual-sign pendant 7 j (ancien + nouveau secret acceptés).
ApplicationSettingcontenant un secret → préfixesecret:→ chiffrement au repos automatique.
Cycle de vie organisation (cible)
Cible — non encore implémenté.
- Suspension réversible pendant 90 j.
- Archivage = définitif (données légales conservées : audit, factures, contrats).
- Export données → ZIP via URL signée temporaire.