Sanctions et modération
Cette page documente le modèle de sanctions réellement présent dans le repo.
Sources principales :
README.md
convex/schema.ts
convex/lib/person_sanctions.ts
convex/admin/sanctions.ts
convex/messaging/events.ts
convex/messaging/bus.ts
Modèle canonique personne
Une sanction personne est stockée dans sanctions avec :
Types canoniques vérifiés :
PERSON_BAN
PERSON_SUSPENSION
PERSON_DISCIPLINARY_REVIEW
Effets canoniques vérifiés :
APP_ACCESS
SPORT_ELIGIBILITY
VISIBILITY
CONTACT
Cycle de vie
États vérifiés :
ACTIVE
ENDED_MANUAL
ENDED_EXPIRED
ENDED_REPLACED
Sources de fin vérifiées :
MANUAL
AUTO_EXPIRED
REPLACED
Une sanction active doit cibler PERSON, avoir isActive = true, ne pas avoir endedAt, et ne pas être expirée.
État de restriction utilisateur
getPersonRestrictionState retourne un état agrégé :
hasActiveRestriction
activeSanctionIds
activeEffects
reasonSummary
nextExpiryAt
blockingMessage
Le message bloquant est produit quand l’effet APP_ACCESS est actif.
Sanctions expirées
Une sanction active avec expiresAt <= now est considérée comme expirée dans les vues effectives.
Elle peut apparaître comme :
isActive = false
lifecycleState = ENDED_EXPIRED
endSource = AUTO_EXPIRED
Historique
listPersonSanctionHistoryFromRecords supporte les vues :
La liste est triée par createdAt décroissant.
Il existe aussi une vue near-expiry avec fenêtre configurable.
Création d’une sanction personne
Inputs validés :
actorUserId
personUserId
sanctionType
reason
expiresAt
effectScopes
Règles vérifiées :
- raison non vide ;
- au moins un effet canonique ;
- date d’expiration future si fournie ;
- acteur admin ou moderator ;
- un moderator ne peut pas sanctionner un admin ;
- un acteur ne peut pas appliquer une sanction à lui-même.
Effets :
- insertion dans
sanctions ;
targetType = PERSON ;
lifecycleState = ACTIVE ;
isActive = true ;
- audit personne ;
- notification si transition de restriction ;
- invalidation de sessions si
APP_ACCESS ;
- sync Algolia
sanction.
Remplacement d’une sanction
replacePersonSanctionCommand crée une nouvelle sanction et termine l’ancienne.
Règles vérifiées :
- ancienne sanction existante ;
- ancienne sanction cible
PERSON ;
- ancienne sanction active ;
- même
targetId ;
- nouvelle sanction normalisée comme une création.
Effets sur l’ancienne sanction :
isActive = false ;
lifecycleState = ENDED_REPLACED ;
endSource = REPLACED ;
replacedBySanctionId ;
endedAt ;
endedByUserId.
Effets sur la nouvelle sanction :
replacementOfSanctionId ;
- audit de création ;
- audit de remplacement ;
- notification selon transition ;
- sync Algolia des deux sanctions.
Fin manuelle d’une sanction
endPersonSanctionCommand termine une sanction active.
Règles vérifiées :
- sanction existante ;
- cible
PERSON ;
- sanction active ;
- acteur autorisé ;
- raison de fin optionnelle mais non vide si fournie.
Effets :
isActive = false ;
lifecycleState = ENDED_MANUAL ;
endSource = MANUAL ;
endedAt ;
endedByUserId ;
- audit ;
- notification si restriction levée ;
- sync Algolia.
Notifications de sanction
Événements de messagerie liés :
person_sanction.activated
person_sanction.ended
user.banned
user.unbanned
user.reactivated
user.deleted
APP_ACCESS déclenche les notifications de type ban/unban en plus des notifications de sanction personne.
Legacy
Le README indique que le runtime ne lit plus :
users.banned
users.banReason
users.banExpires
Le backfill legacy est disponible dans convex/migration/person_sanctions.ts.
Sanctions club et compétition
Le schéma contient aussi des types historiques ou club/compétition :
BAN_PLAYER
SUSPENSION
BAN_CLUB
PENDING_DISCIPLINARY
POINTS_DEDUCTION
FINE
FORFAIT
COMPETITION_BAN
La documentation détaillée de ces sanctions doit rester alignée avec les modules métier qui les créent, notamment les forfaits club et l’éligibilité compétition.Last modified on June 24, 2026