import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { eq } from "drizzle-orm"; import bcrypt from "bcrypt"; import { Resend } from "resend"; import { db } from "~/server/db"; import * as schema from "~/database/schema"; import { generateFlagConfig } from "~/lib/flag-generator"; if (!process.env.BETTER_AUTH_SECRET) { throw new Error("BETTER_AUTH_SECRET is required"); } const resend = new Resend(process.env.RESEND_API_KEY); const googleProvider = process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET ? { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } } : {}; const discordProvider = process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET ? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } } : {}; const appBaseUrl = process.env.APP_URL ?? process.env.BETTER_AUTH_URL; export const auth = betterAuth({ baseURL: appBaseUrl, database: drizzleAdapter(db, { provider: "pg", usePlural: true, }), databaseHooks: { user: { create: { after: async (user) => { await db .update(schema.users) .set({ flagConfig: generateFlagConfig(user.id) }) .where(eq(schema.users.id, user.id)); }, }, update: { before: async (data) => { const incomingImage = "imageUrl" in data ? data.imageUrl : "image" in data ? data.image : undefined; const userId = typeof data.id === "string" ? data.id : null; if (!incomingImage || !userId) return; const existing = await db.query.users.findFirst({ where: eq(schema.users.id, userId), }); if (existing?.avatarType === "uploaded" || existing?.avatarType === "flag") { const next = { ...data }; delete next.imageUrl; delete next.image; return { data: next }; } }, }, }, }, advanced: { database: { generateId: false, }, }, user: { fields: { name: "displayName", image: "imageUrl", }, additionalFields: { firstName: { type: "string", required: false, fieldName: "firstName" }, lastName: { type: "string", required: false, fieldName: "lastName" }, username: { type: "string", required: false, fieldName: "username" }, isAdmin: { type: "boolean", defaultValue: false, fieldName: "isAdmin" }, }, }, emailAndPassword: { enabled: true, password: { hash: (password: string) => bcrypt.hash(password, 10), verify: ({ hash, password }: { hash: string; password: string }) => bcrypt.compare(password, hash), }, sendResetPassword: async ({ user, url }) => { await resend.emails.send({ from: "Brackt ", to: user.email, subject: "Reset your Brackt password", html: `

Click the link below to reset your password. This link expires in 1 hour.

${url}

`, }); }, }, socialProviders: { ...googleProvider, ...discordProvider, }, account: { accountLinking: { enabled: true, trustedProviders: ["google", "discord"], }, }, });