Initialisation du projet photobooth-qr

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014akYEuf44fM6EiUvrmoBzS
This commit is contained in:
2026-09-15 21:44:00 +02:00
co-authored by Claude Sonnet 5
commit 2007eab006
9 changed files with 348 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# Copier ce fichier en .env et remplir avec les vraies valeurs.
# Ne jamais commiter le fichier .env réel.
# URL du projet Supabase
SUPABASE_URL=
# Clé publique (anon) Supabase — utilisable côté client
SUPABASE_ANON_KEY=
# Clé service_role Supabase — usage backend/admin UNIQUEMENT,
# jamais exposée côté client
SUPABASE_SERVICE_ROLE_KEY=
# Nom du bucket Supabase Storage utilisé pour les photos
SUPABASE_STORAGE_BUCKET=photos
# URL de base de l'application (utilisée pour générer les liens QR code)
APP_BASE_URL=
+33
View File
@@ -0,0 +1,33 @@
# Dépendances
node_modules/
# Variables d'environnement
.env
.env.local
.env.*.local
# Build
dist/
build/
.cache/
.parcel-cache/
# Supabase (local)
supabase/.branches/
supabase/.temp/
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS / éditeurs
.DS_Store
Thumbs.db
.vscode/
.idea/
# Divers
*.zip
*.tmp
+65
View File
@@ -0,0 +1,65 @@
# CLAUDE.md — photobooth-qr
## Contexte
Application web de galerie photo événementielle par QR code.
Principe : les invités d'un événement (mariage, soirée, etc.) scannent un QR
code, arrivent sur une page d'upload, et déposent leurs photos/vidéos qui
atterrissent dans une galerie consultable par l'organisateur/admin. À la fin,
l'admin peut exporter toutes les photos en ZIP.
Développé par **Kévin Lecou**, auto-entrepreneur, mixologue (projet
**Rêves Liquides**). Ce projet est distinct de Rêves Liquides mais peut lui
servir d'outil.
- **V1** : usage personnel de Kévin (ses propres événements / tests).
- **V2 / V3** : vente du service à des clients externes (mariages,
événementiel professionnel). Le projet doit donc être pensé dès le début
pour être **multi-clients et réutilisable**, sans pour autant sur-ingénierer
la V1.
## Stack imposée
- **Supabase** pour la base de données (Postgres) et le stockage (Storage).
- **Front léger, mobile-first, sans framework lourd** (pas de React/Vue/Next
sauf décision contraire explicite plus tard). HTML/CSS/JS vanilla ou
micro-lib, en priorité.
- **Pas d'application native.**
- **Pas de compte invité** : les invités uploadent sans créer de compte,
sans authentification.
## Contraintes non négociables
1. **Écoconception**
- Compression image **côté client obligatoire** avant upload (jamais
envoyer une photo brute non compressée vers Supabase Storage).
- Minimiser le nombre de requêtes réseau et le poids des pages.
- Toujours proposer, par défaut, le choix le plus sobre en dépendances
et en requêtes réseau — pas de librairie ajoutée "par confort" si une
solution native (fetch, Canvas API, etc.) suffit.
2. **RGPD**
- Mention légale obligatoire sur la page d'upload (voir `docs/rgpd.md`).
- Suppression automatique des données (photos + métadonnées) à échéance
fixée (voir `docs/decisions.md` pour la règle exacte : J+7 après la
date de l'événement, indépendamment du téléchargement du ZIP).
3. **Pas de dette technique inutile**
- Le projet est pensé pour durer et être réutilisé sur plusieurs clients
(multi-tenant logique via la table `events`).
- Éviter les raccourcis qui compliqueraient le passage V1 → V2/V3
(ex : éviter de hardcoder des valeurs propres à un seul événement).
- Mais ne pas sur-architecturer non plus : pas de fonctionnalité pour un
besoin hypothétique non encore confirmé par Kévin.
## Mode de collaboration
- **Avant toute décision structurante** (schéma de base de données,
mécanisme d'authentification/admin, choix d'hébergement, changement de
stack), **demander confirmation à Kévin** avant de trancher seul et
d'implémenter. Proposer les options avec leurs compromis, pas juste une
question ouverte.
- Toute décision actée est à consigner dans `docs/decisions.md`.
- Ne pas inventer de credentials, URLs, ou valeurs de config manquantes —
demander si besoin (cf. règle globale dans `~/.claude/CLAUDE.md`).
+46
View File
@@ -0,0 +1,46 @@
# photobooth-qr
Galerie photo événementielle par QR code : les invités d'un événement
scannent un QR code, uploadent leurs photos depuis leur téléphone (sans
compte, sans app), et l'organisateur récupère toutes les photos dans une
galerie admin exportable en ZIP.
## Objectif
- **V1** : usage personnel (test grandeur nature sur mes propres événements).
- **V2 / V3** : produit vendu à des clients (mariages, événementiel).
## Stack
- **Supabase** — base de données Postgres + Storage.
- **Front** — HTML/CSS/JS léger, mobile-first, sans framework lourd.
- Pas d'app native, pas de compte invité.
## Documentation
- [`docs/cahier-des-charges.md`](docs/cahier-des-charges.md) — spécifications fonctionnelles.
- [`docs/architecture.md`](docs/architecture.md) — schéma de flux général.
- [`docs/modele-donnees.md`](docs/modele-donnees.md) — schéma des tables Supabase.
- [`docs/rgpd.md`](docs/rgpd.md) — mention légale et politique de conservation.
- [`docs/decisions.md`](docs/decisions.md) — journal des décisions actées.
## Lancer en local
_À compléter au fur et à mesure du développement._
```bash
# 1. Copier les variables d'environnement
cp .env.example .env
# puis remplir .env avec les vraies valeurs Supabase
# 2. Installer les dépendances (si un tooling est ajouté)
# npm install
# 3. Lancer le front en local
# (à définir : serveur statique simple, ex. npx serve src/)
```
## Statut
Projet en phase de cadrage — structure et documentation en place,
développement pas encore commencé.
+55
View File
@@ -0,0 +1,55 @@
# Architecture
## Vue d'ensemble
Le système repose sur un flux simple, sans compte invité, avec Supabase
comme unique backend (base de données + stockage de fichiers).
1. Un événement est créé dans la table `events` avec un `slug` unique (utilisé
dans l'URL publique) et un `admin_token` (utilisé pour l'accès admin).
2. Un QR code pointe vers l'URL publique d'upload de l'événement
(`/e/<slug>`).
3. Un invité scanne le QR code, arrive sur la page d'upload, compresse ses
photos côté client, puis les envoie vers Supabase Storage. Une ligne est
créée dans la table `photos` pour chaque fichier.
4. L'organisateur (admin) accède à la galerie via une URL contenant son
`admin_token` (`/admin/<slug>?token=...`), consulte les photos, et peut
déclencher un export ZIP de l'ensemble des photos de l'événement.
5. À J+7 après la **date de l'événement** (pas la date de création), les
photos et les métadonnées associées sont automatiquement supprimées,
que le ZIP ait été téléchargé ou non (voir `rgpd.md` et `decisions.md`).
## Schéma de flux
```mermaid
flowchart TD
A[Invité scanne le QR code] --> B[Page d'upload publique /e/slug]
B --> C[Compression image côté client]
C --> D[(Supabase Storage\nbucket photos)]
C --> E[(Table photos\nSupabase DB)]
D --> F[Galerie admin /admin/slug?token=...]
E --> F
F --> G[Export ZIP de toutes les photos]
H[Date événement + 7 jours] --> I[Suppression automatique\nphotos + storage]
E -.expire_at atteint.-> I
D -.expire_at atteint.-> I
```
## Composants
| Composant | Rôle | Techno |
|---|---|---|
| Page upload (publique) | Formulaire de dépôt photo, sans auth | HTML/CSS/JS léger |
| Compression image | Réduction de poids avant envoi | Canvas API / lib légère côté client |
| Supabase Storage | Stockage des fichiers photo | Supabase Storage (bucket `photos`) |
| Supabase DB | Métadonnées événements et photos | Supabase Postgres |
| Page galerie admin | Consultation + export ZIP | HTML/CSS/JS léger |
| Job d'expiration | Suppression auto à J+7 | Fonction planifiée (Supabase Edge Function / cron), à définir |
## Points à trancher avec Kévin avant implémentation
- Mécanisme exact du job d'expiration (cron Supabase, Edge Function planifiée,
ou script sur le VPS existant).
- Génération et hébergement du QR code (généré à la volée ou stocké).
- Bibliothèque de compression image côté client (ou implémentation maison via
Canvas API pour rester sobre en dépendances).
+1
View File
@@ -0,0 +1 @@
# Cahier des charges
+19
View File
@@ -0,0 +1,19 @@
# Journal des décisions
Ce fichier consigne les décisions structurantes actées pour le projet, avec
la date à laquelle elles ont été prises. Toute nouvelle décision structurante
(schéma DB, auth, hébergement, etc.) doit être ajoutée ici après validation
par Kévin.
## 2026-09-15
- **Expiration des données** : suppression à **J+7 à partir de la date de
l'événement** (`date_evenement`), et non à partir de la date de création
de l'événement dans le système.
- **Sécurité admin (V1)** : accès à la galerie admin via un **token secret
dans l'URL** (`admin_token`), pas de système de login/mot de passe. Réévaluer
ce choix avant V2/V3 si le nombre de clients augmente.
- **Suppression des données** : effective à J+7 **dans tous les cas**, y
compris si l'organisateur a déjà téléchargé le ZIP d'export des photos.
- **Hébergement** : utilisation du **VPS existant de Kévin**
(148.230.114.175), pas de nouvel hébergement dédié pour l'instant.
+64
View File
@@ -0,0 +1,64 @@
# Modèle de données
Deux tables Supabase (Postgres). Pas de table `users` côté invité : les
invités n'ont pas de compte. L'admin n'a pas de compte non plus en V1 — son
accès repose sur un token secret (voir `decisions.md`).
## Table `events`
| Colonne | Type | Description |
|---|---|---|
| `id` | `uuid` (PK, défaut `gen_random_uuid()`) | Identifiant unique de l'événement |
| `slug` | `text`, unique | Identifiant lisible utilisé dans l'URL publique d'upload (`/e/<slug>`) |
| `nom` | `text` | Nom de l'événement (ex : "Mariage Julie & Marc") |
| `date_evenement` | `date` | Date de l'événement, sert de référence pour l'expiration |
| `created_at` | `timestamptz`, défaut `now()` | Date de création de l'événement dans le système |
| `expire_at` | `timestamptz` | Date/heure d'expiration calculée (`date_evenement` + 7 jours) |
| `statut` | `text` | Statut de l'événement : `actif`, `expire`, `archive` (à affiner) |
| `admin_token` | `text`, unique | Token secret donnant accès à la galerie admin |
| `client_nom` | `text`, nullable | Nom du client (utile en V2/V3 quand le service est vendu) |
| `facture_montant` | `numeric`, nullable | Montant facturé au client (utile en V2/V3) |
## Table `photos`
| Colonne | Type | Description |
|---|---|---|
| `id` | `uuid` (PK, défaut `gen_random_uuid()`) | Identifiant unique de la photo |
| `event_id` | `uuid` (FK → `events.id`, `on delete cascade`) | Événement associé |
| `url_storage` | `text` | Chemin/URL du fichier dans Supabase Storage |
| `nom_invite` | `text`, nullable | Nom saisi par l'invité (optionnel) |
| `message` | `text`, nullable | Message laissé par l'invité (optionnel) |
| `uploaded_at` | `timestamptz`, défaut `now()` | Date d'upload |
## Row Level Security (RLS)
RLS activé sur les deux tables. Règles à valider avec Kévin avant
implémentation définitive, principe retenu pour l'instant :
### `events`
- **Lecture publique** : non autorisée directement (pas de `SELECT` public
sur `events`). L'accès public à l'upload passe par une vérification côté
fonction/API sur le `slug` et le `statut = 'actif'`, sans exposer toute la
ligne (notamment pas `admin_token`).
- **Lecture admin** : autorisée si la requête présente l'`admin_token`
correspondant (comparaison côté fonction Postgres/Edge Function plutôt que
policy RLS naïve, pour éviter d'exposer le token dans une clause `WHERE`
côté client).
- **Écriture** : réservée à l'admin/back-office (pas de policy publique
d'insertion/update sur `events`).
### `photos`
- **Insertion (upload) anonyme autorisée** si et seulement si :
- l'`event_id` correspond à un événement dont le `statut = 'actif'`, et
- la date courante est antérieure à `expire_at`.
- **Lecture** réservée à l'admin (accès via `admin_token` validé), jamais
de lecture publique de la liste des photos d'un événement.
- **Update / delete** : pas d'update par les invités ; delete réservé au
job d'expiration automatique (ou à l'admin, à confirmer).
> ⚠️ Le détail exact des policies RLS (SQL) sera écrit dans
> `supabase/migrations/` au moment de l'implémentation, et validé avec
> Kévin avant application — cf. règle "décision structurante" dans
> `CLAUDE.md`.
+47
View File
@@ -0,0 +1,47 @@
# RGPD
> ⚠️ **Ce document n'est pas un avis juridique formel.** Il pose les
> principes retenus pour le projet, à faire valider par un professionnel du
> droit (avocat, service juridique) avant tout usage commercial (V2/V3)
> impliquant des clients externes.
## Mention légale (page d'upload)
Texte à afficher, de façon visible, sur la page d'upload avant que l'invité
ne dépose une photo :
> En déposant une photo, vous acceptez qu'elle soit stockée temporairement
> dans le cadre de cet événement et mise à disposition de l'organisateur.
> Vos photos sont automatiquement supprimées **7 jours après la date de
> l'événement**, sans action de votre part. Aucune photo n'est utilisée à
> d'autres fins, ni transmise à des tiers. Vous pouvez demander la
> suppression anticipée d'une photo en contactant l'organisateur de
> l'événement.
## Politique de conservation
- **Durée de conservation** : 7 jours à compter de la **date de
l'événement** (`date_evenement`), et non de la date de création de
l'événement dans le système ni de la date d'upload de chaque photo.
- **Suppression automatique et systématique** : à l'échéance, les photos
(fichiers dans Supabase Storage) et les métadonnées associées (lignes de
la table `photos`) sont supprimées, **même si l'organisateur a déjà
téléchargé le ZIP d'export**. Le téléchargement du ZIP transfère la
responsabilité de conservation à l'organisateur ; le système ne conserve
pas de copie au-delà de l'échéance.
- **Données minimales collectées côté invité** : uniquement ce que l'invité
choisit de fournir (nom, message), aucune collecte d'email, de
numéro de téléphone ou de données de compte (pas de compte invité).
- **Pas de traçage** : pas de cookies de suivi ni d'analytics tiers sur la
page d'upload, conformément à la contrainte d'écoconception et de
sobriété du projet.
## Points à valider avec Kévin (et si besoin un juriste) avant V2/V3
- Rédaction définitive des CGU/mentions légales pour un usage commercial
auprès de clients externes (mariages, événementiel pro).
- Statut du client (organisateur) comme responsable de traitement, et de
Kévin/l'outil comme sous-traitant, au sens RGPD — à formaliser
contractuellement en V2/V3.
- Procédure de demande de suppression anticipée par un invité (droit à
l'effacement) avant l'échéance des 7 jours.