30 lines
977 B
TypeScript
30 lines
977 B
TypeScript
import 'server-only'
|
|
import { sql } from 'drizzle-orm'
|
|
import { userDb, adminDb, type UserDb, type AdminDb } from './index'
|
|
|
|
export type DrizzleTransaction = Parameters<Parameters<typeof userDb.transaction>[0]>[0]
|
|
|
|
/**
|
|
* Runs fn inside a transaction with app.user_id SET LOCAL to userId.
|
|
* All RLS policies read this GUC via app_current_user_id().
|
|
* The GUC is LOCAL so it is automatically cleared when the transaction ends.
|
|
*/
|
|
export async function withUser<T>(
|
|
userId: string,
|
|
fn: (tx: DrizzleTransaction) => Promise<T>
|
|
): Promise<T> {
|
|
return userDb.transaction(async (tx) => {
|
|
await tx.execute(sql`SELECT set_config('app.user_id', ${userId}, true)`)
|
|
return fn(tx)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Runs fn against the admin (BYPASSRLS) pool.
|
|
* Use only for: admin user CRUD, service operations that legitimately bypass RLS.
|
|
* Never use for regular user data access.
|
|
*/
|
|
export async function asAdmin<T>(fn: (db: AdminDb) => Promise<T>): Promise<T> {
|
|
return fn(adminDb)
|
|
}
|