Prisma 7 : pilote pg, client généré, prisma.config.ts (#164)

* Prisma 7 : client généré dans src/generated, pilote pg, prisma.config.ts

- générateur prisma-client (ESM, extension .js), compilé dans dist : plus de node_modules/.prisma à copier dans les images
- @prisma/adapter-pg, délai de connexion de 5 s comme le moteur de Prisma 6
- prisma.config.ts : adresse de la base et .env pour le CLI, sans dépendre de prisma/config à l'exécution (CLI à part dans les images)
- CI : migrate diff --from-config-datasource --to-schema
- deploy.sh : arrêt avant installation si Node est trop vieux pour Prisma 7
- remplace les PR Dependabot #135 et #137

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Prisma 7 : import du client généré dans token-revocation (#163)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
IliesAllaliandClaude Opus 5.5 authored and GitHub committed 2026-10-26 13:58:42 +01:00
1 parent ef503c570d
commit cc01059c47
33 files changed
+1502 -204

No files matched your search

+1 -1
View File
@@ -141,7 +141,7 @@ jobs:
run: npx prisma migrate deploy run: npx prisma migrate deploy
- name: Schéma Prisma aligné sur les migrations - name: Schéma Prisma aligné sur les migrations
run: npx prisma migrate diff --from-url "$DATABASE_URL" --to-schema-datamodel prisma/schema.prisma --exit-code run: npx prisma migrate diff --from-config-datasource --to-schema prisma/schema.prisma --exit-code
- name: Tests - name: Tests
run: npm test run: npm test
+4 -4
View File
@@ -18,12 +18,12 @@ RUN apt-get update \
WORKDIR /app WORKDIR /app
COPY backend/package.json backend/package-lock.json ./ COPY backend/package.json backend/package-lock.json ./
COPY backend/prisma ./prisma
RUN npm ci --no-audit --no-fund RUN npm ci --no-audit --no-fund
RUN npx prisma generate
COPY backend/tsconfig.json ./ COPY backend/prisma ./prisma
COPY backend/prisma.config.ts backend/tsconfig.json ./
COPY backend/src ./src COPY backend/src ./src
RUN npx prisma generate
RUN npm run build RUN npm run build
@@ -68,8 +68,8 @@ RUN PRISMA_VERSION="$(node -p "require('./package-lock.json').packages['node_mod
&& npm install --no-audit --no-fund "prisma@${PRISMA_VERSION}" \ && npm install --no-audit --no-fund "prisma@${PRISMA_VERSION}" \
&& npm cache clean --force && npm cache clean --force
COPY --from=api-build /app/node_modules/.prisma ./node_modules/.prisma
COPY backend/prisma ./prisma COPY backend/prisma ./prisma
COPY backend/prisma.config.ts ./
COPY --from=api-build /app/dist ./dist COPY --from=api-build /app/dist ./dist
# nginx : la configuration du conteneur web de compose, l'API étant ici sur la même machine. # nginx : la configuration du conteneur web de compose, l'API étant ici sur la même machine.
+1
View File
@@ -7,6 +7,7 @@
!deploy/aio !deploy/aio
backend/node_modules backend/node_modules
backend/dist backend/dist
backend/src/generated
backend/.local backend/.local
backend/storage backend/storage
backend/test backend/test
+1
View File
@@ -1,5 +1,6 @@
node_modules node_modules
dist dist
src/generated
.local .local
storage storage
test test
+3
View File
@@ -3,3 +3,6 @@ dist/
.env .env
.local/ .local/
*.log *.log
# Client Prisma généré (prisma generate)
src/generated/
+7 -8
View File
@@ -1,12 +1,12 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# API Parenthèse (Fastify + Prisma). Contexte de build : le dossier backend/. # API Parenthèse (Fastify + Prisma). Contexte de build : le dossier backend/.
# Étape 1 : dépendances complètes, client Prisma, compilation TypeScript. # Étape 1 : dépendances complètes, client Prisma (généré dans src/generated), compilation TypeScript.
# Étape 2 : image finale avec les seules dépendances de production, plus la CLI Prisma # Étape 2 : image finale avec les seules dépendances de production, plus la CLI Prisma
# installée à part (nécessaire à `prisma migrate deploy` au démarrage). # installée à part (nécessaire à `prisma migrate deploy` au démarrage).
FROM node:24-bookworm-slim AS build FROM node:24-bookworm-slim AS build
# openssl : requis par les moteurs Prisma (cible debian-openssl-3.0.x). # openssl : requis par le moteur de migration de Prisma (cible debian-openssl-3.0.x).
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends openssl ca-certificates \ && apt-get install -y --no-install-recommends openssl ca-certificates \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -14,12 +14,12 @@ RUN apt-get update \
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
COPY prisma ./prisma
RUN npm ci --no-audit --no-fund RUN npm ci --no-audit --no-fund
RUN npx prisma generate
COPY tsconfig.json ./ COPY prisma ./prisma
COPY prisma.config.ts tsconfig.json ./
COPY src ./src COPY src ./src
RUN npx prisma generate
RUN npm run build RUN npm run build
@@ -46,10 +46,9 @@ RUN PRISMA_VERSION="$(node -p "require('./package-lock.json').packages['node_mod
&& npm install --no-audit --no-fund "prisma@${PRISMA_VERSION}" \ && npm install --no-audit --no-fund "prisma@${PRISMA_VERSION}" \
&& npm cache clean --force && npm cache clean --force
# Client Prisma généré à l'étape 1 (même image de base, même cible de moteur). # Schéma, migrations et configuration : indispensables à `prisma migrate deploy`. Le client Prisma est compilé dans dist.
COPY --from=build /app/node_modules/.prisma ./node_modules/.prisma
# Schéma + migrations : indispensables à `prisma migrate deploy`.
COPY prisma ./prisma COPY prisma ./prisma
COPY prisma.config.ts ./
COPY --from=build /app/dist ./dist COPY --from=build /app/dist ./dist
RUN mkdir -p /app/storage/media && chown -R node:node /app/storage RUN mkdir -p /app/storage/media && chown -R node:node /app/storage
+1431 -157
View File
File diff suppressed because it is too large. Load diff
+5 -2
View File
@@ -22,16 +22,19 @@
"@fastify/jwt": "^10.2.5", "@fastify/jwt": "^10.2.5",
"@fastify/rate-limit": "^11.2.0", "@fastify/rate-limit": "^11.2.0",
"@node-rs/bcrypt": "^1.10.7", "@node-rs/bcrypt": "^1.10.7",
"@prisma/client": "^6.15.0", "@prisma/adapter-pg": "^7.10.1",
"@prisma/client": "^7.10.1",
"dotenv": "^18.0.8", "dotenv": "^18.0.8",
"fastify": "^5.12.7", "fastify": "^5.12.7",
"openid-client": "^6.8.8", "openid-client": "^6.8.8",
"pg": "^8.23.1",
"sharp": "^0.35.5", "sharp": "^0.35.5",
"zod": "^4.6.7" "zod": "^4.6.7"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.19.2", "@types/node": "^24.19.2",
"prisma": "^6.15.0", "@types/pg": "^8.23.1",
"prisma": "^7.10.1",
"tsx": "^4.23.15", "tsx": "^4.23.15",
"typescript": "^7.0.2", "typescript": "^7.0.2",
"vitest": "^5.0.5" "vitest": "^5.0.5"
+13
View File
@@ -0,0 +1,13 @@
import { config } from 'dotenv'
import type { PrismaConfig } from 'prisma/config'
// Configuration du CLI Prisma (generate, migrate). Depuis Prisma 7, le CLI ne lit plus .env tout seul. Seul un import
// de type vient de prisma/config : dans les images, le CLI est installé à part (/opt/prisma) et ce fichier doit se
// charger sans lui. Sans DATABASE_URL (generate pendant un build), l'adresse reste vide.
config({ quiet: true })
export default {
schema: 'prisma/schema.prisma',
migrations: { path: 'prisma/migrations' },
datasource: { url: process.env.DATABASE_URL ?? '' },
} satisfies PrismaConfig
+6 -2
View File
@@ -1,10 +1,14 @@
// Client généré dans src/generated (ignoré par git), compilé avec le reste dans dist : rien à copier depuis
// node_modules dans les images. L'adresse de la base vient de prisma.config.ts.
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "esm"
importFileExtension = "js"
} }
datasource db { datasource db {
provider = "postgresql" provider = "postgresql"
url = env("DATABASE_URL")
} }
enum MembershipRole { enum MembershipRole {
+1 -1
View File
@@ -4,7 +4,7 @@ import cookie from '@fastify/cookie'
import cors from '@fastify/cors' import cors from '@fastify/cors'
import jwt from '@fastify/jwt' import jwt from '@fastify/jwt'
import rateLimit from '@fastify/rate-limit' import rateLimit from '@fastify/rate-limit'
import { Prisma } from '@prisma/client' import { Prisma } from './generated/prisma/client.js'
import { env } from './config/env.js' import { env } from './config/env.js'
import { prisma } from './lib/prisma.js' import { prisma } from './lib/prisma.js'
+1 -1
View File
@@ -1,6 +1,6 @@
import path from 'node:path' import path from 'node:path'
import { Prisma, type PrismaClient } from '@prisma/client' import { Prisma, type PrismaClient } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
import { InvalidAnnotationContentError, sanitizeAnnotationContentForWrite } from './annotation-content.js' import { InvalidAnnotationContentError, sanitizeAnnotationContentForWrite } from './annotation-content.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Prisma, type PrismaClient } from '@prisma/client' import { Prisma, type PrismaClient } from '../generated/prisma/client.js'
type Db = PrismaClient | Prisma.TransactionClient type Db = PrismaClient | Prisma.TransactionClient
+1 -1
View File
@@ -1,6 +1,6 @@
import { createHash } from 'node:crypto' import { createHash } from 'node:crypto'
import type { PrismaClient } from '@prisma/client' import type { PrismaClient } from '../generated/prisma/client.js'
import { env } from '../config/env.js' import { env } from '../config/env.js'
import { decryptImmichApiKey } from './immich-secret.js' import { decryptImmichApiKey } from './immich-secret.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Prisma } from '@prisma/client' import type { Prisma } from '../generated/prisma/client.js'
// Renumérote les souvenirs acceptés d'une personne et met en avant les trois premiers. // Renumérote les souvenirs acceptés d'une personne et met en avant les trois premiers.
// Les souvenirs en attente de relecture ne comptent pas. // Les souvenirs en attente de relecture ne comptent pas.
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
type Db = PrismaClient | Prisma.TransactionClient type Db = PrismaClient | Prisma.TransactionClient
+5 -8
View File
@@ -1,12 +1,9 @@
import pkg from '@prisma/client' import { PrismaPg } from '@prisma/adapter-pg'
import { env } from '../config/env.js' import { env } from '../config/env.js'
import { PrismaClient } from '../generated/prisma/client.js'
const { PrismaClient } = pkg // Prisma 7 passe par le pilote pg. Délai de connexion de 5 s, comme le moteur de Prisma 6 (pg attend sans fin par défaut)
export const prisma = new PrismaClient({ export const prisma = new PrismaClient({
datasources: { adapter: new PrismaPg({ connectionString: env.DATABASE_URL, connectionTimeoutMillis: 5_000 }),
db: {
url: env.DATABASE_URL,
},
},
}) })
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
import { env } from '../config/env.js' import { env } from '../config/env.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
type LinkReader = Pick<PrismaClient, 'parentChildLink'> | Pick<Prisma.TransactionClient, 'parentChildLink'> type LinkReader = Pick<PrismaClient, 'parentChildLink'> | Pick<Prisma.TransactionClient, 'parentChildLink'>
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PrismaClient } from '@prisma/client' import type { PrismaClient } from '../generated/prisma/client.js'
// Sessions fermées par « Se déconnecter » ou par la suppression du compte, jusqu'à la fin de leur jeton. // Sessions fermées par « Se déconnecter » ou par la suppression du compte, jusqu'à la fin de leur jeton.
// Gardées en mémoire pour ne coûter aucune requête par appel, et en base pour survivre au redémarrage de l'API : // Gardées en mémoire pour ne coûter aucune requête par appel, et en base pour survivre au redémarrage de l'API :
+1 -1
View File
@@ -1,6 +1,6 @@
import { createHash, randomBytes } from 'node:crypto' import { createHash, randomBytes } from 'node:crypto'
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
// Co-gestion d'un arbre (issue #145). Le propriétaire invite une adresse ; Parenthèse n'envoie pas de mail, il // Co-gestion d'un arbre (issue #145). Le propriétaire invite une adresse ; Parenthèse n'envoie pas de mail, il
// donne un lien à transmettre. Seule l'empreinte du jeton est gardée en base. // donne un lien à transmettre. Seule l'empreinte du jeton est gardée en base.
+1 -1
View File
@@ -1,7 +1,7 @@
import { rm } from 'node:fs/promises' import { rm } from 'node:fs/promises'
import path from 'node:path' import path from 'node:path'
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
import { env } from '../config/env.js' import { env } from '../config/env.js'
+1 -1
View File
@@ -1,5 +1,5 @@
import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify' import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify'
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
import { import {
+2 -2
View File
@@ -1,7 +1,7 @@
import path from 'node:path' import path from 'node:path'
import type { FastifyInstance, FastifyPluginAsync } from 'fastify' import type { FastifyInstance, FastifyPluginAsync } from 'fastify'
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
import { env, UPLOAD_BODY_LIMIT } from '../config/env.js' import { env, UPLOAD_BODY_LIMIT } from '../config/env.js'
import { sanitizeAnnotationContentForRead } from '../lib/annotation-content.js' import { sanitizeAnnotationContentForRead } from '../lib/annotation-content.js'
@@ -250,7 +250,7 @@ function stripAnnotationUrl<T>(value: T): T {
function toPrismaJson( function toPrismaJson(
value: unknown, value: unknown,
): Prisma.InputJsonValue | Prisma.NullTypes.DbNull | Prisma.NullTypes.JsonNull | undefined { ): Prisma.InputJsonValue | typeof Prisma.DbNull | typeof Prisma.JsonNull | undefined {
if (value === undefined) { if (value === undefined) {
return undefined return undefined
} }
+1 -1
View File
@@ -1,4 +1,4 @@
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify' import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify'
import path from 'node:path' import path from 'node:path'
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
+1 -1
View File
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto'
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises' import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises'
import path from 'node:path' import path from 'node:path'
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import type { FastifyPluginAsync, FastifyRequest } from 'fastify' import type { FastifyPluginAsync, FastifyRequest } from 'fastify'
import { z } from 'zod' import { z } from 'zod'
+1 -1
View File
@@ -1,6 +1,6 @@
import type { FastifyInstance, FastifyPluginAsync, FastifyReply } from 'fastify' import type { FastifyInstance, FastifyPluginAsync, FastifyReply } from 'fastify'
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
import { env } from '../config/env.js' import { env } from '../config/env.js'
+1 -1
View File
@@ -1,7 +1,7 @@
import path from 'node:path' import path from 'node:path'
import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify' import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify'
import { Prisma } from '@prisma/client' import { Prisma } from '../generated/prisma/client.js'
import { z } from 'zod' import { z } from 'zod'
import { env } from '../config/env.js' import { env } from '../config/env.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PrismaClient } from '@prisma/client' import type { PrismaClient } from '../generated/prisma/client.js'
import type { Actor, AnyJwtPayload } from './auth.js' import type { Actor, AnyJwtPayload } from './auth.js'
declare module '@fastify/jwt' { declare module '@fastify/jwt' {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Annotation, MediaItem, MediaPersonTag, ParentChildLink, Person, Union } from '@prisma/client' import type { Annotation, MediaItem, MediaPersonTag, ParentChildLink, Person, Union } from '../generated/prisma/client.js'
import { sanitizeAnnotationContentForRead } from '../lib/annotation-content.js' import { sanitizeAnnotationContentForRead } from '../lib/annotation-content.js'
import { readAvatarCrop, type AvatarCrop } from '../lib/avatar-crop.js' import { readAvatarCrop, type AvatarCrop } from '../lib/avatar-crop.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Prisma, PrismaClient } from '@prisma/client' import type { Prisma, PrismaClient } from '../generated/prisma/client.js'
type RelationReader = Pick<PrismaClient, 'parentChildLink' | 'union'> type RelationReader = Pick<PrismaClient, 'parentChildLink' | 'union'>
+1 -1
View File
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto'
import { mkdtemp, rm } from 'node:fs/promises' import { mkdtemp, rm } from 'node:fs/promises'
import os from 'node:os' import os from 'node:os'
import path from 'node:path' import path from 'node:path'
import { Prisma } from '@prisma/client' import { Prisma } from '../src/generated/prisma/client.js'
import type { FastifyInstance } from 'fastify' import type { FastifyInstance } from 'fastify'
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
+3
View File
@@ -133,6 +133,9 @@ predeploy_dump() {
BACKEND_CHANGED=0 BACKEND_CHANGED=0
if changed backend ecosystem.config.cjs; then if changed backend ecosystem.config.cjs; then
BACKEND_CHANGED=1 BACKEND_CHANGED=1
# Prisma 7 demande Node ^20.19, ^22.12 ou 24+ : avec un Node plus vieux, on s'arrête avant toute installation
node -e "const [a, b] = process.versions.node.split('.').map(Number); process.exit((a === 20 && b >= 19) || (a === 22 && b >= 12) || a >= 24 ? 0 : 1)" \
|| { echo "ERROR: Node $(node -v) trop ancien pour Prisma 7 (^20.19, ^22.12 ou 24+)"; exit 1; }
install_deps backend install_deps backend
(cd "$REPO_DIR/backend" && node -e "require('fastify'); require('zod'); require('@prisma/client')") \ (cd "$REPO_DIR/backend" && node -e "require('fastify'); require('zod'); require('@prisma/client')") \
|| { echo "ERROR: dépendances backend manquantes après installation"; exit 1; } || { echo "ERROR: dépendances backend manquantes après installation"; exit 1; }