Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion markets-migrate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import process from "node:process"
import { Client } from "pg"

const databaseUrl = process.env.DATABASE_URL?.trim()
const LEGACY_SSL_MODES = new Set(["prefer", "require", "verify-ca"])
const MARKET_STORAGE_SCHEMA_SQL = `
CREATE TABLE IF NOT EXISTS symbol_directory (
symbol text PRIMARY KEY,
Expand Down Expand Up @@ -151,13 +152,41 @@ ALTER TABLE IF EXISTS portfolio_holdings
DROP CONSTRAINT IF EXISTS "portfolio_holdings_userId_fkey";
`

function normalizeDatabaseConnectionString(connectionString) {
const normalizedConnectionString = connectionString.trim()
if (!normalizedConnectionString) {
return normalizedConnectionString
}

let connectionUrl

try {
connectionUrl = new URL(normalizedConnectionString)
} catch {
return normalizedConnectionString
}

const useLibpqCompat = connectionUrl.searchParams.get("uselibpqcompat")
if (useLibpqCompat?.toLowerCase() === "true") {
return normalizedConnectionString
}

const sslMode = connectionUrl.searchParams.get("sslmode")?.toLowerCase()
if (!sslMode || !LEGACY_SSL_MODES.has(sslMode)) {
return normalizedConnectionString
}

connectionUrl.searchParams.set("sslmode", "verify-full")
return connectionUrl.toString()
}

if (!databaseUrl) {
console.error("Missing DATABASE_URL.")
process.exit(1)
}

const client = new Client({
connectionString: databaseUrl,
connectionString: normalizeDatabaseConnectionString(databaseUrl),
})

await client.connect()
Expand Down
43 changes: 43 additions & 0 deletions src/lib/server/__tests__/postgres.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from "vitest"

import { normalizeDatabaseConnectionString } from "../postgres"

describe("normalizeDatabaseConnectionString", () => {
it("upgrades legacy sslmode=require to verify-full", () => {
expect(
normalizeDatabaseConnectionString(
"postgresql://user:pass@example.com/db?sslmode=require"
)
).toBe(
"postgresql://user:pass@example.com/db?sslmode=verify-full"
)
})

it("keeps connection strings with explicit libpq compatibility", () => {
expect(
normalizeDatabaseConnectionString(
"postgresql://user:pass@example.com/db?uselibpqcompat=true&sslmode=require"
)
).toBe(
"postgresql://user:pass@example.com/db?uselibpqcompat=true&sslmode=require"
)
})

it("leaves non-legacy sslmode values unchanged", () => {
expect(
normalizeDatabaseConnectionString(
"postgresql://user:pass@example.com/db?sslmode=verify-full"
)
).toBe(
"postgresql://user:pass@example.com/db?sslmode=verify-full"
)
})

it("leaves connection strings without sslmode unchanged", () => {
expect(
normalizeDatabaseConnectionString(
"postgresql://user:pass@example.com/db"
)
).toBe("postgresql://user:pass@example.com/db")
})
})
36 changes: 35 additions & 1 deletion src/lib/server/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type DatabaseUrlEnvName =
| typeof DATABASE_URL_ENV_NAME
| typeof AUTH_DATABASE_URL_ENV_NAME

const LEGACY_SSL_MODES = new Set(["prefer", "require", "verify-ca"])

function getConfiguredDatabaseUrl(name: DatabaseUrlEnvName): string | null {
const databaseUrl = process.env[name]?.trim()
if (!databaseUrl) {
Expand All @@ -38,9 +40,41 @@ function getRequiredDatabaseUrl(name: DatabaseUrlEnvName): string {
return databaseUrl
}

export function normalizeDatabaseConnectionString(
connectionString: string
): string {
const normalizedConnectionString = connectionString.trim()
if (!normalizedConnectionString) {
return normalizedConnectionString
}

let connectionUrl: URL

try {
connectionUrl = new URL(normalizedConnectionString)
} catch {
return normalizedConnectionString
}

const useLibpqCompat = connectionUrl.searchParams.get("uselibpqcompat")
if (useLibpqCompat?.toLowerCase() === "true") {
return normalizedConnectionString
}

const sslMode = connectionUrl.searchParams.get("sslmode")?.toLowerCase()
if (!sslMode || !LEGACY_SSL_MODES.has(sslMode)) {
return normalizedConnectionString
}

// pg currently treats these legacy modes as verify-full. Make that explicit
// so builds and migrations keep the same behavior without the warning.
connectionUrl.searchParams.set("sslmode", "verify-full")
return connectionUrl.toString()
}

function createPool(connectionString: string) {
return new Pool({
connectionString,
connectionString: normalizeDatabaseConnectionString(connectionString),
})
}

Expand Down
31 changes: 30 additions & 1 deletion threads-migrate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import process from "node:process"
import { Client } from "pg"

const databaseUrl = process.env.DATABASE_URL?.trim()
const LEGACY_SSL_MODES = new Set(["prefer", "require", "verify-ca"])
const THREAD_STORAGE_SCHEMA_SQL = `
CREATE TABLE IF NOT EXISTS thread (
"userId" text NOT NULL,
Expand Down Expand Up @@ -32,13 +33,41 @@ ALTER TABLE IF EXISTS thread
DROP CONSTRAINT IF EXISTS "thread_userId_fkey";
`

function normalizeDatabaseConnectionString(connectionString) {
const normalizedConnectionString = connectionString.trim()
if (!normalizedConnectionString) {
return normalizedConnectionString
}

let connectionUrl

try {
connectionUrl = new URL(normalizedConnectionString)
} catch {
return normalizedConnectionString
}

const useLibpqCompat = connectionUrl.searchParams.get("uselibpqcompat")
if (useLibpqCompat?.toLowerCase() === "true") {
return normalizedConnectionString
}

const sslMode = connectionUrl.searchParams.get("sslmode")?.toLowerCase()
if (!sslMode || !LEGACY_SSL_MODES.has(sslMode)) {
return normalizedConnectionString
}

connectionUrl.searchParams.set("sslmode", "verify-full")
return connectionUrl.toString()
}

if (!databaseUrl) {
console.error("Missing DATABASE_URL.")
process.exit(1)
}

const client = new Client({
connectionString: databaseUrl,
connectionString: normalizeDatabaseConnectionString(databaseUrl),
})

await client.connect()
Expand Down
Loading