Commentaires
Modération des commentaires
Section titled “Modération des commentaires”Endpoints réservés admin (token statique) pour mesurer, inspecter et
retirer les commentaires média (media_comment). JSON plat (pas JSON:API).
Toutes les mutations sont journalisées par AdminAuditMiddleware
(cf. Journal d’audit).
Différences clés avec la surface publique /api/media/comments/* :
- Aucune rédaction : le
bodyest renvoyé brut même pour un commentaire soft-deleted (le public le masque ennull). Un modérateur doit lire ce qui a réellement été écrit. - Ids en hex 32 (pas l’UUID à tirets du public), comme le reste de
/admin/*. - Le
DELETEignore laCOMMENT_DELETE_POLICY(author/owner) : un modérateur peut retirer n’importe quel commentaire.
Objet commentaire (plat)
| Champ | Type | Sens |
|---|---|---|
id | hex 32 | id du commentaire |
mediaId | hex 32 | média porteur |
userId | hex 32 | auteur |
parentId | hex 32 | null | parent direct (null = top-level) |
rootId | hex 32 | ancêtre top-level (= id si top-level) |
depth | int | 0 = top-level, +1 par niveau |
isTopLevel | bool | parentId === null |
body | string | texte brut, jamais masqué |
replyCount | int | enfants directs non supprimés |
createdAt | ISO-8601 | |
editedAt | ISO-8601 | null | |
deletedAt | ISO-8601 | null | tombstone soft-delete |
isDeleted | bool | deletedAt !== null |
GET /admin/comments/stats
Section titled “GET /admin/comments/stats”Tableau de bord d’engagement des commentaires. Tout vient de la table
hxa.media_comment (une seule base) en quelques agrégats GROUP BY / SUM
conditionnels — endpoint basse fréquence, pas un hot path. JSON plat.
Query
| Param | Défaut | Sens |
|---|---|---|
days | 30 | fenêtre de la courbe quotidienne, borné 1..366 |
Réponse (200)
totals— compteurs globaux.active+deleted=total;topLevel+replies=active(les soft-deleted sont exclus du découpage de forme pour que le ratio reflète la conversation vivante) ;edited= lignes vivantes amendées au moins une fois.perDay— créations par jour, zéro-rempli (chaque jour de la fenêtre présent,count: 0les jours creux) pour tracer une courbe continue. Compté parcreated_at, soft-delete ultérieur inclus (la création est le signal d’engagement).topMedia— 10 médias les plus commentés (commentaires vivants only),namevia LEFT JOIN (nullsi le média a été purgé).topCommenters— 10 comptes les plus actifs (commentaires vivants only),username/nicknamevia LEFT JOIN (nullsi le compte a été purgé).
{ "totals": { "total": 12840, "active": 12190, "deleted": 650, "topLevel": 7320, "replies": 4870, "edited": 540 }, "perDay": { "days": 30, "from": "2026-05-30", "to": "2026-06-28", "total": 1840, "series": [ { "date": "2026-05-30", "count": 61 }, { "date": "2026-05-31", "count": 0 } ] }, "topMedia": [ { "mediaId": "9f8e7d6c5b4a39281706f5e4d3c2b1a0", "name": "Sunset over Bali", "count": 184 } ], "topCommenters": [ { "userId": "1122334455667788990011223344556677", "username": "marco", "nickname": "Marco P.", "count": 312 } ]}# Fenêtre par défaut (30 jours)curl -s "$BASE/admin/comments/stats" -H "$AUTH"# Fenêtre d'un ancurl -s "$BASE/admin/comments/stats?days=366" -H "$AUTH"Erreurs
| Status | Body | Sens |
|---|---|---|
400 | { "error": "Invalid days." } | days non numérique |
403 | { "error": "..." } | auth KO |
GET /admin/comments/{hex}
Section titled “GET /admin/comments/{hex}”Fiche brute d’un commentaire unique. Expose toutes les colonnes, dont le body
d’un commentaire soft-deleted et les ids de threading.
Réponse (200) : l’objet commentaire plat décrit ci-dessus.
curl -s "$BASE/admin/comments/0a1b2c3d4e5f60718293a4b5c6d7e8f9" -H "$AUTH"Erreurs
| Status | Body | Sens |
|---|---|---|
404 | { "error": "Comment not found." } | row absente ou hex malformé |
403 | { "error": "..." } | auth KO |
DELETE /admin/comments/{hex}
Section titled “DELETE /admin/comments/{hex}”Soft-delete de modération : pose deleted_at et décrémente les compteurs
(reply_count du parent si c’est une réponse, sinon
media_stats.comments_count). La ligne est conservée pour ne pas
orpheliner les réponses enfants. Ignore la COMMENT_DELETE_POLICY.
Réponse (200) : l’objet commentaire plat post-état (isDeleted: true,
deletedAt renseigné, body toujours présent).
curl -s -X DELETE "$BASE/admin/comments/0a1b2c3d4e5f60718293a4b5c6d7e8f9" -H "$AUTH"Erreurs
| Status | Body | Sens |
|---|---|---|
404 | { "error": "Comment not found." } | row absente ou hex malformé |
410 | { "error": "Comment already deleted." } | déjà soft-deleted (idempotence stricte) |
403 | { "error": "..." } | auth KO |
Notes
- Aucune notification n’est émise (ni à l’auteur, ni au propriétaire du média) : la modération reste invisible côté produit, comme pour les signalements.
- Pas de hard-delete : la suppression définitive d’un thread se fait via le
hard-delete du média (
DELETE /admin/media/{hex}, cascade FK) ou un script de purge dédié. - Réversible via
POST /admin/comments/{hex}/restore(ci-dessous).
POST /admin/comments/{hex}/restore
Section titled “POST /admin/comments/{hex}/restore”Annule un soft-delete : efface le tombstone deleted_at et ré-incrémente
les compteurs que la suppression avait décrémentés (reply_count du parent
pour une réponse, sinon media_stats.comments_count).
Réponse (200) : l’objet commentaire plat post-état (isDeleted: false,
deletedAt: null).
curl -s -X POST "$BASE/admin/comments/0a1b2c3d4e5f60718293a4b5c6d7e8f9/restore" -H "$AUTH"Erreurs
| Status | Body | Sens |
|---|---|---|
404 | { "error": "Comment not found." } | row absente ou hex malformé |
409 | { "error": "Comment is not deleted." } | commentaire déjà actif (rien à restaurer) |
403 | { "error": "..." } | auth KO |
Note : un commentaire dont le parent est encore supprimé peut être restauré —
reply_countest un cache, la cohérence d’affichage reste gérée côté rendu du thread.
POST /admin/comments/{hex}/censor
Section titled “POST /admin/comments/{hex}/censor”Censure éditoriale : écrase définitivement le body par le message
COMMENT_CENSOR_MESSAGE (défaut [Commentaire modéré]). Contrairement au
soft-delete, le commentaire reste visible — la structure du thread est
préservée et un lecteur voit la notice de modération à la place du texte
original.
Le texte original n’est pas archivé : l’opération est irréversible (il
n’existe pas de pendant « uncensor » ; pour re-masquer sans effacer, utiliser
DELETE /admin/comments/{hex}). Les compteurs (reply_count,
media_stats.comments_count) restent inchangés — le commentaire compte
toujours. edited_at n’est pas touché (ce n’est pas une édition d’auteur).
Réponse (200) : l’objet commentaire plat post-état — body vaut le
message de censure, isDeleted inchangé.
curl -s -X POST "$BASE/admin/comments/0a1b2c3d4e5f60718293a4b5c6d7e8f9/censor" -H "$AUTH"Erreurs
| Status | Body | Sens |
|---|---|---|
404 | { "error": "Comment not found." } | row absente ou hex malformé |
403 | { "error": "..." } | auth KO |
Notes
- Idempotent : re-censurer réécrit simplement le même message (
200). - Aucune notification émise (comme le soft-delete de modération).
- Le message de remplacement est stocké tel quel dans
body(chaîne figée, pas de résolution i18n au rendu) : il s’affiche à l’identique pour toutes les locales. AjusterCOMMENT_CENSOR_MESSAGEen amont si besoin.