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:
@@ -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
@@ -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
|
||||||
@@ -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`).
|
||||||
@@ -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é.
|
||||||
@@ -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).
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Cahier des charges
|
||||||
@@ -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.
|
||||||
@@ -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`.
|
||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user