File size: 2,605 Bytes
e289c5c 192979e e289c5c ffddff2 e289c5c ffddff2 cfbb685 ffddff2 e289c5c 6dd9bad e289c5c 7b0c22b e289c5c 7b0c22b e289c5c 7b0c22b e289c5c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | import { PrismaClient } from '@prisma/client';
import { getOrganizationId } from './context';
export const createTenantExtension = (explicitOrganizationId?: string) => {
return {
query: {
$allModels: {
async $allOperations({ model, operation, args, query }: any) {
const organizationId = explicitOrganizationId || getOrganizationId();
// 🚨 EXCEPTION: Certain models should never be filtered by organizationId
const EXCLUDED_MODELS = ['Organization', 'TrainingData', 'NormalizationRule'];
if (EXCLUDED_MODELS.includes(model) || !organizationId) {
return query(args);
}
// List of operations where we want to enforce organizationId
const filteredOperations = [
'findMany',
'findFirst',
'count',
'updateMany',
'deleteMany',
'aggregate',
'groupBy'
];
if (filteredOperations.includes(operation)) {
args.where = { ...args.where, organizationId };
}
// Special handling for create: automatically inject organizationId
if (operation === 'create') {
args.data = { ...args.data, organizationId };
}
// Special handling for upsert: scope where, and inject in create/update
if (operation === 'upsert') {
args.where = { ...args.where, organizationId };
args.create = { ...args.create, organizationId };
args.update = { ...args.update, organizationId };
}
// For update/delete/findUnique
if (['update', 'delete', 'findUnique'].includes(operation)) {
args.where = { ...args.where, organizationId };
}
return query(args);
},
},
},
};
};
/**
* TenantClient exposes all standard PrismaClient delegates plus $forOrganization.
* The double $extends() in the implementation produces a type that TypeScript cannot
* easily infer, so we cast explicitly to this alias so callers don't need `as any`.
*/
export type TenantClient = PrismaClient & {
$forOrganization(organizationId: string): PrismaClient;
};
/**
* Extends a PrismaClient with a helper to get a tenant-bound client
*/
export function withTenantIsolation(prisma: PrismaClient): TenantClient {
return prisma.$extends({
client: {
$forOrganization(organizationId: string) {
return prisma.$extends(createTenantExtension(organizationId));
}
}
}).$extends(createTenantExtension()) as unknown as TenantClient;
}
|