Skip to content

Commentaires

Système de commentaires threadés sur les médias, avec réponses jusqu’à COMMENT_MAX_DEPTH niveaux (défaut : 5, soit depth ∈ [0, 4]).

ChampNotes
idUUID v4 du commentaire.
mediaIdUUID du média porteur.
userIdUUID de l’auteur.
parentIdUUID du commentaire parent, ou null pour un top-level.
rootIdUUID du top-level ancestral. Pour un top-level, rootId = id (dénormalisation : permet le fetch d’un thread complet avec un seul WHERE root_id = ?, sans CTE récursive).
depth0 pour un top-level, +1 par niveau de réponse. Plafonné à COMMENT_MAX_DEPTH - 1.
bodyTexte du commentaire. null quand le commentaire est soft-supprimé.
replyCountNombre d’enfants directs non supprimés. Maintenu applicativement.
createdAtDate d’écriture.
editedAtDate de la dernière édition ; null si jamais édité.
deletedAtDate de soft-delete ; null si actif.

Quand deletedAt !== null : la ligne est conservée pour ne pas orphelinser les enfants, mais body est renvoyé à null et meta.deleted = true est ajouté à la ressource.

  • media_comment.reply_count : enfants directs non supprimés (utilisé pour les listings de réponses).
  • media_stats.comments_count : top-level non supprimés (exposé dans la ressource Media en tant que commentsCount, utilisé pour meta.total du listing /api/media/{mediaId}/comments).

Les deux sont maintenus dans la même transaction que l’INSERT / soft-delete par MediaCommentService. Contrairement aux likes/dislikes (triggers), le choix applicatif évite des triggers complexes autour du soft-delete.

VarDéfautRôle
COMMENT_MAX_LENGTH2000Longueur max du body (en caractères UTF-8, via mb_strlen).
COMMENT_MAX_DEPTH5Niveaux de profondeur autorisés (depth ∈ [0, max-1]).
COMMENT_EDIT_WINDOW_MINUTES15Fenêtre d’édition après création. 0 = édition désactivée, -1 = illimitée, N>0 = N minutes.
COMMENT_DELETE_POLICYbothQui peut soft-supprimer : author, owner (propriétaire du média), ou both.
  • Top-level → l’owner du média reçoit media.comment.received (déduplique sur (media, actor) dans la fenêtre).
  • Réponse → l’auteur du commentaire parent reçoit media.comment.reply.received (déduplique sur (parent, actor)).
  • Pas de notif sur soi-même (commenter / répondre à son propre contenu reste autorisé mais silencieux).
  • Pas de notif lors de l’édition ni du soft-delete.

Les commentaires ne sont pas indexés dans Meilisearch. Le volume peut être élevé et il n’y a pas de cas d’usage de recherche full-text aujourd’hui ; un index pourra être ajouté plus tard sans casser l’API (les ressources resteront identiques).

Liste les commentaires top-level d’un média, du plus récent au plus ancien. Paginé en keyset (cursor) — convention partagée avec les autres listings. Public (pas d’auth requise).

Query :

  • limit (1..100, def 20)
  • cursor / before (mutuellement exclusifs)

meta.total lit le compteur dénormalisé media_stats.comments_count (top-level non supprimés). Les commentaires soft-supprimés sont retournés avec body: null + meta.deleted = true.

ListMediaCommentsAction

Erreurs :

  • 404 Media not found
  • 400 Invalid cursor
  • 422 Invalid media id

Crée un commentaire top-level OU une réponse. Auth requise (Bearer). L’utilisateur doit avoir confirmé son e-mail et ne pas être banni.

Body (flat ou JSON:API) :

{ "body": "Joli cliché !", "parentId": null }
{
"data": {
"type": "mediaComments",
"attributes": {
"body": "Merci pour la réponse 🙏",
"parentId": "0192c4e3-…"
}
}
}
  • body (string, requis, trimé non-vide, ≤ COMMENT_MAX_LENGTH).
  • parentId (UUID, optionnel). Si renseigné, le parent doit appartenir au même média, ne pas être soft-supprimé, et parent.depth + 1 doit rester < COMMENT_MAX_DEPTH.

