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
+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.