diff --git a/packages/lib/account/service.ts b/packages/lib/account/service.ts new file mode 100644 index 0000000000..94885be3d3 --- /dev/null +++ b/packages/lib/account/service.ts @@ -0,0 +1,23 @@ +import { prisma } from "@formbricks/database"; +import { Prisma } from "@prisma/client"; +import { DatabaseError } from "@formbricks/types/errors"; + +import { validateInputs } from "../utils/validate"; +import { ZAccountInput, TAccountInput, TAccount } from "@formbricks/types/account"; + +export const createAccount = async (accountData: TAccountInput): Promise => { + validateInputs([accountData, ZAccountInput]); + + try { + const account = await prisma.account.create({ + data: accountData, + }); + return account; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; diff --git a/packages/lib/authOptions.ts b/packages/lib/authOptions.ts index d8dc76b5fc..b75f28cffa 100644 --- a/packages/lib/authOptions.ts +++ b/packages/lib/authOptions.ts @@ -3,13 +3,17 @@ import { verifyPassword } from "@/app/lib/auth"; import { prisma } from "@formbricks/database"; import { EMAIL_VERIFICATION_DISABLED } from "./constants"; import { verifyToken } from "./jwt"; -import { getProfileByEmail, updateProfile } from "./profile/service"; +import { createProfile, getProfileByEmail, updateProfile } from "./profile/service"; import type { IdentityProvider } from "@prisma/client"; import type { NextAuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import GitHubProvider from "next-auth/providers/github"; import GoogleProvider from "next-auth/providers/google"; import AzureAD from "next-auth/providers/azure-ad"; +import { createTeam } from "./team/service"; +import { createProduct } from "./product/service"; +import { createAccount } from "./account/service"; +import { createMembership } from "./membership/service"; export const authOptions: NextAuthOptions = { providers: [ @@ -205,120 +209,21 @@ export const authOptions: NextAuthOptions = { return "/auth/login?error=A%20user%20with%20this%20email%20exists%20already."; } - await prisma.user.create({ - data: { - name: user.name, - email: user.email, - emailVerified: new Date(Date.now()), - onboardingCompleted: false, - identityProvider: provider, - identityProviderAccountId: user.id as string, - accounts: { - create: [{ ...account }], - }, - memberships: { - create: [ - { - accepted: true, - role: "owner", - // @ts-ignore - team: { - create: { - name: `${user.name}'s Team`, - products: { - create: [ - { - name: "My Product", - environments: { - create: [ - { - type: "production", - actionClasses: { - create: [ - { - name: "New Session", - description: "Gets fired when a new session is created", - type: "automatic", - }, - { - name: "Exit Intent (Desktop)", - description: "A user on Desktop leaves the website with the cursor.", - type: "automatic", - }, - { - name: "50% Scroll", - description: "A user scrolled 50% of the current page", - type: "automatic", - }, - ], - }, - attributeClasses: { - create: [ - { - name: "userId", - description: "The internal ID of the person", - type: "automatic", - }, - { - name: "email", - description: "The email of the person", - type: "automatic", - }, - ], - }, - }, - { - type: "development", - actionClasses: { - create: [ - { - name: "New Session", - description: "Gets fired when a new session is created", - type: "automatic", - }, - { - name: "Exit Intent (Desktop)", - description: "A user on Desktop leaves the website with the cursor.", - type: "automatic", - }, - { - name: "50% Scroll", - description: "A user scrolled 50% of the current page", - type: "automatic", - }, - ], - }, - attributeClasses: { - create: [ - { - name: "userId", - description: "The internal ID of the person", - type: "automatic", - }, - { - name: "email", - description: "The email of the person", - type: "automatic", - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - }, - }, - ], - }, - }, - include: { - memberships: true, - }, + const userProfile = await createProfile({ + name: user.name, + email: user.email, + emailVerified: new Date(Date.now()), + onboardingCompleted: false, + identityProvider: provider, + identityProviderAccountId: account.providerAccountId, }); - + const team = await createTeam({ name: userProfile.name + "'s Team" }); + await createAccount({ + ...account, + userId: userProfile.id, + }); + await createProduct(team.id, { name: "My Product" }); + await createMembership(team.id, userProfile.id, { role: "owner", accepted: true }); return true; } diff --git a/packages/types/account.ts b/packages/types/account.ts new file mode 100644 index 0000000000..6b7bce8df6 --- /dev/null +++ b/packages/types/account.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; + +export const ZAccountInput = z.object({ + userId: z.string(), + type: z.string(), + provider: z.string(), + providerAccountId: z.string(), + access_token: z.string().nullish(), + refresh_token: z.string().nullish(), + expires_at: z.number().nullish(), + scope: z.string().nullish(), + token_type: z.string().nullish(), + id_token: z.string().nullish(), +}); + +export type TAccountInput = z.infer; + +export const ZAccount = z.object({ + id: z.string(), + createdAt: z.date(), + updatedAt: z.date(), + userId: z.string(), + type: z.string(), + provider: z.string(), + providerAccountId: z.string(), + access_token: z.string().nullable(), + refresh_token: z.string().nullable().optional(), + expires_at: z.number().nullable(), + scope: z.string().nullable(), + token_type: z.string().nullable(), + id_token: z.string().nullable(), +}); + +export type TAccount = z.infer; diff --git a/packages/types/profile.ts b/packages/types/profile.ts index 36c3093a09..e2a3412d19 100644 --- a/packages/types/profile.ts +++ b/packages/types/profile.ts @@ -48,6 +48,8 @@ export const ZProfileCreateInput = z.object({ onboardingCompleted: z.boolean().optional(), role: ZRole.optional(), objective: ZProfileObjective.nullish(), + identityProvider: z.enum(["email", "google", "github", "azuread"]).optional(), + identityProviderAccountId: z.string().optional(), }); export type TProfileCreateInput = z.infer;