Commit Graph
11 Commits
Author SHA1 Message Date
williamandClaude Sonnet 5 5c2d7c49cd Corrige l'affichage des codes de récupération 2FA (invisibles en pratique)
Le modal était placé en dehors de #pageChrome ET de <main> — or pjax.js
(swapDocument()) ne remplace jamais que ces deux conteneurs à chaque
navigation, jamais le body entier. La redirection qui suit la
confirmation de la 2FA passe par une navigation pjax (fetch), pas un
vrai rechargement de page : le HTML du modal était bien renvoyé par le
serveur, mais jamais copié dans le DOM réellement affiché, donc jamais
visible pour un utilisateur qui vient de s'inscrire.

Déplacé à l'intérieur de <main class="content"> : fait maintenant partie
de innerHTML remplacé par pjax.js à chaque navigation, comme le reste du
contenu de page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 06:18:00 +02:00
williamandClaude Sonnet 5 0d46abe193 Ajoute une page de profil (modifier ses infos, mot de passe, supprimer son compte)
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>
2026-08-28 21:03:47 +02:00
williamandClaude Sonnet 5 9efe119936 Ajoute des codes de récupération 2FA (perte du téléphone)
10 codes à usage unique (format "xxxx-xxxx-xxxx") sont générés à
l'instant même où la 2FA est confirmée (auth/recovery_codes.py, table
_recovery_codes séparée pour marquer/consommer chaque code un par un) —
seul leur hash (werkzeug, comme les mots de passe) est stocké, ils ne
sont visibles en clair qu'à cet instant précis.

Plutôt que d'interrompre la redirection habituelle après confirmation de
la 2FA, les codes sont posés en session ("recovery_codes_to_show") et
affichés une seule fois, en modal, dès le premier rendu de base.html qui
suit (core/recovery_codes_flash.py, session.pop) — préserve tel quel le
comportement de redirection déjà couvert par les tests existants.

Sur /login/2fa, un code de récupération est accepté à la place du code
TOTP habituel (routes/auth/login_2fa.py) : verify_totp est essayé en
premier (verify_recovery_code consomme le code dès qu'il correspond, on
ne veut pas en griller un pour rien sur une saisie qui aurait en fait
été un TOTP valide). Compte toujours vers le même compteur anti-bruteforce
que le code TOTP (déjà en place, voir auth/rate_limit.py).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 20:42:39 +02:00
williamandClaude Sonnet 5 a52b244f27 Ajoute la protection CSRF sur tous les formulaires et requêtes AJAX
Un jeton unique par session (core/csrf.py, exposé côté Jinja via
csrf_token()) est vérifié sur toute requête non-GET par un before_request
(core/csrf_guard.py), dans le même esprit que core/auth_guard.py : une
seule garde globale plutôt que de toucher aux ~90 routes existantes une
par une.

L'app entière fait déjà transiter ses formulaires par fetch() : pjax.js
intercepte chaque <form> interne et le transforme lui-même en requête
fetch (aucun usage de l'attribut d'échappement data-no-pjax nulle part
dans le repo, confirmé par grep). Il suffit donc de patcher window.fetch
UNE SEULE FOIS (static/csrf_fetch.js) pour y ajouter automatiquement
l'en-tête X-CSRFToken sur toute requête non-GET, formulaires pjax comme
fetch() écrits à la main dans screen_edit.html/game_dashboard.html/
play.html — sans modifier un seul appel existant.

La vérification est désactivée quand app.config["TESTING"] est actif
(même convention que Flask-WTF/WTF_CSRF_ENABLED), pour ne pas avoir à
ajouter le jeton aux ~170 tests existants qui appellent les routes
directement via le client de test Flask. tests/test_csrf.py réactive
volontairement la garde pour la mettre à l'épreuve pour de vrai (GET
jamais bloqué, POST sans jeton/avec mauvais jeton -> 400, POST avec le
bon jeton via l'en-tête ou le champ de formulaire -> succès).

