|
1 | 1 | import { PrismaClient } from '@prisma/client'; |
2 | 2 | import { PrismaPg } from '@prisma/adapter-pg'; |
3 | 3 |
|
4 | | - |
5 | 4 | const globalForPrisma = global as unknown as { prisma: PrismaClient }; |
6 | 5 |
|
7 | 6 | /** |
8 | | - * Derive pg SSL config from the DATABASE_URL sslmode parameter. |
9 | | - * |
10 | | - * pg-connection-string parses sslmode=require into ssl: {} (empty object), |
11 | | - * and pg@8+ treats ssl: {} as { rejectUnauthorized: true }, which rejects |
12 | | - * AWS RDS Proxy's self-signed certificate. This is a known pg@8 breaking |
13 | | - * change (https://node-postgres.com/announcements#ssl-by-default). |
| 7 | + * Derive pg SSL config from the DATABASE_URL. |
14 | 8 | * |
15 | | - * Per PostgreSQL spec (https://www.postgresql.org/docs/current/libpq-ssl.html): |
16 | | - * - require: encrypt connection, skip certificate verification |
17 | | - * - verify-ca: encrypt + verify server CA |
18 | | - * - verify-full: encrypt + verify CA + verify hostname |
| 9 | + * pg@8+ defaults rejectUnauthorized to true, which rejects AWS RDS Proxy's |
| 10 | + * certificate (signed by internal AWS CA, not in Node.js root CA store). |
19 | 11 | * |
20 | | - * Per Prisma v7 migration docs: "SSL certificate defaults have changed. |
21 | | - * Previously [v6 Rust engine], invalid SSL certificates were ignored." |
22 | | - * (https://www.prisma.io/docs/orm/more/upgrades/to-v7) |
| 12 | + * Per PostgreSQL sslmode spec: |
| 13 | + * - disable: no SSL |
| 14 | + * - require: encrypt, skip certificate verification |
| 15 | + * - verify-ca: encrypt + verify CA |
| 16 | + * - verify-full: encrypt + verify CA + hostname |
23 | 17 | * |
24 | | - * Our infra enforces sslmode=require with RDS Proxy (requireTls: true). |
25 | | - * The proxy's certificate is signed by an internal AWS CA not in Node.js's |
26 | | - * root CA store, so rejectUnauthorized: false is required. The connection |
27 | | - * is still TLS-encrypted — only identity verification is skipped. |
| 18 | + * When no sslmode is set, we default to SSL with rejectUnauthorized: false |
| 19 | + * for non-localhost connections (matches Prisma v6 behavior where the Rust |
| 20 | + * engine silently accepted all certificates). |
28 | 21 | */ |
29 | 22 | function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined { |
30 | | - const match = url.match(/sslmode=(\w[\w-]*)/); |
31 | | - if (!match) return undefined; |
| 23 | + const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/); |
32 | 24 |
|
33 | | - const mode = match[1]; |
34 | | - switch (mode) { |
35 | | - case 'disable': |
36 | | - return undefined; |
37 | | - case 'require': |
38 | | - case 'no-verify': |
39 | | - return { rejectUnauthorized: false }; |
40 | | - case 'verify-ca': |
41 | | - case 'verify-full': |
42 | | - return { rejectUnauthorized: true }; |
43 | | - default: |
44 | | - return undefined; |
| 25 | + if (sslmodeMatch) { |
| 26 | + switch (sslmodeMatch[1]) { |
| 27 | + case 'disable': |
| 28 | + return undefined; |
| 29 | + case 'require': |
| 30 | + case 'no-verify': |
| 31 | + return { rejectUnauthorized: false }; |
| 32 | + case 'verify-ca': |
| 33 | + case 'verify-full': |
| 34 | + return { rejectUnauthorized: true }; |
| 35 | + } |
45 | 36 | } |
| 37 | + |
| 38 | + // No sslmode specified — enable SSL for non-localhost (production default) |
| 39 | + const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url); |
| 40 | + return isLocalhost ? undefined : { rejectUnauthorized: false }; |
46 | 41 | } |
47 | 42 |
|
48 | 43 | function createPrismaClient(): PrismaClient { |
49 | 44 | const url = process.env.DATABASE_URL!; |
50 | 45 | const ssl = getSslConfig(url); |
51 | | - // Strip sslmode from connection string — pg parses it independently and |
52 | | - // can override our explicit ssl config. We handle SSL entirely via the ssl option. |
53 | | - const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, ''); |
54 | | - const adapter = new PrismaPg({ connectionString: cleanUrl, ssl }); |
| 46 | + const adapter = new PrismaPg({ connectionString: url, ssl }); |
55 | 47 | return new PrismaClient({ adapter }); |
56 | 48 | } |
57 | 49 |
|
|
0 commit comments