diff --git a/.env.example b/.env.example index 8334b05..7558b7b 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,16 @@ -# Database +# ── Production (Coolify) ──────────────────────────────────────────────────── +# DB_PASSWORD is passed separately so special characters never need URL-encoding. +# DATABASE_URL is constructed inside docker-compose.yml and does NOT need to be +# set in Coolify — only DB_PASSWORD is required. DB_PASSWORD=changeme -DATABASE_URL=postgres://wc:changeme@worldcup_db:5432/worldcup # Traefik (set TRAEFIK_ENABLED=true when deploying behind Traefik) TRAEFIK_ENABLED=false TRAEFIK_HOST=worldcup.example.com NETWORK_NAME=traefik-network + +# ── Local development ──────────────────────────────────────────────────────── +# Set DATABASE_URL when running pnpm dev or pnpm sync on the host directly. +# The password can be plain-text here since it goes through the postgres driver, +# not URL parsing, when DB_PASSWORD is unset. +# DATABASE_URL=postgres://wc:wc@localhost:5432/worldcup diff --git a/docker-compose.yml b/docker-compose.yml index 7c0718b..956651f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,12 +2,12 @@ services: app: build: . restart: unless-stopped - container_name: worldcup depends_on: db: condition: service_healthy environment: - DATABASE_URL: postgres://wc:${DB_PASSWORD}@worldcup_db:5432/worldcup + DATABASE_URL: postgres://wc@db:5432/worldcup + DB_PASSWORD: ${DB_PASSWORD} NODE_ENV: production labels: - "traefik.enable=${TRAEFIK_ENABLED:-false}" @@ -27,7 +27,6 @@ services: db: image: postgres:16-alpine restart: unless-stopped - container_name: worldcup_db environment: POSTGRES_DB: worldcup POSTGRES_USER: wc diff --git a/lib/db/index.ts b/lib/db/index.ts index e43b958..94370e6 100644 --- a/lib/db/index.ts +++ b/lib/db/index.ts @@ -4,7 +4,12 @@ import * as schema from './schema' const connectionString = process.env.DATABASE_URL ?? 'postgres://wc:wc@localhost:5432/worldcup' -const client = postgres(connectionString, { max: 10 }) +// DB_PASSWORD is passed separately to avoid URL-encoding issues with special chars +// (e.g. #, ], = in passwords break URL parsing). Falls back to password in DATABASE_URL for local dev. +const client = postgres(connectionString, { + max: 10, + ...(process.env.DB_PASSWORD ? { password: process.env.DB_PASSWORD } : {}), +}) export const db = drizzle(client, { schema }) export * from './schema'