import { APIError, betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { emailOTP, username, organization } from "better-auth/plugins"; import * as schema from "../../auth-schema"; import { db } from "../db/config"; import { ac, owner, admin, member } from "../controllers/accessControl"; import { and, eq } from "drizzle-orm"; async function getActiveOrganization(userId: string) { const membership = await db.query.member.findFirst({ where: eq(schema.member.userId, userId), }); return membership?.organizationId ?? null; } export const auth: any = betterAuth({ handler: { type: "express", }, database: drizzleAdapter(db, { provider: "pg", schema: { user: schema.user, session: schema.session, account: schema.account, verification: schema.verification, organization: schema.organization, member: schema.member, invitation: schema.invitation, }, }), emailAndPassword: { enabled: true, autoSignIn: true, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, redirectURI: "http://localhost:3000/api/auth/callback/google", }, }, session: { expiresIn: 60 * 60 * 24 * 3, // 3 days (session is valid for 3 days) updateAge: 60 * 60 * 24, // 1 day (every 1 day the session expiration is updated) freshAge: 60 * 60, // 1 hour (the session is considered fresh for 1 hour after creation) cookieCache: { enabled: true, maxAge: 10 * 60, //10 minutes In seconds }, cookieOptions: { domain: process.env.NODE_ENV === "production" ? ".uncalledinnovators.com" : undefined, }, }, advanced: { useSecureCookies: true, trustedOrigins: [ "http://localhost:3000", "https://test.uncalledinnovators.com", "https://api.uncalledinnovators.com", ], }, account: { accountLinking: { enabled: true, trustedProviders: ["google"], }, }, plugins: [ username({ minUsernameLength: 3, }), emailOTP({ //maxAttempts: 5, otpLength: 4, expiresIn: 600, //seconds async sendVerificationOTP({ email, otp, type = "forget-password" }) { const { Resend } = await import("resend"); const resend = new Resend(process.env.RESEND_FG_KEY as string); await resend.emails.send({ from: "contact@uncalledinnovators.com", to: `${email}`, subject: "Reset Password for Uncalled Innovators", html: `

Your verification code is ${otp}

`, }); }, }), organization({ organizationCreation: { afterCreate: async ({ organization, user, member }, request) => { const founderMail = "founder@uncalledinnovators.com"; const founderUserQuery = await db.query.user.findFirst({ where: eq(schema.user.email, founderMail), }); if (founderUserQuery) { const founderMember = await db.query.member.findFirst({ where: and( eq(schema.member.organizationId, organization.id), eq(schema.member.userId, founderUserQuery.id) ), }); if (founderMember) { await db .update(schema.member) .set({ role: "owner" }) .where(eq(schema.member.id, founderMember.id)); } else { await db.insert(schema.member).values({ id: crypto.randomUUID(), userId: founderUserQuery.id, organizationId: organization.id, role: "owner", createdAt: new Date(), }); } } }, }, /*allowUserToCreateOrganization: async (user) => { if (user.email === "founder@uncalledinnovators.com") { return true; } else { return false; } },*/ ac, roles: { owner, admin, member, }, creatorRole: "owner", membershipLimit: 100, //Max number of users in an organization teams: { enabled: true, maximumTeams: 20, allowRemovingAllTeams: false, }, }), ], resend: { apiKey: process.env.RESEND_API_KEY, from: "contact@uncalledinnovators.com", }, // databaseHooks: { // session: { // create: { // before: async (session) => { // const organizationId = await getActiveOrganization(session.userId); // if (organizationId === null) { // throw new APIError("EXPECTATION_FAILED", { // message: "Cannot set the organization" // }) // } // return { // data: { // ...session, // activeOrganizationId: organizationId, // }, // }; // }, // }, // }, // }, });