Commit Graph

19 Commits

Author SHA1 Message Date
Valentin ROBIN 00743067cc Ajoute un ping serveur régulier comme troisième couche d'anti-triche sur Icare et la Corne
Build and deploy / deploy (push) Successful in 36s
duration_ms/actions/merges restaient entièrement déclaratifs (calculés et envoyés par le client en une fois à la fin), donc falsifiables par un appel RPC direct. Le client envoie maintenant un ping toutes les 5s pendant la partie (ping_game_run), horodaté par le serveur, jamais par le client. submit_icarus_score/submit_melon_score corrèlent le nombre de pings et leur écart réel avec la durée annoncée (parties ≥ 15s uniquement) et signalent toute incohérence dans game_cheat_flags (missing_pings, ping_span_mismatch) — toujours en signalement silencieux, jamais de blocage, comme pour le reste de l'anti-triche.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 16:58:51 +02:00
Valentin ROBIN 088cd92e5f Resynchronise schema.sql avec l'anti-triche par signalement d'Alexandre
submit_icarus_score/submit_melon_score (appliquées directement en base, jamais commitées) sont récupérées mot pour mot via pg_get_functiondef : signalement dans game_cheat_flags plutôt que rejet serveur, le score reste toujours accepté. L'ancienne couche par rejet strict (icarus_runs/start_icarus_run) devenue orpheline est explicitement supprimée plutôt que laissée à traîner. CLAUDE.md documente le nouveau mécanisme (V12) et n'a plus aucune mention de l'ancien.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 16:42:26 +02:00
Valentin ROBIN 35704e50fc Resserre les plafonds de score, ajoute une vérif par temps réel écoulé sur Icare, corrige le contraste de l'Urne
Build and deploy / deploy (push) Successful in 36s
5000 restait trop haut pour être un vrai plafond réaliste : Icare abaissé à 500 (calculé sur le rythme le plus rapide théoriquement atteignable dans le jeu), Corne à 2000 (estimation plus prudente, économie de score plus dure à borner). Ajoute icarus_runs/start_icarus_run() : le serveur enregistre l'instant réel de début de partie et rejette un score incohérent avec le temps écoulé, sans jamais faire confiance à une durée envoyée par le client. Corrige aussi le nombre de jetons de /urne, peu lisible en gold-bright sur fond marbre (passe à text-sea, même convention qu'Icare/Corne).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 11:28:02 +02:00
Valentin ROBIN dc99af6bb3 Durcit la sécurité : plafond de score réaliste, REVOKE explicites, banque de questions du Char réservée aux juges
Build and deploy / deploy (push) Successful in 36s
Un score falsifié envoyé directement à submit_icarus_score/submit_melon_score pouvait se convertir en vraies gloires au Tribunal (plafond abaissé de 1 000 000 à 5000, contrainte + RPC). Les fonctions cron-only award_*_points_if_due() n'étaient protégées que par un commentaire, pas un vrai REVOKE. Le bucket avatars n'avait aucune limite de taille/type. La banque complète des questions du Char (spoiler du jeu) était lisible par tout Citoyen via /char ou un appel direct — RLS restreinte aux juges, /char passe désormais par la RPC chariot_revealed_question().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 04:41:39 +02:00
Valentin ROBIN 3b64e1cdb5 Ajoute La Corne d'Abondance, jeu de fusion façon Watermelon Game
Build and deploy / deploy (push) Successful in 35s
Nouveau mini-jeu à /corne : des avatars de Citoyens tombent dans un bac
(physique matter-js), deux avatars de même taille qui se touchent
fusionnent en un plus gros, jusqu'au débordement soutenu (5s de grâce,
pièces clignotantes pour prévenir) qui met fin à la partie. Même
mécanique de gloires que Le Vol d'Icare (record personnel, classement,
gel + attribution automatique au Tribunal via pg_cron).

Un palier de taille par Citoyen (pas les Archontes) plutôt qu'un nombre
arbitraire : citizens[tier] donne l'association palier → Citoyen, fixe
pour tout le monde (les Citoyens sont triés par pseudo côté serveur, un
ordre déterministe). Rendu en DOM pour réutiliser le composant Avatar
existant (étendu pour accepter une taille en pixels arbitraire) plutôt
que de redessiner des images sur canvas — position et rotation de chaque
pièce appliquées en impératif depuis la boucle physique, jamais via
setState.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 00:52:07 +02:00
Valentin ROBIN 47ca05f1c6 Ajoute images et réactions sur les notes du Mur de la Honte
Build and deploy / deploy (push) Successful in 36s
Chaque note peut désormais porter une image facultative : compressée et
redimensionnée côté client (resizeImageToBlob, sans forcer un carré comme
pour les avatars) avant l'upload, avec une limite dure côté bucket
Supabase (3 Mo, webp/jpeg/png uniquement) en filet de sécurité. Le chemin
de stockage ne contient jamais le user_id, contrairement aux avatars,
pour ne pas faire fuiter l'auteur via l'URL publique de l'image.

