From 5e75939b94a1ad9842db1da5137963d4e62b687e Mon Sep 17 00:00:00 2001 From: Valentin ROBIN Date: Tue, 25 Aug 2026 17:30:46 +0200 Subject: [PATCH] =?UTF-8?q?Ouvre=20Le=20Mur=20de=20la=20Honte=20aux=20Arch?= =?UTF-8?q?ontes,=20plafond=20quotidien=20relev=C3=A9=20=C3=A0=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit post_wall_note() n'a plus de vérification de rôle : un Archonte peut désormais publier une note anonyme comme n'importe quel Citoyen. La policy Storage d'upload sur wall-images perd aussi son filtre par rôle (sinon possible de poster une note mais pas d'y joindre une image). Limite quotidienne relevée de 10 à 12 notes pour couvrir tout le groupe plutôt que les seuls Citoyens. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 8 +- src/app/mur/mur-view.tsx | 158 ++++++++++++++++++++------------------- src/app/mur/page.tsx | 19 +++-- supabase/schema.sql | 35 +++++---- 4 files changed, 113 insertions(+), 107 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a68c737..5ec8ea6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ Application web pour un groupe d'amis (~12 personnes) qui anime un jeu et des ol ## 1. État actuel -V1 à V13 sont livrées : +V1 à V14 sont livrées : - **V1** : comptes, profils, leaderboard statique. - **V2** : authentification par email réel (plus de pseudo+email interne), rôles `public`/`judge`, verrou de pseudo (modifiable une fois), section Admin. - **V3** : attribution de points par les juges (RPC sécurisée, +/- avec confirmation explicite), journal public « le crieur », podium top 3, flèches de progression, recadrage/compression photo côté client. @@ -23,6 +23,8 @@ V1 à V13 sont livrées : - **V13** : troisième couche de l'anti-triche des mini-jeux (à la demande explicite de l'utilisateur, après qu'un premier signalement réel via V12 a confirmé que le mécanisme fonctionnait) — un ping régulier pendant la partie, en plus du signalement fondé sur la télémétrie déclarative de V12. `public.game_run_pings` (`id, user_id, game, session_token, created_at`) journalise chaque ping, horodaté par le **serveur** (jamais par le client, contrairement à `duration_ms`/`actions`/`merges` qui restent déclaratifs) ; `ping_game_run(p_game, p_session_token)` l'écrit. Côté client, `IcarusGame`/`MelonGame` retrouvent un prop `onStart` (le composant de jeu reste agnostique de Supabase, comme avant — c'est `IcareView`/`CorneView` qui gèrent le token/l'intervalle) déclenché au premier battement d'aile pour Icare, au montage et à chaque `resetGame()` pour Corne ; `IcareView`/`CorneView` y génèrent un `session_token` (`crypto.randomUUID()`), lancent un `setInterval` de 5s qui appelle `ping_game_run()` (best-effort, erreurs ignorées), l'arrêtent à la fin de la partie ou au démontage du composant, et l'incluent dans `p_run.session_token` envoyé à `submit_icarus_score`/`submit_melon_score`. Ces deux RPC corrèlent alors le nombre de pings et l'écart réel entre le premier et le dernier avec `duration_ms` annoncé (uniquement si celui-ci dépasse 15s, pour ne jamais signaler une partie courte/un échec immédiat) et ajoutent deux nouveaux `trigger_code` à `game_cheat_flags` : `missing_pings` (zéro ping reçu) et `ping_span_mismatch` (écart réel entre pings très inférieur à la durée annoncée) — toujours en signalement, jamais en blocage, même philosophie que V12. Décision produit assumée (l'utilisateur l'a explicitement refusée pour V12 aussi) : les signalements restent **volontairement invisibles** pour la personne concernée — le but est de savoir qui triche sans le prévenir, y compris si un score signalé finit par recevoir de vraies gloires automatiquement au Tribunal. +- **V14** : Le Mur de la Honte (V9) s'ouvre aux Archontes — ils peuvent désormais publier une note anonyme comme n'importe quel Citoyen (`post_wall_note` n'a plus de vérification de rôle), et la limite quotidienne passe de 10 à 12 notes pour couvrir tout le groupe plutôt que les seuls Citoyens. La policy Storage d'upload sur `wall-images` perd elle aussi son filtre par rôle (sinon un Archonte aurait pu poster une note mais pas y joindre d'image). Contraste assumé avec V10 (l'Urne, où les Archontes ne votent ni ne reçoivent de jetons) : ici rien ne les distingue plus des Citoyens côté publication — ils gardent seulement, comme avant, leur visibilité privilégiée sur l'auteur de chaque note via « Historique complet ». + Hors périmètre pour l'instant (voir roadmap en fin de doc) : éditions/saisons, mode grand écran. --- @@ -139,7 +141,7 @@ Deuxième volet de l'audit (revue complète de tout le code applicatif, pas seul - `chariot_revealed_question()` — authenticated, `language sql`, seul moyen pour un non-juge de connaître la question actuellement révélée sans jamais lire `chariot_questions` (RLS juges uniquement, voir §3bis) — renvoie zéro ligne tant qu'aucune question n'est révélée, et calcule `question_number` (position dans l'ordre complet) côté serveur pour afficher « Question N » sur `/char` sans exposer les autres lignes. - `admin_list_members()` — authenticated, vérifie `role = 'judge'`, seule façon d'exposer `auth.users.email` (jamais stocké dans `profiles`) sur `/admin` sans passer par la `service_role key` côté client. - `wall_notes_today()` — authenticated, `language sql`, renvoie les notes du jour (heure de Paris), y compris `image_url`, **sans `user_id`** — seul moyen de laisser un Citoyen voir les notes des autres sans jamais exposer l'auteur côté client (la RLS ne peut pas masquer une colonne pour certaines lignes). -- `post_wall_note(p_text, p_image_url default null)` — authenticated, rejette les juges (seuls les Citoyens publient), valide un texte non vide (≤ 200 caractères), puis vérifie que l'appelant n'a pas déjà publié aujourd'hui et que le mur du jour a moins de 10 notes (sinon `raise exception` dans les deux cas — pas de no-op silencieux ici, l'UI doit remonter l'erreur). `p_image_url` est facultatif et doit venir du bucket `wall-images` (vérifié par un `like`). +- `post_wall_note(p_text, p_image_url default null)` — authenticated, ouvert à tout le monde y compris les juges depuis V14 (plus de vérification de rôle), valide un texte non vide (≤ 200 caractères), puis vérifie que l'appelant n'a pas déjà publié aujourd'hui et que le mur du jour a moins de 12 notes (sinon `raise exception` dans les deux cas — pas de no-op silencieux ici, l'UI doit remonter l'erreur). `p_image_url` est facultatif et doit venir du bucket `wall-images` (vérifié par un `like`). - `wall_note_reaction_counts()` — authenticated, `language sql`, renvoie `(note_id, emoji, count)` agrégé sur les notes du jour — même principe que `urn_vote_counts` : jamais qui a réagi, seulement le total. - `toggle_wall_note_reaction(p_note_id, p_emoji)` — authenticated, bascule une réaction (ajoute si absente, retire si déjà posée en un `delete` puis, si rien n'a été supprimé, un `insert`). `p_emoji` limité à une liste fermée (contrainte `check` sur la colonne, revérifiée dans la RPC pour un message d'erreur clair). - `urn_vote_counts()` — authenticated, `language sql`, renvoie `(target_id, votes)` agrégé sur tout l'historique (pas de filtre de date, contrairement à `wall_notes_today`) — seul moyen de calculer un total public sans jamais exposer une ligne individuelle (qui a voté pour qui). @@ -179,7 +181,7 @@ Deuxième volet de l'audit (revue complète de tout le code applicatif, pas seul | `/corne` | « La Corne d'Abondance » — jeu de fusion façon Watermelon Game (avatars aléatoires, physique `matter-js`), même mécanique de gloires que Le Vol d'Icare | | `/char` | « Le Jeu de l'Agora » — format paysage (projeté) : Le Char (2 colonnes de 3, boutons de duel), Le Gardien du Silence (tirage aléatoire, minuteur discret) et La Roulette (tirage au sort animé) | | `/char/questions` | Banque de questions du Char (ajout/édition/réordonnancement/afficher-masquer) + modération des suggestions des Citoyens (ajouter à la banque/rejeter) — juges uniquement, pilotée depuis un téléphone pendant que `/char` est projetée | -| `/mur` | « Le Mur de la Honte » — une note anonyme par Citoyen par jour (dix places, remis à zéro chaque jour) ; les Archontes y voient en plus l'historique complet avec l'auteur de chaque note | +| `/mur` | « Le Mur de la Honte » — une note anonyme par personne par jour, Archontes compris depuis V14 (douze places, remis à zéro chaque jour) ; les Archontes y voient en plus l'historique complet avec l'auteur de chaque note | | `/urne` | « L'Urne de l'Agora » — un jeton par Citoyen par jour sur une autre personne, classement cumulatif public identique pour tous, Archontes compris (aucune vision privilégiée ici) | | `/calendrier` | « Le Calendrier des Dieux » — agenda de la semaine, panthéon, date du Tribunal, édition réservée aux juges. Retirée du menu de navigation (la date du Tribunal, seule info largement utile au quotidien, est reprise sur `/leaderboard`) mais la route reste fonctionnelle — les juges y accèdent directement par l'URL pour gérer le calendrier et changer la date | | `/journal` | « Le Crieur » — fil live des décrets (attributions de points), lecture pour tous | diff --git a/src/app/mur/mur-view.tsx b/src/app/mur/mur-view.tsx index 63c8e59..786123f 100644 --- a/src/app/mur/mur-view.tsx +++ b/src/app/mur/mur-view.tsx @@ -9,7 +9,11 @@ import { createClient, waitForRealtimeAuth } from "@/lib/supabase/client"; import type { MyReactionRow, ReactionCountRow, WallHistoryRow, WallNoteRow } from "./page"; const MAX_LENGTH = 200; -const MAX_NOTES_PER_DAY = 10; +// Les Archontes peuvent désormais publier aussi (plus réservé aux +// Citoyens) : 12 places pour couvrir tout le groupe, pas seulement les +// Citoyens — doit rester aligné avec la borne côté serveur dans +// post_wall_note (supabase/schema.sql). +const MAX_NOTES_PER_DAY = 12; const MAX_IMAGE_SOURCE_BYTES = 20 * 1024 * 1024; const REACTION_EMOJIS = ["👍", "😂", "😱", "❤️", "🔥"]; @@ -362,85 +366,83 @@ export function MurView({ )} - {!isJudge && ( -
- {myNoteToday ? ( -
-

Ta note d'aujourd'hui est publiée.

- {myNoteToday.image_url && ( - - )} -

- {myNoteToday.text} -

-

Reviens demain pour en publier une nouvelle.

-
- ) : wallFull ? ( -

Le mur est complet pour aujourd'hui, reviens demain !

- ) : ( -
-