# Manuel d'incident À lire quand quelque chose ne va pas. Chaque section : le symptôme, où regarder, quoi faire. > **Le réflexe préalable** — demander la **référence** affichée sur la page > d'erreur (`Référence à indiquer si vous signalez ce problème`). C'est > l'identifiant de requête ; il apparaît dans chaque ligne de journal produite > par cette requête, entre crochets. > > ```bash > grep '\[a1b2c3d4e5f60718\]' logs/*.log > ``` ## Où sont les journaux | Fichier | Contenu | |---|---| | `logs/app.log` | Tout, à partir de `LOG_LEVEL` (défaut `INFO`) | | `logs/errors.log` | `ERROR` et au-dessus, avec les traces | | `logs/auth.log` | Connexions, échecs, verrous, changements de rôle, suppressions de compte, refus d'inscription | | Console Pterodactyl | Les mêmes lignes que `app.log` — le handler console est actif en production, délibérément | | `C:\nginx\logs\error.log` | Ce qui n'a jamais atteint l'application | Format : `[date] NIVEAU [module:ligne] [id-de-requête] message`. Un `-` à la place de l'identifiant signifie « hors requête » : démarrage, bot Discord, planificateur. ### Les événements de `auth.log` Tout est en `clé=valeur`, dans l'ordre, donc greppable sans dépendance JSON. | Événement | Quand | |---|---| | `login.success` | Connexion réussie | | `login.failure` | Mot de passe faux sur un compte existant | | `login.failure.unknown_user` | Identifiant inconnu | | `login.rejected.deactivated` | Bons identifiants, compte désactivé | | `account.throttled` | Délai d'attente déclenché après des échecs répétés | | `logout` | Déconnexion | | `account.registered` | Inscription réussie | | `account.registration_refused` | Inscription filtrée — `reason=honeypot`, `too-fast` ou `no-form-issued` | | `account.created_by_admin` | Compte créé depuis l'administration | | `account.updated` | Fiche modifiée | | `account.role_changed` | Changement de rôle, avec l'ancien et le nouveau | | `account.password_changed` | Changement par la personne elle-même | | `account.password_reset_by_admin` | Réinitialisation par un président | | `account.deleted` | Suppression, avec le nombre de fichiers de contrat retirés | Le champ `ip=` vient de `request.remote_addr`, donc de `X-Forwarded-For`. Tant que `TRUSTED_PROXY=*`, **c'est une indication et pas une preuve**. ## Le site ne répond pas 1. **`/health` répond-il ?** ```bash curl -i https:///health ``` - Pas de réponse du tout → nginx est tombé, ou l'application n'écoute plus. Vérifier la console Pterodactyl. - `503` → l'application tourne mais la base ne répond pas. Voir plus bas. - `200` mais le site est inutilisable → le problème est dans nginx ou dans le DNS, pas dans l'application. 2. **L'application est-elle démarrée ?** Console Pterodactyl. Au démarrage elle imprime `Starting Waitress server on :`. 3. **Un déploiement vient-il d'avoir lieu ?** C'est la cause la plus fréquente. Voir « Revenir en arrière » dans `docs/deployment.md`. ## `/health` renvoie 503 La base ne répond pas. Le corps de la réponse le dit : ```json {"status": "unhealthy", "database": "error"} ``` Le corps ne contient **jamais** l'erreur du pilote : elle porte l'hôte, le nom de la base et l'utilisateur de la chaîne de connexion, et `/health` n'est pas authentifié. La trace complète est dans `logs/errors.log`. - La base est hébergée sur Render : vérifier son état côté Render en premier. - `DATABASE_URL` a-t-elle changé ? Un mot de passe tourné et pas reporté produit exactement ça. - **Piège connu** : `DATABASE_URL=postgresql://…` seul ne démarre pas — SQLAlchemy y cherche psycopg **2**, le projet épingle psycopg **3**. `normalise_database_url()` nomme le pilote, donc les deux formes marchent ; si vous voyez `ModuleNotFoundError: psycopg2`, c'est que le code qui normalise n'a pas été déployé. ## Plus aucune notification Discord Le bot tourne dans un **fil du même processus** que le site. Quand il meurt, les pages continuent d'être servies et toutes les notifications s'arrêtent. C'est précisément ce qui est resté invisible longtemps. 1. `/health` le rapporte : ```json {"discord_bot": {"running": false, ...}} ``` Rapporté et **non fatal** : un club sans rappels Discord est dégradé, pas hors service, et un 503 le sortirait du répartiteur de charge pour ça. 2. Chercher les livraisons manquées : ```bash grep 'not delivered' logs/app.log ``` Trois causes, qui ne se traitent pas pareil : - `their direct messages are closed` → **définitif**. La personne doit autoriser les messages privés des membres du serveur. Réessayer ne sert à rien. - `does not exist` → l'identifiant Discord sur le compte est faux ou le compte a été supprimé. À corriger dans la fiche de la personne. - autre chose → passager, côté Discord. 3. Le bilan du lot quotidien : ```bash grep 'Daily reminders' logs/app.log ``` `17 of 20 delivered, 3 failed` — les trois sont nommées juste au-dessus. 4. Redémarrer le processus relance le bot. Les réactions en attente survivent au redémarrage (`discord_pending.json`), sauf si ce fichier a été mis de côté pour corruption — auquel cas le journal le dit et donne le nom du fichier de quarantaine. ## Quelqu'un est verrouillé dehors Cinq échecs consécutifs déclenchent un délai qui double ensuite. Le compte n'est pas bloqué définitivement. ```bash grep -E 'login\.failure|account\.throttled' logs/auth.log | grep '' ``` - **Le verrou se lève tout seul.** Le délai est dans le message. - Un président peut réinitialiser le mot de passe depuis la fiche du compte, ce qui remet le compteur à zéro. - **Beaucoup de verrous sur des comptes différents en même temps** → quelqu'un essaie des mots de passe. Le champ `ip=` est une indication et **pas une preuve** tant que `TRUSTED_PROXY=*` (voir `OPS-002` dans `docs/deployment.md`) : il est falsifiable à chaque requête. ## Une page renvoie 500 1. Récupérer la référence auprès de la personne, ou la dernière trace : ```bash tail -50 logs/errors.log ``` 2. La trace complète y est. Elle n'est jamais montrée à l'utilisateur. 3. Si c'est arrivé juste après un déploiement, revenir en arrière d'abord et diagnostiquer ensuite. ## Le style ou le JavaScript ne se chargent plus Les statiques sont servis par nginx avec un cache de 30 jours, ce qui n'est sûr que parce que leurs URL portent une estampille (`?v=`). - **Après un déploiement, la page a l'ancien style** → l'estampille n'a pas changé, donc le fichier n'a pas été déployé. Vérifier : ```bash curl -s https:///auth/login | grep -o 'style.css?v=[0-9]*' ``` - **404 sur `/static/…`** → le chemin `alias` du bloc `location /static/` de `nginx.conf` ne pointe pas au bon endroit. - **Une page est cassée et la console du navigateur parle de CSP** → un bloc `