Ajoute la V1 fonctionnelle : upload invité, galerie admin, QR code, backend Supabase

Schéma dédié `photobooth` (jamais exposé via PostgREST, accès exclusivement
via fonctions SECURITY DEFINER dans public) avec RLS, policies Storage sur
bucket privé event-photos, Edge Functions admin-gallery et expire-events,
et job pg_cron d'expiration J+7. Le tout déployé et testé en conditions
réelles sur le projet Supabase kevin-lecou-hub avant relecture sécurité
(faille d'abus sur insert_public_photo corrigée en cours de route).

Côté front : page upload invité (compression + HEIC + RGPD), galerie admin
avec export ZIP, générateur de QR code autonome. Workflow n8n de purge
Storage J+7 fourni (à activer manuellement côté n8n).
This commit is contained in:
2026-09-16 11:32:31 +02:00
parent 2007eab006
commit 9b1461978b
37 changed files with 6255 additions and 4 deletions
+180
View File
@@ -0,0 +1,180 @@
/* Page upload invité — mobile-first, sobre, sans dépendance externe */
:root {
color-scheme: light;
--color-bg: #faf9f7;
--color-text: #2a2a28;
--color-muted: #6b6b66;
--color-accent: #8a5a44;
--color-accent-contrast: #ffffff;
--color-error-bg: #fdecea;
--color-error-text: #7a1f14;
--color-success-bg: #eaf5ec;
--color-success-text: #1e5c2d;
--radius: 10px;
--spacing: 1rem;
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--color-bg);
color: var(--color-text);
line-height: 1.45;
}
.page {
max-width: 480px;
margin: 0 auto;
padding: 1.25rem 1rem 3rem;
min-height: 100dvh;
}
.page__header {
text-align: center;
margin-bottom: 1.5rem;
}
.page__header h1 {
font-size: 1.4rem;
margin: 0.25rem 0 0;
}
.state-block {
text-align: center;
padding: 1.25rem 1rem;
border-radius: var(--radius);
margin-bottom: 1rem;
}
.state-block--error {
background: var(--color-error-bg);
color: var(--color-error-text);
}
.state-block--success {
background: var(--color-success-bg);
color: var(--color-success-text);
}
.state-block p {
margin: 0 0 0.75rem;
}
.state-block p:last-child {
margin-bottom: 0;
}
.upload-form {
display: flex;
flex-direction: column;
gap: 1.1rem;
}
.field {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.field label {
font-weight: 600;
font-size: 0.95rem;
}
.field .optional {
font-weight: 400;
color: var(--color-muted);
font-size: 0.85rem;
}
input[type="text"],
input[type="file"],
textarea {
font: inherit;
padding: 0.7rem 0.75rem;
border: 1px solid #d8d5cf;
border-radius: var(--radius);
background: #fff;
color: inherit;
width: 100%;
}
input[type="file"] {
padding: 0.55rem 0.5rem;
}
textarea {
resize: vertical;
min-height: 4.5rem;
}
input:focus-visible,
textarea:focus-visible,
button:focus-visible {
outline: 3px solid var(--color-accent);
outline-offset: 1px;
}
.preview {
width: 100%;
max-height: 320px;
object-fit: contain;
border-radius: var(--radius);
background: #eee;
}
.legal-notice {
font-size: 0.8rem;
color: var(--color-muted);
background: #f1efe9;
border-radius: var(--radius);
padding: 0.75rem;
margin: 0;
}
.progress-text {
min-height: 1.2em;
font-size: 0.9rem;
color: var(--color-muted);
margin: 0;
text-align: center;
}
button {
font: inherit;
font-weight: 600;
border: none;
border-radius: var(--radius);
padding: 0.85rem 1rem;
min-height: 48px;
cursor: pointer;
}
#submit-btn,
#add-another-btn {
background: var(--color-accent);
color: var(--color-accent-contrast);
}
#submit-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
#retry-btn {
background: var(--color-error-text);
color: #fff;
}
button:active {
transform: translateY(1px);
}