Ajoute aussi des réactions emoji (👍😂😱❤️🔥) sur chaque note, avec le
même principe d'anonymat que le reste du Mur : le total par emoji est
public (wall_note_reaction_counts), mais personne ne voit qui a réagi.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 23:01:40 +02:00
Valentin ROBIN e8a9a0ddb9 Ajoute des justifications anonymes aux votes de L'Urne de l'Agora
Build and deploy / deploy (push) Successful in 35s
Un Citoyen peut désormais laisser un motif facultatif en déposant son
jeton (toujours deux étapes avant l'envoi). Le classement permet de
consulter les justifications reçues par une personne via une nouvelle RPC
urn_vote_reasons() — même principe que wall_notes_today : le contenu est
public, l'auteur ne l'est jamais, ce qui préserve la garantie de L'Urne
que personne (Archontes compris) ne voit qui a voté pour qui.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 22:48:25 +02:00
Valentin ROBIN 86756c41da Corrige les comptes invités qui restaient bloqués sans mot de passe
Le pseudo temporaire fixe "Nouveau membre" entrait en collision (contrainte
unique) dès qu'une 2e personne était invitée avant que la 1re ait fini son
inscription ; il inclut maintenant un suffixe d'uuid. Le token d'invitation
pouvait aussi s'établir sur n'importe quelle page (pas seulement /signup),
laissant certains comptes connectés sans jamais voir le formulaire de mot
de passe — /signup affiche désormais un état de chargement au lieu d'un
écran vide, et le proxy renvoie systématiquement vers /signup tant que le
compte n'est pas finalisé.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 22:12:31 +02:00
Valentin ROBIN d02f1543e9 Ajoute L'Urne de l'Agora
Build and deploy / deploy (push) Successful in 36s
Nouvelle page /urne : vote quotidien inspiré du vote à l'urne de
l'Athènes antique. Chaque Citoyen dépose un jeton par jour sur une
autre personne (jamais lui-même), avec confirmation à deux temps
avant l'envoi — le vote est définitif pour la journée, aucune RPC de
modification. Les jetons s'accumulent sur toute la semaine dans un
classement public et cumulatif, identique pour tout le monde y
compris les Archontes : contraste volontaire avec Le Mur de la Honte,
ici personne (Archontes compris) ne voit qui a voté pour qui, seul le
total par personne est public.

Nouvelle table urn_votes (append-only, comme points_log) + deux RPC :
urn_vote_counts() agrège les votes sans jamais exposer une ligne
individuelle, cast_urn_vote() applique les règles (pas de vote pour
un juge ni pour soi-même, un vote par jour). Les Archontes ne votent
pas et ne peuvent pas recevoir de jetons.
2026-08-23 18:02:12 +02:00
Valentin ROBIN ab599d55c9 Ajoute Le Mur de la Honte
Build and deploy / deploy (push) Successful in 35s
Nouvelle page /mur : chaque Citoyen peut publier une note anonyme par
jour (dix places max sur le mur, remis à zéro chaque jour — filtrage
par date, jamais de suppression). L'anonymat n'est qu'à moitié réel :
les Archontes voient une section "Historique complet" (filtrable par
jour) avec l'auteur de chaque note, cohérent avec le thème du site où
les Archontes ont un ascendant sur les Citoyens.

Nouvelle table wall_notes + deux RPC : wall_notes_today() ne renvoie
jamais l'auteur (seul moyen de garantir l'anonymat côté serveur, la
RLS ne pouvant pas masquer une colonne pour certaines lignes) et
post_wall_note() applique les règles (un juge ne publie pas, une note
par jour, mur plafonné à 10). Pas de gel lié à la date du Tribunal :
contrairement au Char, c'est une mécanique de toute la semaine.
2026-08-23 17:14:12 +02:00
Valentin ROBIN ec6b1920af Ajoute l'historique des versions des questions soumises par les Citoyens
Build and deploy / deploy (push) Successful in 35s
Nouvelle table chariot_submission_history (append-only, comme
points_log) : chariot_submissions ne garde que la valeur actuelle
(upsert), donc sans ce journal les Archontes ne voyaient jamais les
versions précédentes d'une question modifiée plusieurs fois.
submit_chariot_question() y écrit désormais à chaque changement réel.

