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).
172 lines
7.3 KiB
JSON
172 lines
7.3 KiB
JSON
{
|
|
"name": "photobooth-qr — Purge Storage J+7",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"rule": {
|
|
"interval": [
|
|
{
|
|
"field": "cronExpression",
|
|
"expression": "30 3 * * *"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000001",
|
|
"name": "Cron quotidien 03h30 UTC",
|
|
"type": "n8n-nodes-base.scheduleTrigger",
|
|
"typeVersion": 1.2,
|
|
"position": [ -400, 0 ]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"content": "## photobooth-qr — Purge Storage J+7\n\n**Pourquoi 03h30 et pas 03h00 ?** Le job `public.expire_events()` (pg_cron, côté Supabase, projet `kevin-lecou-hub`) tourne à 03h00 UTC et marque les events expirés (`statut = 'expire'`) + supprime les lignes `photobooth.photos`. Ce workflow tourne 30 min plus tard pour laisser le temps à ce job SQL de terminer avant d'aller nettoyer les fichiers réels dans Storage.\n\n**Input** : aucun (le trigger est un cron, pas de payload).\n\n**Ce que fait l'Edge Function `expire-events`** : elle liste elle-même les events `statut = 'expire'` côté Supabase (via `public.get_expired_event_ids()`) et purge leur dossier `{event_id}/` dans le bucket `event-photos`. Ce workflow n'a donc rien à lui passer — il se contente de l'appeler et de vérifier que ça s'est bien passé.\n\n**Credential requis** : `Supabase service_role (photobooth-qr)` — credential type Header Auth, header **`Authorization`**, valeur `Bearer <clé service_role>`. Seul ce header est nécessaire (pas de header `apikey` — vérifié en test réel le 2026-09-16, `Authorization` seul suffit). ⚠️ Utiliser la clé au **nouveau format `sb_secret_...`** (Dashboard Supabase → kevin-lecou-hub → Settings → API Keys), PAS l'ancien JWT `service_role` legacy — les deux sont visibles dans le dashboard mais seule la valeur `sb_secret_...` est celle injectée par la plateforme et vérifiée côté Edge Function. À créer dans le Credentials Store n8n, jamais en dur dans le workflow.\n\n**Statut** : Edge Function déployée et testée en conditions réelles le 2026-09-16 (upload, purge, idempotence — tout fonctionne). Il reste à créer le credential ci-dessus dans n8n et à activer ce workflow (`active: false` actuellement).\n\n**Sortie / échec** : en cas d'échec (HTTP != 200, ou erreur réseau), notification Telegram immédiate — aucune erreur ne doit passer silencieusement, cf. règle globale CLAUDE.md.",
|
|
"height": 460,
|
|
"width": 460
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000099",
|
|
"name": "Sticky Note — Doc workflow",
|
|
"type": "n8n-nodes-base.stickyNote",
|
|
"typeVersion": 1,
|
|
"position": [ -400, -520 ]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ $env.SUPABASE_URL }}/functions/v1/expire-events",
|
|
"authentication": "predefinedCredentialType",
|
|
"nodeCredentialType": "httpHeaderAuth",
|
|
"options": {
|
|
"timeout": 120000
|
|
}
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000002",
|
|
"name": "Appel Edge Function expire-events",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [ -160, 0 ],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "REMPLACER_PAR_ID_CREDENTIAL_N8N",
|
|
"name": "Supabase service_role (photobooth-qr)"
|
|
}
|
|
},
|
|
"onError": "continueErrorOutput"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict"
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "cond-1",
|
|
"leftValue": "={{ $json.statusCode }}",
|
|
"rightValue": 200,
|
|
"operator": {
|
|
"type": "number",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
}
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000003",
|
|
"name": "Purge réussie ?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [ 80, 0 ]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"chatId": "={{ $env.TELEGRAM_CHAT_ID_KEVIN }}",
|
|
"text": "=⚠️ *photobooth-qr — échec purge Storage J+7*\n\nL'appel à l'Edge Function `expire-events` a échoué.\n\nStatut HTTP : {{ $json.statusCode || 'inconnu (erreur réseau)' }}\nDétail : {{ $json.error?.message || $json.body || 'voir logs n8n' }}\n\n➡️ Les fichiers expirés ne sont PAS supprimés tant que ce n'est pas corrigé — vérifier manuellement le bucket `event-photos` si le problème persiste plusieurs jours (engagement RGPD J+7).",
|
|
"additionalFields": {
|
|
"parse_mode": "Markdown"
|
|
}
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000004",
|
|
"name": "Telegram → Erreur purge Storage",
|
|
"type": "n8n-nodes-base.telegram",
|
|
"typeVersion": 1.2,
|
|
"position": [ 340, 120 ],
|
|
"credentials": {
|
|
"telegramApi": {
|
|
"id": "REMPLACER_PAR_ID_CREDENTIAL_N8N",
|
|
"name": "Telegram Bot Kevin"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"chatId": "={{ $env.TELEGRAM_CHAT_ID_KEVIN }}",
|
|
"text": "=⚠️ *photobooth-qr — échec purge Storage J+7 (erreur réseau/timeout)*\n\nL'appel HTTP vers `expire-events` n'a même pas abouti (timeout ou erreur réseau).\n\n➡️ Vérifier que l'Edge Function est bien déployée et que le VPS/Supabase sont accessibles.",
|
|
"additionalFields": {
|
|
"parse_mode": "Markdown"
|
|
}
|
|
},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000005",
|
|
"name": "Telegram → Erreur réseau",
|
|
"type": "n8n-nodes-base.telegram",
|
|
"typeVersion": 1.2,
|
|
"position": [ -160, 240 ],
|
|
"credentials": {
|
|
"telegramApi": {
|
|
"id": "REMPLACER_PAR_ID_CREDENTIAL_N8N",
|
|
"name": "Telegram Bot Kevin"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"id": "a1a1a1a1-0000-4000-8000-000000000006",
|
|
"name": "OK (pas de notif)",
|
|
"type": "n8n-nodes-base.noOp",
|
|
"typeVersion": 1,
|
|
"position": [ 340, -100 ]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Cron quotidien 03h30 UTC": {
|
|
"main": [
|
|
[
|
|
{ "node": "Appel Edge Function expire-events", "type": "main", "index": 0 }
|
|
]
|
|
]
|
|
},
|
|
"Appel Edge Function expire-events": {
|
|
"main": [
|
|
[
|
|
{ "node": "Purge réussie ?", "type": "main", "index": 0 }
|
|
]
|
|
],
|
|
"error": [
|
|
[
|
|
{ "node": "Telegram → Erreur réseau", "type": "main", "index": 0 }
|
|
]
|
|
]
|
|
},
|
|
"Purge réussie ?": {
|
|
"main": [
|
|
[
|
|
{ "node": "OK (pas de notif)", "type": "main", "index": 0 }
|
|
],
|
|
[
|
|
{ "node": "Telegram → Erreur purge Storage", "type": "main", "index": 0 }
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"active": false,
|
|
"settings": {
|
|
"executionOrder": "v1"
|
|
},
|
|
"meta": {
|
|
"instructions": "Workflow conçu hors-ligne (pas de connexion à l'instance n8n réelle) — à relire et adapter dans l'éditeur n8n avant activation : (1) lier les credentials 'Supabase service_role (photobooth-qr)' et 'Telegram Bot Kevin' existants ou à créer, (2) vérifier les noms d'env vars $env.SUPABASE_URL et $env.TELEGRAM_CHAT_ID_KEVIN dans la config n8n du VPS, (3) laisser 'active: false' tant que l'Edge Function expire-events n'a pas été testée en réel."
|
|
}
|
|
}
|