diff --git a/db/client.ts b/db/client.ts index 950d9af..f82f980 100644 --- a/db/client.ts +++ b/db/client.ts @@ -23,38 +23,22 @@ */ import "server-only"; -import { Pool, type PoolClient } from "pg"; import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres"; import * as schema from "./schema"; +import { getPool, withTx } from "@/lib/db"; type Schema = typeof schema; export type Db = NodePgDatabase; -let _pool: Pool | null = null; - -function getPool(): Pool { - if (_pool) return _pool; - const connectionString = process.env.DATABASE_URL; - if (!connectionString) { - throw new Error( - "DATABASE_URL is not set. Add it to .env.local (see .env.example).", - ); - } - _pool = new Pool({ - connectionString, - max: parseInt(process.env.PG_POOL_MAX ?? "10", 10), - idleTimeoutMillis: parseInt(process.env.PG_POOL_IDLE_MS ?? "30000", 10), - connectionTimeoutMillis: parseInt( - process.env.PG_POOL_CONN_TIMEOUT_MS ?? "10000", - 10, - ), - allowExitOnIdle: false, - }); - _pool.on("error", (err) => { - console.error("[db] idle client error", err); - }); - return _pool; -} +/** + * The Drizzle layer reuses the shared `pg` `Pool` from `@/lib/db` so the + * app connects to Postgres through a single pool. Connection settings + * (`PG_POOL_MAX`, `PG_POOL_IDLE_MS`, `PG_POOL_CONN_TIMEOUT_MS`, + * `DATABASE_URL`) live in one place. See `src/lib/db.ts` for the config. + * + * The `withBrand` and `withPlatformAdmin` helpers reuse `withTx` from + * `@/lib/db` so the BEGIN/COMMIT/ROLLBACK handling has a single home. + */ /** * Run `fn` with a Drizzle client. No brand context is set — the caller @@ -83,7 +67,7 @@ export async function withBrand( brandId: string, fn: (db: Db) => Promise, ): Promise { - return runInTransaction(async (client) => { + return withTx(async (client) => { // set_config(setting, value, is_local) — is_local=true makes it // transaction-local so it auto-resets at COMMIT/ROLLBACK and never // leaks across pooled connections. @@ -103,7 +87,7 @@ export async function withBrand( export async function withPlatformAdmin( fn: (db: Db) => Promise, ): Promise { - return runInTransaction(async (client) => { + return withTx(async (client) => { await client.query("SELECT set_config('app.current_brand_id', '', true)"); await client.query("SELECT set_config('app.platform_admin', 'true', true)"); const db = drizzle(client, { schema }); @@ -111,25 +95,4 @@ export async function withPlatformAdmin( }); } -async function runInTransaction( - fn: (client: PoolClient) => Promise, -): Promise { - const client = await getPool().connect(); - try { - await client.query("BEGIN"); - const result = await fn(client); - await client.query("COMMIT"); - return result; - } catch (err) { - try { - await client.query("ROLLBACK"); - } catch { - // ignore secondary rollback failure - } - throw err; - } finally { - client.release(); - } -} - export { schema };