Sur /char/questions, chaque suggestion affiche un lien "Historique (N)"
(visible seulement si la personne a déjà modifié sa proposition) qui
déplie les versions précédentes avec leur date.
2026-08-22 00:16:42 +02:00
Valentin ROBIN 76fa8cd451 Ajoute les suggestions de questions des Citoyens et les emails sur Admin
Build and deploy / deploy (push) Successful in 35s
Chaque Citoyen peut proposer une question pour Le Char depuis /profile
(upsert via submit_chariot_question, figée dès la date du Tribunal
comme les scores d'Icare). Les Archontes modèrent ces suggestions
directement sur /char/questions (ajouter à la banque ou rejeter).

/admin affiche maintenant l'email de chaque membre via une nouvelle
RPC admin_list_members(), seule façon d'exposer auth.users.email sans
passer par la service_role key côté client.

Corrige au passage deux bugs découverts pendant les tests : la
policy interne de admin_list_members() référençait id/role sans les
qualifier, ambigus avec les colonnes du RETURNS TABLE (erreur
Postgres 42702) ; et l'auteur d'une suggestion s'affichait toujours
comme "un Citoyen" car le embed profiles(...) avait été mal retypé en
tableau alors qu'il est retourné en objet à l'exécution.
2026-08-19 01:22:36 +02:00
Valentin ROBIN f30f77a394 Ajoute Le Jeu de l'Agora : Le Char et Le Gardien du Silence
Build and deploy / deploy (push) Successful in 36s
Page /char (16:9, projetée) pour piloter les deux jeux physiques du
Tribunal : Le Char (2 colonnes de 3 emplacements côte à côte avec un
VS et une illustration de char vu du dessus, sélection en un clic,
duels, compteur et classement des passages) et Le Gardien du Silence
(tirage aléatoire parmi les Citoyens, minuteur avec reroll auto et
manuel). Banque de questions gérée séparément sur /char/questions
(pensée pour être pilotée depuis un téléphone pendant que /char est
projetée depuis un ordinateur).
2026-08-11 18:17:45 +02:00
Valentin ROBIN 33d2476ef6 Remplace la Course du Char par Le Vol d'Icare
Build and deploy / deploy (push) Successful in 37s
Après retour d'expérience, la course de char (vue du dessus, piste
générée par jour) laisse place à un Flappy Bird grec plus simple et
plus lisible sur téléphone : Icare vole entre des colonnes de temple,
touche l'écran pour battre des ailes, échec net au premier contact
(score remis à zéro).

Record personnel all-time (plus de piste quotidienne ni de fantômes) :
les scores se figent dès que la date du Tribunal est atteinte, puis les
gloires du top 3 sont attribuées automatiquement via une tâche
planifiée pg_cron, comme pour l'ancienne Course du Char.

schema.sql nettoie explicitement l'ancienne Course du Char (tables,
RPC, tâche planifiée) avant de poser le nouveau schéma, puisqu'elle
avait déjà été appliquée en prod.
2026-07-29 19:14:52 +02:00
Valentin ROBIN 777d9ebaea Ajoute le schéma de La Course du Char (tables, RPC, clôture auto)
- chariot_runs : meilleur temps + tracé fantôme par joueur et par jour,
  verrouillée comme points_log (écriture uniquement via RPC).
- submit_chariot_run() : calcule la date côté serveur, borne le temps,
  n'écrase que si strictement meilleur.
- close_daily_chariot_race() + chariot_race_closes : clôture la journée
  précédente et attribue les gloires du top 3 automatiquement, sans
  intervention d'un Archonte — planifiée via pg_cron (à activer une
  fois côté Dashboard Supabase).
