Update README.md #51

Merged
BenjaminPalko merged 5 commits from BenjaminPalko-patch-1 into master 2025-01-16 21:34:13 -05:00
5 changed files with 92 additions and 3 deletions
Showing only changes of commit ece86700a6 - Show all commits

View file

@ -41,6 +41,15 @@ export default ts.config(
caughtErrorsIgnorePattern: '^_', 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.',
},
],
}, },
} }
); );

View file

@ -0,0 +1,39 @@
/*
Warnings:
- The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint.
- A unique constraint covering the columns `[clerkId,tenantId]` on the table `User` will be added. If there are existing duplicate values, this will fail.
- Added the required column `tenantId` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "User_clerkId_key";
-- AlterTable
ALTER TABLE "User" DROP CONSTRAINT "User_pkey",
ADD COLUMN "tenantId" TEXT NOT NULL,
ADD CONSTRAINT "User_pkey" PRIMARY KEY ("id", "tenantId");
-- CreateTable
CREATE TABLE "Tenant" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"clerkOrganizationId" TEXT NOT NULL,
CONSTRAINT "Tenant_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Tenant_slug_key" ON "Tenant"("slug");
-- CreateIndex
CREATE UNIQUE INDEX "Tenant_clerkOrganizationId_key" ON "Tenant"("clerkOrganizationId");
-- CreateIndex
CREATE UNIQUE INDEX "User_clerkId_tenantId_key" ON "User"("clerkId", "tenantId");
-- AddForeignKey
ALTER TABLE "User" ADD CONSTRAINT "User_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -15,12 +15,27 @@ datasource db {
} }
model User { model User {
id String @id @default(uuid()) id String @default(uuid())
clerkId String @unique clerkId String
tenant Tenant @relation(fields: [tenantId], references: [id])
tenantId String
email String? email String?
name String name String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt 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())
updatedAt DateTime @updatedAt
clerkOrganizationId String @unique
users User[]
} }

View file

@ -14,7 +14,7 @@ export async function validateSession({ locals }: ServerLoadEvent) {
return redirect(307, '/login'); 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 // Sign out the user if they are not associated with an organization
await clerkSessionClient.sessions.revokeSession(locals.auth.sessionId); await clerkSessionClient.sessions.revokeSession(locals.auth.sessionId);
return redirect(307, '/login'); return redirect(307, '/login');
@ -22,9 +22,32 @@ export async function validateSession({ locals }: ServerLoadEvent) {
const clerkUser = await clerkClient.users.getUser(locals.auth.userId); const clerkUser = await clerkClient.users.getUser(locals.auth.userId);
const tenantClerkId = locals.auth.orgId;
let tenant = await prisma.tenant.findUnique({
where: {
clerkOrganizationId: tenantClerkId,
},
});
if (!tenant) {
const organization = await clerkClient.organizations.getOrganization({
organizationId: tenantClerkId,
});
tenant = await prisma.tenant.create({
data: {
clerkOrganizationId: tenantClerkId,
name: organization.name,
slug: organization.slug ?? `tenant-${tenantClerkId}`,
},
});
}
let user = await prisma.user.findFirst({ let user = await prisma.user.findFirst({
where: { where: {
clerkId: clerkUser.id, clerkId: clerkUser.id,
tenantId: tenant.id,
}, },
}); });
@ -41,6 +64,7 @@ export async function validateSession({ locals }: ServerLoadEvent) {
clerkId: clerkUser.id, clerkId: clerkUser.id,
email: clerkUser.emailAddresses[0].emailAddress, email: clerkUser.emailAddresses[0].emailAddress,
name: clerkUser.fullName ?? '', name: clerkUser.fullName ?? '',
tenantId: tenant.id,
}, },
}); });

View file

@ -19,6 +19,8 @@ builder.queryFields((t) => ({
users: t.prismaField({ users: t.prismaField({
type: [User], type: [User],
resolve: async () => { resolve: async () => {
// TODO: Fix this when we add a tenant context
// eslint-disable-next-line no-restricted-syntax
return await prisma.user.findMany(); return await prisma.user.findMany();
}, },
}), }),