Rendre l'image constructible sans les secrets de production

La construction échouait : `src/lib/env.ts` validait le contrat d'environnement
à l'import, et `src/server/db.ts` ouvrait la connexion Prisma de même. Next.js
évalue les modules serveur pendant la construction, où ni la base ni la clé
n'existent — l'image exigeait donc les secrets de production pour être bâtie,
c'est-à-dire de les confier au constructeur. C'était à l'envers : ce sont des
valeurs d'exécution.

Les deux sont désormais résolus à la première lecture. La garantie ne bouge
pas : le premier appel utile arrive bien avant qu'une requête aboutisse, et
échoue avec le même message. Éprouvé en construisant sans aucune variable, ce
que fait exactement Docker.

Les tests décrivaient l'ancien contrat — ils restauraient l'environnement avant
de lire, ce qui ne prouvait plus rien une fois la lecture différée. Réécrits
pour lire pendant que les valeurs sont posées, avec deux cas de plus :
l'import seul n'exige rien, et la lecture d'une configuration absente échoue.

Ajout d'un `.dockerignore`, absent jusqu'ici. `.env` en tête, et ce n'est pas un
détail : il porte la clé de chiffrement et les mots de passe de la base, et
copié dans le contexte il aurait fini dans une couche de l'image, lisible par
quiconque la récupère.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
This commit is contained in:
Claude committed 2026-08-09 10:13:32 +00:00
1 parent 6a8c310c87
commit 495b91bf7f
4 files changed
+133 -24

No files matched your search

+24 -4
View File
@@ -24,8 +24,28 @@ function createClient(): PrismaClient {
});
}
export const prisma = globalForPrisma.prisma ?? createClient();
if (env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
/**
* Client construit à la **première utilisation**, pas à l'import.
*
* Next.js évalue les modules serveur pendant la construction de l'image, où
* aucune base n'existe : ouvrir la connexion à l'import rendait l'image
* impossible à construire sans une base joignable et sans les secrets de
* production. Une connexion est un objet d'exécution.
*/
function client(): PrismaClient {
globalForPrisma.prisma ??= createClient();
return globalForPrisma.prisma;
}
export const prisma: PrismaClient = new Proxy({} as PrismaClient, {
get(_target, property) {
const instance = client();
const value = Reflect.get(instance, property) as unknown;
// Les méthodes doivent rester liées à leur client : détachées du proxy,
// elles perdraient leur `this`.
return typeof value === 'function' ? value.bind(instance) : value;
},
has(_target, property) {
return property in client();
},
});