diff --git a/eslint.config.js b/eslint.config.js index 4c9cdec..5b2744d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -41,6 +41,15 @@ export default ts.config( caughtErrorsIgnorePattern: '^_', }, ], + 'no-restricted-syntax': [ + 'error', + { + selector: + 'CallExpression:matches([callee.object.object.name="prisma"], [callee.object.object.name="prismaTransactionClient"], [callee.object.object.name="transactionClient"]):matches([callee.property.name="findFirst"], [callee.property.name="findMany"], [callee.property.name="updateMany"], [callee.property.name="deleteMany"], [callee.property.name="count"], [callee.property.name="aggregate"], [callee.property.name="groupBy"]):not(:has(ObjectExpression > Property[key.name="where"] > ObjectExpression > Property[key.name="tenantId"]))', + message: + 'Please filter on the current tenant when using findFirst, findMany, updateMany, deleteMany, count, aggregate or groupBy.', + }, + ], }, } ); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3033568..af4a1f4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,12 +15,29 @@ datasource db { } model User { - id String @id @default(uuid()) - clerkId String @unique + id String @default(uuid()) + clerkId String + tenant Tenant @relation(fields: [tenantId], references: [id]) + tenantId String email String? name String createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@id([id, tenantId]) + @@unique([clerkId, tenantId]) +} + +model Tenant { + id String @id @default(uuid()) + name String + slug String @unique + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + clerkId String @unique + User User[] + + @@map("tenant") } diff --git a/src/lib/server/auth/index.ts b/src/lib/server/auth/index.ts index 8bc10b9..e5b5d9d 100644 --- a/src/lib/server/auth/index.ts +++ b/src/lib/server/auth/index.ts @@ -14,7 +14,7 @@ export async function validateSession({ locals }: ServerLoadEvent) { return redirect(307, '/login'); } - if (!locals.auth.orgId && locals.auth.sessionId) { + if ((!locals.auth.orgId && locals.auth.sessionId) || !locals.auth.orgId) { // Sign out the user if they are not associated with an organization await clerkSessionClient.sessions.revokeSession(locals.auth.sessionId); return redirect(307, '/login'); @@ -22,9 +22,32 @@ export async function validateSession({ locals }: ServerLoadEvent) { const clerkUser = await clerkClient.users.getUser(locals.auth.userId); + const tenantClerkId = locals.auth.orgId; + + let tenant = await prisma.tenant.findUnique({ + where: { + clerkId: tenantClerkId, + }, + }); + + if (!tenant) { + const organization = await clerkClient.organizations.getOrganization({ + organizationId: tenantClerkId, + }); + + tenant = await prisma.tenant.create({ + data: { + clerkId: tenantClerkId, + name: organization.name, + slug: organization.slug ?? `tenant-${tenantClerkId}`, + }, + }); + } + let user = await prisma.user.findFirst({ where: { clerkId: clerkUser.id, + tenantId: tenant.id, }, }); @@ -41,6 +64,7 @@ export async function validateSession({ locals }: ServerLoadEvent) { clerkId: clerkUser.id, email: clerkUser.emailAddresses[0].emailAddress, name: clerkUser.fullName ?? '', + tenantId: tenant.id, }, });