Le rôle "admin" contournait entièrement l'isolation par projet
(core/auth_guard.py) : il pouvait ouvrir/modifier/supprimer le jeu de
n'importe quel autre compte en connaissant son slug, et la page d'accueil
listait sans filtrage tous les projets de tous les comptes.
- La propriété d'un projet se vérifie désormais sur le segment
"propriétaire" du slug (id du compte), pour tous les rôles y compris
admin — un slug "à plat" (sans compte associé) reste réservé à
l'admin, comportement historique conservé pour ce cas précis.
- routes/games/index.py ne liste plus que les projets du compte connecté.
- Le dossier propriétaire d'un projet est maintenant l'id numérique du
compte, plus jamais son email slugifié (visible en clair dans chaque
URL auparavant) — script de migration fourni et déjà exécuté sur les
données existantes.
- Changer d'email ne renomme plus aucun dossier (n'en dépend plus).
- Deux nouveaux tests de régression, fixtures corrigées en conséquence.
- README réécrit pour refléter l'état actuel du produit (jeu 2D
uniquement, plus de traces de l'ancien éditeur "document").
Remplace le rangement plat projects/<slug>/ par une vraie arborescence
par compte créateur, sans toucher aux routes existantes (aucune ne
déclare de "/" dans son <slug> : le chemin composé "propriétaire_projet"
reste un détail interne à db/, décodé par db/games/project_slug.py, seul
fichier à modifier si la convention change un jour).
- db/game_dir.py, create_game.py, delete_game.py, list_games.py,
move_game.py : résolvent/construisent ce chemin composé. Bénéfice
direct : deux comptes différents peuvent chacun avoir un projet nommé
pareil sans collision (l'unicité ne se vérifie plus que par dossier
propriétaire). move_game.py renomme désormais le dossier PROPRIÉTAIRE
entier (changement d'email), prêt pour un futur multi-projet.
- scripts/migrate_flat_project_slugs.py : migration des projets déjà
créés sous l'ancien rangement plat (simulation par défaut, --apply
pour exécuter).
- routes/auth/profile.py, tests/conftest.py : suppression de compte/jeu
de test via db.delete_game (nettoie aussi le dossier propriétaire
devenu vide) plutôt qu'un shutil.rmtree direct du seul dossier projet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Depuis /profile :
- "Codes de récupération 2FA" : régénère les 10 codes (invalide les 10
précédents d'un coup, voir auth.generate_recovery_codes) et les affiche
une seule fois via le même modal qu'à l'inscription (session flash,
core/recovery_codes_flash.py) — mot de passe actuel requis.
- "Adresse email" : change l'adresse (mêmes règles qu'à l'inscription —
format valide, unicité — voir auth/email_validation.py, désormais
partagé avec create_user.py au lieu d'être dupliqué). Pour un compte
"user", le dossier de son unique projet porte le nom de son adresse
(project_slug = slugify(email)) : il est renommé pour suivre le
changement (db/games/move_game.py, même logique d'unicité par suffixe
que create_game()), sans quoi project_slug ne correspondrait plus à
aucun dossier réel. Un "admin" n'a pas de project_slug dédié : rien
n'est renommé pour ce rôle.
18 nouveaux tests (tests/test_profile.py) : mauvais mot de passe refusé
pour les deux actions, mauvais format/email déjà pris refusés, dossier
bien renommé et project_slug mis à jour, connexion possible avec la
nouvelle adresse, anciens codes de récupération bien invalidés après
régénération, modal jamais réaffiché deux fois.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le nom affiché dans la barre de navigation (base.html) devient un lien
vers /profile : informations du compte (email en lecture seule, nom/
prénom modifiables), changement de mot de passe (mot de passe actuel
requis + mêmes règles de force qu'à l'inscription/la réinitialisation),
et une section "danger" pour demander la suppression du compte.
La suppression exige le mot de passe actuel ET la saisie exacte de
"SUPPRIMER" (deux confirmations distinctes pour une action irréversible)
— routes/auth/profile.py. Pour un compte "user" (limité à un seul projet,
créé automatiquement et impossible à supprimer autrement, voir
core/auth_guard.py), supprimer le compte supprime aussi son unique projet
sur le disque, faute de quoi il resterait orphelin sans plus aucun
propriétaire. Un compte "admin" peut posséder plusieurs jeux qui ne lui
sont pas dédiés de la même façon : ses projets ne sont jamais touchés.
L'unique compte administrateur ne peut pas être supprimé (auth/
count_admins.py) — le supprimer bloquerait la création d'un nouveau
compte admin (réservée au tout premier compte jamais créé, base vide).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>