templates/play.html reçoit les mêmes deux balises que base.html car il
est autonome (ne l'étend pas, propre <html>/<head>).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 20:35:32 +02:00
williamandClaude Sonnet 5 d90abc827b Ajoute l'authentification : inscription, mot de passe fort, 2FA obligatoire, isolation par utilisateur
Première des deux grandes fonctionnalités demandées (authentification
d'abord, export HTML/CSS/JS autonome ensuite) :

- Inscription (nom, prénom, email UNIQUE, mot de passe) avec schéma
  visuel du mot de passe (jauge + liste de critères qui passent au vert
  en direct — auth/password_strength.py, mêmes règles vérifiées côté
  serveur qu'affichées côté client).
- Double authentification (TOTP, compatible Google Authenticator/Authy)
  OBLIGATOIRE dès l'inscription : QR code (SVG, sans dépendance Pillow)
  à scanner puis code à confirmer avant que le compte soit utilisable —
  voir auth/create_user.py (totp_confirmed) et routes/auth/register_2fa.py.
- Connexion en 2 temps (mot de passe puis code TOTP), déconnexion.
- Isolation par utilisateur : un compte "user" est limité à un SEUL
  projet, dont le dossier est nommé d'après son adresse email (slugifiée)
  et créé automatiquement dès la 2FA confirmée — aucune page de gestion
  multi-jeux pour lui (redirigé directement vers son propre tableau de
  bord). Le rôle "admin" reste illimité, comme le moteur l'a toujours été
  (le TOUT PREMIER compte jamais créé sur une base de comptes vide devient
  automatiquement admin — voir auth/is_first_user.py — pas de mot de
  passe par défaut à faire circuler : s'inscrire en premier suffit).
  Un compte "user" ne peut pas non plus supprimer son unique projet
  (aucune façon d'en recréer un ensuite).
- Garde d'accès globale (core/auth_guard.py, un seul before_request) :
  toute page exige une connexion, sans avoir touché individuellement aux
  ~80 routes déjà existantes du moteur.

tests/conftest.py isole complètement les tests de la vraie base de
comptes (FORGE_USERS_DB_PATH/FORGE_SECRET_KEY_PATH vers un dossier
temporaire propre à la session de tests) et authentifie automatiquement
la fixture `client` partagée en tant que compte admin de test — les 155
tests déjà existants continuent de passer SANS AUCUNE modification de
leur côté, exactement comme avant l'authentification. 10 nouveaux tests
dédiés (tests/test_auth.py) : inscription/mots de passe/2FA/connexion/
isolation par projet/blocage de suppression, vérifiés en conditions
réelles (vrai client de test Flask, vraie base SQLite, vrais codes TOTP
calculés avec pyotp). 165 tests au total, tous au vert.

Nouvelles dépendances : pyotp, qrcode (requirements.txt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 19:30:43 +02:00
williamandClaude Sonnet 5 b8dfdcb205 Corrige la nav pjax, panneau déplaçable/redimensionnable pour éditer un objet
1) Bug pjax trouvé : swapDocument() cherchait "header.topbar", qui n'a
jamais existé (c'est un <nav>, pas un <header>) — la barre de navigation
du jeu (ajoutée après pjax.js) n'était donc jamais mise à jour pendant
une navigation pjax : absente en arrivant sur un jeu sans Ctrl+F5, et
inversement laissée en place en revenant sur l'accueil où elle n'a rien
à faire. Fix : tout ce qui est hors <main> mais doit changer d'une page
à l'autre (topbar + barre du jeu) est regroupé dans un nouveau conteneur
stable #pageChrome, que pjax.js remplace en bloc — plus fiable qu'un
sélecteur qui ne correspondait à rien. CSS (flex:0 0 auto des layouts
plein-écran) mis à jour en conséquence.

2) Renommages demandés : onglet/panneau "Écrans du jeu" -> "Écrants",
"Éléments de jeu" -> "Templates" (tab, titre de panneau, bouton
"+ Créer un template", état vide, infobulle, confirmation de
suppression).

3) Le crayon ✏️ sur une ligne d'objet ouvre désormais un panneau
déplaçable ET redimensionnable (nouveau coin de redimensionnement
générique, .floatPanelResizeHandle) au lieu de naviguer vers
object_edit.html (retirée) — un panneau par objet, pré-rendu et
caché par défaut. Reprend telles quelles les fonctionnalités de
l'ancienne page : renommer l'objet, ajouter un champ (ligne compacte),
modifier/supprimer un champ existant (tableau dense déjà repris pour
"Nouvel objet"), supprimer l'objet. Les routes de champs (object_field_
add/edit/delete) et object_edit lui-même redirigent maintenant vers le
tableau de bord avec ?edit=<id>, pour rouvrir automatiquement le bon
panneau après l'action plutôt que de le fermer silencieusement.

makeFloatPanelDraggable()/makeFloatPanelResizable() généralisées pour
être partagées entre "Nouvel objet" et les panneaux d'édition, plutôt
que du code dupliqué par panneau.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 11:56:49 +02:00
williamandClaude Sonnet 5 d875557254 Fusionne les 4 pages de création dans le dashboard, retire le titre/BDD
Suite de la demande précédente : les 4 pages autrefois listées dans la
barre de navigation n'existent plus en tant que pages séparées — tout
vit désormais dans les onglets du tableau de bord (commit précédent) ou,
pour la création d'un objet, dans un panneau déplaçable.

- screens_list.html + sa route (screens_list) : supprimés (l'onglet
  "Écrans du jeu" du dashboard couvre déjà tout : créer, réordonner,
  éditer, supprimer). screen_new/screen_move/screen_delete redirigent
  maintenant vers le dashboard (tab=screens) au lieu de cette page.
- element_types.html : supprimé, mais la route element_types est
  conservée (GET redirige vers le dashboard, POST — utilisé par la barre
  de création repliable de l'onglet "Éléments de jeu" — continue de
  fonctionner). element_type_edit/element_type_delete redirigent aussi
  vers le dashboard.
- game_variables.html + sa route (game_variables) : supprimés (l'onglet
  "Variables" du dashboard couvre déjà tout). create_global_var/
  global_var_edit/global_var_delete redirigent vers le dashboard
  (tab=variables) au lieu de cette page.
- object_form.html : supprimé. La route object_new (POST) est conservée
  pour traiter la soumission du panneau — voir plus bas — mais ne rend
  plus de page pour un GET (redirige vers le dashboard).

Nouveau panneau déplaçable "Nouvel objet" sur le dashboard (bouton
"+ Nouvel objet" de l'onglet Objets) : réutilise .floatPanel/
.floatPanelHeader/.floatPanelBody (déjà utilisées dans l'éditeur d'écran)
avec une nouvelle variante centrée (.floatPanel--center) et son propre
glisser-déposer minimal (pas de position persistée, contrairement aux
panneaux de l'éditeur d'écran — inutile pour un panneau ouvert
ponctuellement). Contenu et script (object_form.js) repris tels quels de
l'ancienne page.

base.html : la barre de navigation du jeu n'a donc plus que 2 liens —
"📊 Tableau de bord" (nouveau) et "▶️ Jouer" (toujours en dernier).

game_dashboard.html : titre du jeu et chemin de la base de données
retirés (redondants avec le nom déjà visible dans l'onglet du
navigateur/la barre de nav).

Les liens "crée-en un"/"gérer les variables" dans l'éditeur d'écran
(screen_edit.html) pointent maintenant vers le dashboard avec le bon
onglet (?tab=...), lu et appliqué au chargement de la page
(switchDashTab() côté JS).

2 tests (test_screens_and_elements.py) mis à jour : ils vérifiaient le
contenu des pages supprimées (element-types, screens) — adaptés pour
vérifier la même chose sur le dashboard, qui porte maintenant cette
information.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 10:54:19 +02:00
williamandClaude Sonnet 5 8f45169209 Retire le fil d'Ariane, centre/réordonne la barre de nav, refait le dashboard
Trois demandes distinctes de l'utilisateur, regroupées car elles touchent
toutes à la navigation d'un jeu :

1. Fil d'Ariane retiré (devenu redondant avec la barre de navigation du
   jeu ajoutée au commit précédent) : bloc breadcrumb_wrap retiré de
   base.html, et son override ({% block breadcrumb %}) retiré des 9
   templates qui le définissaient encore. .breadcrumbBar (CSS) retiré,
   y compris des règles flex:0 0 auto de body.objectEditBody/builderBody.

2. Liens de .gameNavBar centrés (justify-content:center).

3. "Jouer" déplacé en dernier lien (c'est une action à part — ouvre
   l'aperçu jouable dans un nouvel onglet — pas un éditeur de plus comme
   les 4 autres).

4. game_dashboard.html devient un vrai tableau de bord : une grille de
   cartes (Écrans/Objets/Éléments de jeu/Variables), chacune listant les
   entrées existantes avec un accès direct (clic = éditeur concerné) et
   un lien "Gérer" vers la page dédiée pour créer/réorganiser. Remplace
   l'ancien panneau "Créer" (redondant avec la barre de navigation
   persistante) et la simple table "Objets définis". routes/games/
   game_dashboard.py alimente maintenant aussi screen_list, element_types
   (+ usage) et variables, en réutilisant list_screens/
   list_element_types/element_type_usage_count/list_global_variables déjà
   utilisés ailleurs.

Vérifié en rendant toutes les pages concernées via le client de test
Flask : barre de nav présente partout où un `game` est dans le contexte
(absente sur l'accueil), fil d'Ariane absent partout, ordre des liens
avec "Jouer" en dernier.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 10:04:09 +02:00
williamandClaude Sonnet 5 de89115534 Ajoute une barre de navigation persistante entre les éditeurs d'un jeu
Jusqu'ici, les liens vers les différents éditeurs d'un jeu (Écrans,
Éléments de jeu, Variables, Jouer, Objet) n'existaient que dans le
panneau "Créer" du tableau de bord (game_dashboard.html) — changer
d'éditeur obligeait à revenir sur cette page à chaque fois.

base.html expose désormais une 2e barre (.gameNavBar), sous la barre
"Forge Engine" et au-dessus du fil d'Ariane, reprenant ces mêmes 5
liens — visible sur TOUTE page qui met un `game` dans le contexte du
template (déjà fait par chaque route pour le fil d'Ariane, donc aucun
changement de route nécessaire), absente sur l'accueil (liste des jeux,
pas de jeu courant). L'onglet correspondant à la section actuelle est
mis en évidence via un simple préfixe sur request.path.

static/style.css : nouvelle barre en ligne (contrairement à .navList/
.navRow, empilés verticalement dans le panneau "Créer" du tableau de
bord, réutilisés tels quels ailleurs). Ajoutée aux règles flex:0 0 auto
de body.objectEditBody/body.builderBody (mise en page plein-écran des
éditeurs) aux côtés de .topbar/.breadcrumbBar, sans quoi elle aurait
cassé la répartition de hauteur figée de ces pages.

Vérifié en rendant plusieurs routes via le client de test Flask : barre
absente sur l'accueil, présente partout ailleurs (tableau de bord, liste
des écrans, éditeur d'écran normal ET d'écran-modèle, éléments de jeu,
variables, nouvel objet).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 09:20:05 +02:00
william 25a331830b Reskin the whole admin UI (not just game widgets) with Bulma
Loads Bulma 1.0.2 in base.html (data-theme="dark" for its native dark palette) for every page that extends it, plus play.html directly. Converts every admin template — index, game dashboard, screens list, element types, object new/edit, data form/list — to real Bulma markup: navbar, breadcrumb component, box/card, field/control/input/select, table, notification, buttons, and a native Bulma modal for the data-row detail popup. Forge's own style.css keeps only what Bulma doesn't cover (the fixed-viewport builder/object-edit layouts, the element tree, canvas, flow-graph editor) and now acts as a secondary/override layer rather than a competing design system, matching how per-element inline customization already overrides widget defaults.

screen_edit.html (the 3-panel screen builder) gets the same navbar/breadcrumb/button treatment plus its top rename form, but its flow-graph node editor, canvas and element tree keep their existing custom styling — several of their inputs have JS relying on exact DOM sibling structure (e.g. slider oninput reading nextElementSibling) or Bulma's own select/wrapper requirement, and reskinning them without a browser to verify against risked silently breaking the app's most complex feature. Buttons and headings there are still fully converted (safe, purely additive class changes).

Verified: full test suite green, every route smoke-rendered 200 OK via the test client after the change.
2026-08-24 13:19:02 +02:00
williamandwilliam 3f4ebc4527 first commit
Build and deploy / deploy (push) Successful in 10s
Build and deploy / build-and-push (push) Successful in 17s
2026-08-21 16:23:49 +02:00