- points_log.judge_id devient nullable (un point auto n'a pas de juge) ;
  Le Crieur affiche "Le Tribunal" pour ces décrets-là.
2026-07-27 18:53:45 +02:00
Valentin ROBIN 5cb19caec2 Boutons de points compacts unifiés + page Le Calendrier des Dieux
Boutons de validation des points :
- Les icônes ✓/✕ étaient trop grandes (40px) et dupliquées entre le
  podium et le reste du classement. Factorisées dans un composant
  partagé PointsConfirmControls (32px, icône 16px, olive/oxblood,
  aria-label + tooltip), utilisé à l'identique par JudgePointControls
  sur le podium ET dans la liste.

Nouvelle page /calendrier — "Le Calendrier des Dieux" :
- Modèle de données : days (date, dieu, domaine), events (titre, type,
  heure, lieu, created_by forcé par trigger), settings (ligne unique,
  date du Tribunal). RLS : lecture ouverte à tout authentifié, écriture
  (insert/update/delete) réservée au rôle judge par policies dédiées —
  jamais un simple masquage des boutons côté client.
- 8 préréglages de divinités (Dionysos, Arès, Athéna, Aphrodite, Hermès,
  Poséidon, Hadès, Zeus) avec domaine, couleur d'accent et emblème SVG ;
  un Archonte peut aussi saisir une divinité libre.
- Bannière avec compte à rebours avant la date du Tribunal, éditable par
  les Archontes. Journées en cartes (jour courant mis en évidence,
  jours passés estompés, jour du Tribunal en accent oxblood), liste
  d'événements avec icône par type (activité/défi/épreuve/tribunal).
  Mode édition (ajout/modification/suppression avec confirmation)
  réservé aux Archontes. Realtime sur les trois tables.
- Ajouté au menu déroulant du header, route protégée par le proxy.

Rendu des icônes dynamiques (GodEmblem, EventTypeIcon) via branchement
JSX explicite plutôt que variable de composant résolue à l'exécution,
pour respecter react-hooks/static-components.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 21:13:53 +02:00
Valentin ROBIN e6f4463cb0 Design responsive, logo, et correctif RLS upload photo
Design & UX :
- Renomme "Administration" en "Admin" (nav, page, docs).
- Logo original (marteau de juge stylisé navy/or) en favicon (src/app/icon.svg,
  remplace le favicon.ico par défaut de create-next-app) et dans la navbar
  (components/logo.tsx).
- Navbar : menu hamburger sur mobile (les liens débordaient à partir de
  ~4 entrées sur petit écran).
- Leaderboard : lignes en deux niveaux sur mobile (infos membre / contrôles
  juges) au lieu d'un seul flex-wrap qui devenait illisible.
- Contrôles de points juges (JudgePointControls) : boutons et champ montant
  réduits en mode compact pour tenir dans les colonnes du podium sur petit
  écran.
- Podium : scroll horizontal de secours (overflow-x-auto) si le contenu
  dépasse malgré tout sur très petits écrans.
- Table admin : pseudo/avatar sur une ligne, rôle/statut/actions sur une
  autre en mobile (le flex-1 sur l'input pseudo n'avait aucun effet en
  layout colonne).

Correctif sécurité/fonctionnel :
- storage.objects n'avait que des policies INSERT et UPDATE pour le bucket
  avatars, pas de policy SELECT. Postgres a besoin de lire la ligne
  existante pour évaluer la clause USING d'un UPDATE : sans SELECT, tout
  remplacement de photo (upsert) échouait avec "new row violates row-level
  security policy", même si les policies INSERT/UPDATE étaient correctes.
  Ajout d'une policy SELECT publique sur le bucket avatars (cohérent avec
  le fait que le bucket est déjà public en lecture).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 16:47:43 +02:00
Valentin ROBIN abbd079cd2 V2 + V3 : rôles, points, journal, podium et recadrage photo
V2 — authentification et administration :
- Authentification par email + mot de passe (email confirmé, un compte
  par email), abandon de l'email interne dérivé du pseudo.
- Rôles public/judge sur profiles, section Administration (juges
  uniquement, vérifiée côté serveur) pour gérer les membres.
- Verrou de pseudo : modifiable une fois par son propriétaire puis figé,
  contournable par un juge.
- RLS étendue par un trigger BEFORE UPDATE (enforce_profile_update) pour
  verrouiller les colonnes sensibles (role, points, pseudo_locked, pseudo
  figé) — la RLS seule ne peut pas exprimer une règle par colonne.

V3 — points, journal, podium, progression, photo :
- RPC award_points() (SECURITY DEFINER) : seul point d'écriture de la
  colonne points, vérifie le rôle juge côté serveur, delta signé sans
  plancher à 0, trace chaque opération dans points_log.
- Leaderboard temps réel (Supabase Realtime) avec podium top 3
  (égalités gérées), flèches de progression (previous_rank), et
  contrôles de points juges avec confirmation explicite (plus de
  debounce auto) avant envoi.
- Page /journal ("le crieur") : fil live et public des attributions de
  points.
- Recadrage photo carré + compression client (react-easy-crop + canvas)
  au signup et sur le profil.
- Passe de polish visuel : cartes/boutons cohérents, lien actif dans la
  nav, podium retravaillé.

schema.sql, README.md et CLAUDE.md mis à jour en conséquence (schéma
idempotent, instructions SMTP/rôles/migration, conventions RLS+trigger
documentées pour les futures colonnes sensibles).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 16:14:10 +02:00
Valentin ROBIN 87cd286e5a Scaffold V1: auth, profils et leaderboard
Next.js 16 + Tailwind + Supabase (Auth, Postgres, Storage). Connexion
par pseudo/mot de passe via email interne dérivé, RLS avec points
protégés au niveau colonne, pages login/signup/leaderboard/profile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:52:18 +02:00