API Admin
Objectif
Documenter les endpoints sensibles du backoffice DMV réservés aux administrateurs globaux ou opérateurs autorisés.
Périmètre
Gestion dashboard, utilisateurs, rôles, acteurs, publications, claims, abonnements, messages et communes.
Applications consommatrices
- Backoffice DMV uniquement.
- Support/administration autorisés.
Authentification requise
Toutes les routes visibles utilisent :
identify.app;auth:sanctum;ensure.admin.
Le commentaire du code mentionne /api/admin/, mais les providers montent les modules sous api/v1 et le route group utilise prefix('admin'). Le chemin documenté est donc /api/v1/admin/... à confirmer par route:list.
Permissions/RBAC
- Endpoints sensibles.
- Réservés admin global ou opérateurs explicitement autorisés.
ensure.adminvérifieisAdmin()via rôleadminselon commentaire code.- Aucun accès
service_rolecôté client.
Endpoints confirmés visibles
| Méthode | Route | Domaine |
|---|---|---|
GET | /api/v1/admin/users | Utilisateurs. |
GET | /api/v1/admin/dashboard | Dashboard agrégé backoffice. |
GET | /api/v1/admin/users/{userId} | Utilisateurs. |
PATCH | /api/v1/admin/users/{userId} | Utilisateurs. |
POST | /api/v1/admin/users/{userId}/roles | Rôles. |
DELETE | /api/v1/admin/users/{userId}/roles/{role} | Rôles. |
GET | /api/v1/admin/acteurs | Acteurs. |
PATCH | /api/v1/admin/acteurs/{id}/validate | Validation acteur. |
PATCH | /api/v1/admin/acteurs/{id}/badge | Badge acteur. |
PATCH | /api/v1/admin/acteurs/{id}/active | Activation/désactivation acteur. |
DELETE | /api/v1/admin/acteurs/{id}/restore | Restauration acteur. |
POST | /api/v1/admin/acteurs/{id}/can-publish | Droit publication acteur. |
GET | /api/v1/admin/claims | Claims. |
PATCH | /api/v1/admin/claims/{id} | Claims. |
GET | /api/v1/admin/publications | Liste paginée des publications. |
PATCH | /api/v1/admin/publications/bulk/moderate | Modération en lot de plusieurs publications. |
PATCH | /api/v1/admin/publications/{id}/moderate | Modération publication. |
PATCH | /api/v1/admin/publications/{id}/valide | Bascule visibilité/validation. |
PATCH | /api/v1/admin/publications/{id}/feature | Mise en avant (en_avant, featured_until). |
DELETE | /api/v1/admin/publications/{id} | Soft-delete publication. |
PATCH | /api/v1/admin/publications/{id}/restore | Restauration publication. |
GET | /api/v1/admin/subscriptions | Abonnements. |
POST | /api/v1/admin/communes | Communes. |
PATCH | /api/v1/admin/communes/{id} | Communes. |
GET | /api/v1/admin/communes/{id}/full | Snapshot complet d'une commune pour le backoffice. |
GET | /api/v1/admin/communes/{id}/defibrillateurs | Liste admin des défibrillateurs de la commune. |
POST | /api/v1/admin/communes/{id}/defibrillateurs/sync | Synchronisation GEODAE pour une commune. |
PATCH | /api/v1/admin/defibrillateurs/{id} | Édition du statut admin d'un défibrillateur communal. |
GET | /api/v1/admin/defibrillateurs-france | Catalogue national paginé des défibrillateurs. |
POST | /api/v1/admin/defibrillateurs-france/sync | Synchronisation nationale GEODAE. |
PATCH | /api/v1/admin/defibrillateurs-france/{id} | Édition du statut admin d'un défibrillateur national. |
GET | /api/v1/admin/messages/threads | Fils de messages support. |
GET | /api/v1/admin/messages/threads/{id}/messages | Messages d'un fil. |
POST | /api/v1/admin/messages/threads/{id}/reply | Répondre (envoie un email via Resend). |
PATCH | /api/v1/admin/messages/threads/{id}/read | Marquer comme lu. |
PATCH | /api/v1/admin/messages/threads/{id}/unread | Marquer comme non lu. |
PATCH | /api/v1/admin/messages/threads/{id}/flag | Changer la couleur de flag. |
PATCH | /api/v1/admin/messages/threads/{id}/close | Ouvrir/fermer le fil. |
GET | /api/v1/admin/import-sources | Liste des sources d'import automatique. |
POST | /api/v1/admin/import-sources | Créer une source d'import. |
GET | /api/v1/admin/import-sources/{id} | Lire une source d'import. |
PATCH | /api/v1/admin/import-sources/{id} | Modifier une source d'import. |
DELETE | /api/v1/admin/import-sources/{id} | Supprimer une source d'import. |
POST | /api/v1/admin/import-sources/{id}/run | Déclencher un import manuel (?dry_run=true pour simulation). |
GET | /api/v1/admin/import-sources/{id}/runs | Historique des runs d'une source. |
GET | /api/v1/admin/import-sources/{id}/logs/{date} | Log d'un run spécifique (format YYYY-MM-DD). |
Endpoints cibles si nécessaire
- Journal d’audit admin à exposer en lecture contrôlée, non confirmé.
- Gestion opérateurs/support différenciée à confirmer.
Exemples génériques de requête/réponse
POST /api/v1/admin/users/user-id/roles
Authorization: Bearer <admin-token>
X-App-Source: backoffice
Content-Type: application/json
{
"role": "admin"
}
{
"data": {
"user_id": "user-id",
"roles": ["admin"]
}
}
Exemple dashboard :
GET /api/v1/admin/dashboard
Authorization: Bearer <admin-token>
X-App-Source: backoffice
{
"publications": 1284,
"publications_pending": 12,
"publications_reported": 3,
"acteurs": 542,
"users": 1880
}
Exemple liste publications admin :
GET /api/v1/admin/publications?page=1&per_page=10&moderation_status=pending&sort=created_at_desc
Authorization: Bearer <admin-token>
X-App-Source: backoffice
Erreurs principales
{
"error": "admin_required",
"message": "Droits administrateur requis."
}
Audit/logs si applicable
Obligatoire pour toutes les mutations admin :
- rôles utilisateurs ;
- validation acteur ;
- badge ;
- restauration ;
- droit de publication ;
- décision claim ;
- création/mise à jour commune.
Sécurité
- Endpoints très sensibles.
- Pas d’usage depuis un frontend public.
- Pas de clé service exposée côté client.
- Limiter les données personnelles affichées.
- L’IA ne doit pas appeler ces endpoints directement sans backend contrôlé et validation humaine.
État actuel vs cible
| Sujet | État actuel | Cible |
|---|---|---|
| Routes admin | Confirmées, avec dashboard et publications backoffice. | Audit complet et rôles opérateurs détaillés. |
ensure.admin | Confirmé. | Matrice admin/support plus fine si besoin. |
| URL finale | /api/v1/admin/... confirmée dans le route group. | Contrat route/payload documenté plus finement. |
Notes d'implémentation observées
- L'endpoint
GET /api/v1/admin/dashboarddoit rester tolérant aux tables optionnelles ou absentes afin d'éviter un500global du dashboard. - Le listing admin des publications supporte
per_pageet le backoffice l'utilise actuellement à10éléments par page. - Le listing admin des publications charge page par page ; il ne doit plus charger l'ensemble du stock au premier affichage.
- Les filtres supportés par
GET /api/v1/admin/publicationssontpage,per_page,sort,q,commune_id,acteur_id,type_id,moderation_status,valide,deletedetduplicates. - Le backoffice ouvre désormais la vue avec
moderation_status=pending,sort=created_at_descetper_page=10.
Doublons publications
Le filtre duplicates=true active un groupement serveur des publications
suspectes. La détection commence par source_url normalisée, puis bascule sur
une signature de contenu normalisée basée sur acteur_id, type_id, titre,
date_debut et le début du contenu.
Le backend renvoie alors duplicate_key, duplicate_reason et
duplicate_count pour permettre au backoffice d'afficher les groupes plutôt
que des lignes isolées.
Bug corrigé : dashboard 500 (2026-06-26)
La jointure subscription_logs → subscription_plans comparait l.plan_id (UUID)
avec p.slug (varchar) — PostgreSQL rejetait l'opérateur. Correction : jointure
sur p.id = l.plan_id. Les blocs subscription_plans et subscription_logs
sont désormais enveloppés dans des blocs try/catch pour éviter un 500 global
si le schéma évolue.
Bug corrigé : publications invisibles après validation manuelle (2026-06-26)
Les méthodes moderate(), bulkModerate() et toggleValide() de
AdminPublicationService mettaient valide = true et
moderation_status = 'published' sans remplir publie_le. Or tous les filtres
du frontend exigent publie_le IS NOT NULL. Correction : lorsqu'une publication
passe à published, publie_le est fixé à now() si la valeur est encore NULL.
Impact : les publications importées puis approuvées manuellement dans le backoffice étaient entièrement invisibles sur le front, même si leur statut apparaissait correct. 82 publications ont été rétroactivement corrigées le 2026-06-22.
Points à confirmer
- Différence admin global vs opérateur support.
- Payloads exacts.
- Audit log exposé ou interne.
- Confirmation route réelle via commande Laravel.