Pour commencer · 3 min
Structure du projet
Où tout se trouve et, plus utile encore, où regarder quand quelque chose de précis est cassé.
src/
app/
api/checkout/route.ts ouvre une session Stripe Checkout
api/webhooks/stripe/route.ts la livraison : le seul endroit où une vente devient réelle
auth/callback/route.ts transforme un lien magique en cookie de session
app/page.tsx le tableau de bord connecté
admin/page.tsx derrière ADMIN_EMAILS
blog/ articles MDX
sign-in/page.tsx
layout.tsx polices, langue, thème, providers
globals.css tous les tokens de design
sitemap.ts robots.ts icon.tsx opengraph-image.tsx
components/
ui/ Card, Field : les deux que vous réutiliserez
SignInForm SignOutButton LocaleSwitcher CopyField Logo Icons
lib/
auth.ts currentUser, requireUser
supabase/server.ts les deux clients serveur
supabase/client.ts le client navigateur
stripe.ts email.ts blog.ts i18n.ts i18n.server.ts
paraglide/ généré. Ne jamais éditer, ne jamais relire
messages/
en.json fr.json la source de vérité des textes
content/blog/ les articles sont des fichiers
supabase/schema.sql tables, policies, triggers
scripts/setup.mjs scripts/db-push.mjs
proxy.ts s'exécute à chaque requête
Où regarder quand
| Symptôme | Fichier |
|---|---|
| Connecté, puis déconnecté aussitôt | proxy.ts, puis auth/callback/route.ts |
| Le lien magique s'ouvre et ne fait rien | Les modèles d'e-mail Supabase, voir Authentification |
| Paiement passé, mais rien ne s'est produit | api/webhooks/stripe/route.ts et le journal d'événements Stripe |
| Mauvais montant débité | api/checkout/route.ts. Le prix vient de la base, jamais de la requête |
Le texte affiche {name} littéralement | messages/*.json, voir Internationalisation |
| Un token de couleur ne fait rien | app/globals.css. Un token doit être déclaré dans @theme avant que Tailwind n'émette la classe |
| Un utilisateur lit la ligne d'un autre | supabase/schema.sql. C'est une policy, pas du code applicatif |
proxy.ts
Next.js 16 a renommé le middleware. Le fichier s'appelle proxy.ts, à la
racine, et il exporte proxy, pas middleware. Si vous copiez un extrait d'un
article écrit avant le renommage, c'est pour cela qu'il ne fait rien.
Il s'exécute à chaque requête et fait une seule chose : rafraîchir le cookie d'authentification. Les Server Components ne peuvent pas écrire de cookies, donc sans lui une session expire en pleine visite et l'utilisateur est déconnecté au milieu du paiement.
src/paraglide
Généré par le compilateur de messages. Le dossier est commité pour qu'un clone propre se construise sans lancer le compilateur d'abord, mais vous ne l'éditez jamais et il n'y a aucun intérêt à le lire dans un diff. Si vous voyez trois cents fichiers modifiés là-dedans, c'est que vous avez ajouté un message.
Les deux clients Supabase
C'est la distinction qui piège, elle vaut la peine d'être apprise tôt.
supabaseServer() // agit comme l'utilisateur connecté. RLS s'applique. Par défaut.
supabaseAdmin() // agit comme le rôle de service. RLS ne s'applique pas.supabaseAdmin() existe pour le webhook, qui n'a pas d'utilisateur et doit
écrire des lignes qu'aucun utilisateur n'a le droit d'écrire. Y recourir parce
qu'une requête n'a rien renvoyé, c'est ainsi qu'on livre une application où
chaque utilisateur lit toutes les lignes. Si supabaseServer() ne renvoie rien,
le bug est dans la policy.
Une erreur ou un manque sur cette page ? Dites-le-nous.