diff --git a/app/lib/db.ts b/app/lib/db.ts index 3407426..23968f1 100644 --- a/app/lib/db.ts +++ b/app/lib/db.ts @@ -10,4 +10,16 @@ function databaseUrl(): string { return url; } -export const sql = postgres(databaseUrl(), { ssl: 'require' }); +/** + * SSL mode is configurable via DATABASE_SSL because internal/private Postgres + * instances (e.g. Coolify's docker-network Postgres) often don't terminate TLS, + * which causes ECONNRESET on connect if ssl is forced to 'require'. + * Defaults to 'require' to preserve prior behavior for externally-hosted DBs. + */ +function sslMode(): 'require' | false { + const mode = process.env['DATABASE_SSL']?.trim().toLowerCase(); + if (mode === 'disable' || mode === 'false') return false; + return 'require'; +} + +export const sql = postgres(databaseUrl(), { ssl: sslMode() }); diff --git a/server.ts b/server.ts index f626f99..1624076 100644 --- a/server.ts +++ b/server.ts @@ -22,10 +22,16 @@ const PING_INTERVAL_MS = 30_000; const SEEN_THROTTLE_MS = 60_000; const WS_PATH = '/api/nodes/ws'; +function sslMode(): 'require' | false { + const mode = process.env.DATABASE_SSL?.trim().toLowerCase(); + if (mode === 'disable' || mode === 'false') return false; + return 'require'; +} + function createDb() { const url = process.env.DATABASE_URL?.trim(); if (!url) throw new Error('DATABASE_URL is not set'); - return postgres(url, { ssl: 'require' }); + return postgres(url, { ssl: sslMode() }); } const db = createDb(); diff --git a/tests/lib/db-bootstrap.test.ts b/tests/lib/db-bootstrap.test.ts index bc61f12..e2f3e57 100644 --- a/tests/lib/db-bootstrap.test.ts +++ b/tests/lib/db-bootstrap.test.ts @@ -33,6 +33,17 @@ describe('app/lib/db bootstrap', () => { }); }); + test('disables ssl when DATABASE_SSL=disable (internal/private Postgres)', async () => { + process.env.DATABASE_SSL = 'disable'; + vi.resetModules(); + await import('@/app/lib/db'); + + expect(postgresMock).toHaveBeenCalledWith('postgres://user:pass@localhost:5432/testdb', { + ssl: false, + }); + delete process.env.DATABASE_SSL; + }); + test('throws when DATABASE_URL is missing', async () => { delete process.env.DATABASE_URL; vi.resetModules();