Aller au contenu principal

Backend (Supabase)

Vue d'ensemble

Le backend utilise Supabase qui fournit :

  • Base de données PostgreSQL
  • Authentification
  • Stockage de fichiers
  • Edge Functions (serverless)
  • Realtime subscriptions

Structure du Projet

back-dedicaces/
├── supabase/
│ ├── functions/ # Edge Functions
│ │ ├── invite-participate/
│ │ ├── stripe/
│ │ ├── stripe-webhook/
│ │ ├── resend-invite-participate/
│ │ └── schedule-participant/
│ └── migrations/ # Migrations SQL
├── .env # Variables dev
└── .env.production # Variables prod

Authentification

Configuration

Supabase Auth est configuré avec :

  • Email/Password : Authentification par défaut
  • Magic Links : Liens de connexion par email
  • Invitations : Via auth.admin.inviteUserByEmail()

Flux d'Invitation

// Edge Function: invite-participate
const { email, productId } = await req.json();

// Créer l'invitation via Supabase Admin
const { data, error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
email,
{
redirectTo: `${SITE_URL}/confirmation-invitation?productId=${productId}`
}
);

// Créer l'entrée product_items
await supabaseAdmin
.from('product_items')
.insert({
product_id: productId,
participant_id: data.user.id,
participant_name: email
});

Edge Functions

invite-participate

Invite des participants à un événement.

Endpoint: POST /functions/v1/invite-participate

Body:

{
"productId": "123",
"emails": ["user1@example.com", "user2@example.com"],
"sendByEmail": true
}

Réponse:

{
"success": true,
"invited": 2
}

stripe

Crée une session Stripe Checkout.

Endpoint: POST /functions/v1/stripe

Body:

{
"product_id": "123"
}

Réponse:

{
"id": "cs_xxx",
"url": "https://checkout.stripe.com/..."
}

Implémentation:

import Stripe from 'stripe';

const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY'));

Deno.serve(async (req) => {
const { product_id } = await req.json();

const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price: 'price_1OrKs4Hk3BKifCINtMxMgRYE',
quantity: 1,
}],
mode: 'payment',
success_url: `${SITE_URL}/paiement/succes?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${SITE_URL}/paiement/erreur`,
metadata: {
product_id
}
});

return new Response(JSON.stringify(session), {
headers: { 'Content-Type': 'application/json' }
});
});

stripe-webhook

Gère les webhooks Stripe.

Endpoint: POST /functions/v1/stripe-webhook

Événement traité: checkout.session.completed

Deno.serve(async (req) => {
const signature = req.headers.get('stripe-signature');
const body = await req.text();

const event = stripe.webhooks.constructEvent(
body,
signature,
Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')
);

if (event.type === 'checkout.session.completed') {
const session = event.data.object;
const productId = session.metadata.product_id;

// Mettre à jour le produit
await supabaseAdmin
.from('product')
.update({
session_id: session.id,
plan: 'basic'
})
.eq('id', productId);
}

return new Response('OK');
});

resend-invite-participate

Renvoie les invitations non confirmées.

schedule-participant

Gestion des participations programmées.


Storage

Configuration

Bucket : video_dedicace

-- Politique de stockage
CREATE POLICY "Allow authenticated uploads"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (bucket_id = 'video_dedicace');

CREATE POLICY "Allow public read"
ON storage.objects
FOR SELECT
TO anon
USING (bucket_id = 'video_dedicace');

Structure des Fichiers

video_dedicace/
└── video_{productId}/
├── dedicaces_{participantId_1}.webm
├── dedicaces_{participantId_2}.webm
└── ...

Upload depuis le Frontend

async uploadVideo(productId: string, file: File) {
const path = `video_${productId}/dedicaces_${uuid()}.webm`;

const { data, error } = await supabase.storage
.from('video_dedicace')
.upload(path, file, {
cacheControl: '30',
upsert: false
});

return data?.path;
}

URLs Signées

async getSignedUrl(path: string) {
const { data, error } = await supabase.storage
.from('video_dedicace')
.createSignedUrl(path, 3600); // 1 heure

return data?.signedUrl;
}

Row Level Security (RLS)

Politiques sur product

-- Lecture : propriétaire uniquement
CREATE POLICY "Users can view own products"
ON product FOR SELECT
TO authenticated
USING (created_by = auth.uid());

-- Création : utilisateurs authentifiés
CREATE POLICY "Users can create products"
ON product FOR INSERT
TO authenticated
WITH CHECK (created_by = auth.uid());

-- Mise à jour : propriétaire uniquement
CREATE POLICY "Users can update own products"
ON product FOR UPDATE
TO authenticated
USING (created_by = auth.uid());

Politiques sur product_items

-- Lecture publique (pour les participants)
CREATE POLICY "Anyone can view items"
ON product_items FOR SELECT
TO anon, authenticated
USING (true);

-- Création : tout le monde (messages anonymes)
CREATE POLICY "Anyone can create items"
ON product_items FOR INSERT
TO anon, authenticated
WITH CHECK (true);

Migrations

Créer une Migration

npx supabase db diff --schema public -f nom_migration \
--db-url="postgres://postgres:password@localhost:54322/postgres"

Appliquer les Migrations

# Local
npx supabase db push --db-url="postgres://..."

# Production
npx supabase db push --db-url="postgres://...@db.xxx.supabase.co:5432/postgres"

Structure des Migrations

migrations/
├── 20240101000000_initial_schema.sql
├── 20240115000000_add_product_table.sql
├── 20240201000000_add_product_items.sql
└── ...

Variables d'Environnement

.env (développement)

POSTGRES_PASSWORD=your_local_password
JWT_SECRET=your_jwt_secret
ANON_KEY=eyJ...
SERVICE_ROLE_KEY=eyJ...

.env.production

POSTGRES_PASSWORD=secure_password
JWT_SECRET=secure_jwt_secret
ANON_KEY=eyJ...
SERVICE_ROLE_KEY=eyJ...
STRIPE_API_KEY=sk_live_...
STRIPE_WEBHOOK_SIGNING_SECRET=whsec_...
SITE_URL=https://app.momentscollectifs.fr

Commandes Utiles

# Démarrer Supabase local
npx supabase start

# Arrêter
npx supabase stop

# Voir les logs
npx supabase logs

# Générer les types TypeScript
npx supabase gen types typescript --local > types/database.ts

# Déployer une Edge Function
npx supabase functions deploy nom-fonction

# Tester une Edge Function localement
npx supabase functions serve nom-fonction