42 lines
1017 B
TypeScript
42 lines
1017 B
TypeScript
|
|
/**
|
||
|
|
* Shared withTenant() RLS helper.
|
||
|
|
* Copy into each product's src/data/db.ts or import from shared.
|
||
|
|
*
|
||
|
|
* CRITICAL (BMad must-have): Always RESET app.tenant_id in finally block
|
||
|
|
* to prevent connection pool tenant context leakage.
|
||
|
|
*/
|
||
|
|
import pg from 'pg';
|
||
|
|
|
||
|
|
export async function withTenant<T>(
|
||
|
|
pool: pg.Pool,
|
||
|
|
tenantId: string,
|
||
|
|
fn: (client: pg.PoolClient) => Promise<T>,
|
||
|
|
): Promise<T> {
|
||
|
|
const client = await pool.connect();
|
||
|
|
try {
|
||
|
|
await client.query('BEGIN');
|
||
|
|
await client.query(`SET LOCAL app.tenant_id = '${tenantId}'`);
|
||
|
|
const result = await fn(client);
|
||
|
|
await client.query('COMMIT');
|
||
|
|
return result;
|
||
|
|
} catch (err) {
|
||
|
|
await client.query('ROLLBACK');
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
await client.query('RESET app.tenant_id');
|
||
|
|
client.release();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Health check query — verifies DB connectivity.
|
||
|
|
*/
|
||
|
|
export async function healthCheck(pool: pg.Pool): Promise<boolean> {
|
||
|
|
try {
|
||
|
|
await pool.query('SELECT 1');
|
||
|
|
return true;
|
||
|
|
} catch {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|