diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 882ac3b440..08d771c51c 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -70,7 +70,7 @@ async function getUserPageProps(context: GetStaticPropsContext) { const { type: slug, user: username } = paramsSchema.parse(context.params); const ssg = await ssgInit(context); - const user = await prisma.user.findUnique({ + const user = await prisma.user.findFirst({ where: { /** TODO: We should standarize this */ username: username.toLowerCase().replace(/( |%20)/g, "+"), diff --git a/apps/web/pages/[user]/calendar-cache/[month].tsx b/apps/web/pages/[user]/calendar-cache/[month].tsx index 9cd9359bb5..055271863d 100644 --- a/apps/web/pages/[user]/calendar-cache/[month].tsx +++ b/apps/web/pages/[user]/calendar-cache/[month].tsx @@ -18,7 +18,7 @@ export const getStaticProps: GetStaticProps< { user: string } > = async (context) => { const { user: username, month } = paramsSchema.parse(context.params); - const userWithCredentials = await prisma.user.findUnique({ + const userWithCredentials = await prisma.user.findFirst({ where: { username, }, diff --git a/apps/web/pages/api/user/avatar.ts b/apps/web/pages/api/user/avatar.ts index 27b171c2da..d7a2b8b2e6 100644 --- a/apps/web/pages/api/user/avatar.ts +++ b/apps/web/pages/api/user/avatar.ts @@ -18,7 +18,7 @@ async function getIdentityData(req: NextApiRequest) { const { username, teamname } = querySchema.parse(req.query); if (username) { - const user = await prisma.user.findUnique({ + const user = await prisma.user.findFirst({ where: { username }, select: { avatar: true, email: true }, }); diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index b5e27ee8f8..22f98fbb2e 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -69,7 +69,7 @@ const providers: Provider[] = [ throw new Error(ErrorCode.InternalServerError); } - const user = await prisma.user.findUnique({ + const user = await prisma.user.findFirst({ where: { email: credentials.email.toLowerCase(), }, @@ -605,7 +605,12 @@ export const AUTH_OPTIONS: AuthOptions = { !existingUserWithEmail.username ) { await prisma.user.update({ - where: { email: existingUserWithEmail.email }, + where: { + email_username: { + email: existingUserWithEmail.email, + username: existingUserWithEmail.username!, + }, + }, data: { // update the email to the IdP email email: user.email, diff --git a/packages/prisma/migrations/20230518173925_organizations/migration.sql b/packages/prisma/migrations/20230518173925_organizations/migration.sql new file mode 100644 index 0000000000..07594942fd --- /dev/null +++ b/packages/prisma/migrations/20230518173925_organizations/migration.sql @@ -0,0 +1,30 @@ +/* + Warnings: + + - A unique constraint covering the columns `[email,username]` on the table `users` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[username,organizationId]` on the table `users` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "users_email_idx"; + +-- DropIndex +DROP INDEX "users_username_key"; + +-- AlterTable +ALTER TABLE "Team" ADD COLUMN "parentId" INTEGER; + +-- AlterTable +ALTER TABLE "users" ADD COLUMN "organizationId" INTEGER; + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_username_key" ON "users"("email", "username"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_username_organizationId_key" ON "users"("username", "organizationId"); + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Team"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Team" ADD CONSTRAINT "Team_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index d000df881a..8e72529074 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -166,10 +166,10 @@ enum UserPermissionRole { model User { id Int @id @default(autoincrement()) - username String? @unique + username String? name String? /// @zod.email() - email String @unique + email String emailVerified DateTime? password String? bio String? @@ -225,8 +225,16 @@ model User { routingForms App_RoutingForms_Form[] @relation("routing-form") verifiedNumbers VerifiedNumber[] hosts Host[] + organizationId Int? + organization Team? @relation("scope", fields: [organizationId], references: [id], onDelete: SetNull) + // Linking account code for orgs v2 + //linkedByUserId Int? + //linkedBy User? @relation("linked_account", fields: [linkedByUserId], references: [id], onDelete: Cascade) + //linkedUsers User[] @relation("linked_account")*/ - @@index([email]) + @@unique([email]) + @@unique([email, username]) + @@unique([username, organizationId]) @@index([emailVerified]) @@index([identityProvider]) @@index([identityProviderId]) @@ -255,6 +263,10 @@ model Team { brandColor String @default("#292929") darkBrandColor String @default("#fafafa") verifiedNumbers VerifiedNumber[] + parentId Int? + parent Team? @relation("organization", fields: [parentId], references: [id], onDelete: Cascade) + children Team[] @relation("organization") + scopedMembers User[] @relation("scope") } enum MembershipRole { diff --git a/packages/prisma/seed-app-store.ts b/packages/prisma/seed-app-store.ts index 0e6fad36df..20bedbe555 100644 --- a/packages/prisma/seed-app-store.ts +++ b/packages/prisma/seed-app-store.ts @@ -142,7 +142,10 @@ async function seedAppData() { ], user: { connect: { - username: "pro", + email_username: { + username: "pro", + email: "pro@example.com", + }, }, }, name: seededForm.name, diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts index 6ebf5cec17..760e26182a 100644 --- a/packages/prisma/seed.ts +++ b/packages/prisma/seed.ts @@ -50,7 +50,7 @@ async function createUserAndEventType(opts: { }; const user = await prisma.user.upsert({ - where: { email: opts.user.email }, + where: { email_username: { email: opts.user.email, username: opts.user.username } }, update: userData, create: userData, });