> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cel-eleague.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sanctions moderation

# 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 :

```ts theme={null}
targetType: "PERSON"
```

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 :

* `ALL`
* `ACTIVE`
* `HISTORICAL`

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.