Réponse 201 : ressource mediaComments (cf. modèle plus haut). CreateMediaCommentAction

Erreurs (meta.code) :

  • 422 comment.bodyMissing — body absent / vide après trim.
  • 422 comment.bodyTooLong — > COMMENT_MAX_LENGTH.
  • 404 comment.mediaNotFound — média inconnu.
  • 404 comment.parentNotFound — parent inconnu ou rattaché à un autre média.
  • 403 comment.parentDeleted — répondre à un commentaire supprimé est refusé.
  • 422 comment.depthExceeded — profondeur max atteinte.
  • 403 comment.actorNotConfirmed / comment.actorBanned.

GET /api/media/comments/{commentId}/thread

Section titled “GET /api/media/comments/{commentId}/thread”

Renvoie le thread complet ancré au top-level dont dépend commentId. Si commentId pointe sur une réponse profonde, on remonte automatiquement à son rootId — la réponse est donc toujours la conversation entière (utile pour un deep-link arrivant depuis une notification).

Pas de pagination (le thread est borné par COMMENT_MAX_DEPTH). Les nœuds sont retournés à plat, triés par (depth ASC, createdAt ASC, id ASC) ; le client reconstruit l’arbre via parentId.

GetMediaCommentThreadAction

Erreurs :

  • 404 Comment not found
  • 422 Invalid comment id

GET /api/media/comments/{commentId}/replies

Section titled “GET /api/media/comments/{commentId}/replies”

Liste les enfants directs d’un commentaire, du plus ancien au plus récent (ordre chronologique de réponse). Keyset-paginé.

Query : limit (1..100, def 20), cursor / before.

meta.total reprend le replyCount du parent (dénormalisé). ListMediaCommentRepliesAction

Erreurs :

  • 404 Comment not found
  • 400 Invalid cursor
  • 422 Invalid comment id

Édite le body d’un commentaire. Auth requise. Seul l’auteur peut éditer, et uniquement dans la fenêtre COMMENT_EDIT_WINDOW_MINUTES (0 = édition désactivée, -1 = illimitée).

Body (flat ou JSON:API) — mêmes shapes que la création, mais sans parentId (le re-parentage n’est jamais autorisé) :

{ "body": "..." }

Réponse 200 : ressource mise à jour avec editedAt rempli. UpdateMediaCommentAction

Erreurs (meta.code) :

  • 422 comment.bodyMissing / comment.bodyTooLong.
  • 404 comment.notFound.
  • 403 comment.forbidden — pas l’auteur.
  • 410 comment.alreadyDeleted — éditer un commentaire supprimé est refusé.
  • 403 comment.editWindowClosed — fenêtre expirée (ou COMMENT_EDIT_WINDOW_MINUTES=0).
  • 403 comment.actorNotConfirmed / comment.actorBanned.

Soft-supprime le commentaire selon COMMENT_DELETE_POLICY :

  • author — seul l’auteur peut supprimer.
  • owner — seul le propriétaire du média peut supprimer.
  • both (défaut) — l’un ou l’autre.

La ligne est conservée pour ne pas orphelinser ses enfants. Le body cesse d’être renvoyé (null) et meta.deleted = true est ajouté à la ressource. Les compteurs reply_count du parent (si réponse) ou media_stats.comments_count (si top-level) sont décrémentés dans la même transaction.

Réponse 200 : ressource redactée (pas de 204 pour permettre au client de mettre à jour sa vue en place). DeleteMediaCommentAction

Erreurs (meta.code) :

  • 404 comment.notFound.
  • 410 comment.alreadyDeleted — opération idempotente côté UX, mais l’API signale l’état.
  • 403 comment.forbidden — la politique en vigueur n’autorise pas le caller.
  • 403 comment.actorNotConfirmed / comment.actorBanned.