import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { admin } from "better-auth/plugins"; import { prisma } from "./prisma"; import bcrypt from "bcryptjs"; import { sendWelcomeEmail, sendPasswordResetEmail, sendVerificationEmail } from "./email"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, requireEmailVerification: true, password: { hash: (password) => bcrypt.hash(password, 10), verify: ({ hash, password }) => bcrypt.compare(password, hash), }, sendResetPassword: async ({ user, url }) => { await sendPasswordResetEmail(user.email, user.name, url); }, }, emailVerification: { // Без этого блока requireEmailVerification блокирует вход, // а письмо подтверждения никто не отправляет — тупик для новых юзеров. sendVerificationEmail: async ({ user, url }) => { await sendVerificationEmail(user.email, user.name, url); }, sendOnSignUp: true, autoSignInAfterVerification: true, expiresIn: 60 * 60 * 24, // 24h }, databaseHooks: { user: { create: { after: async (user) => { await sendWelcomeEmail(user.email, user.name); }, }, }, }, plugins: [ admin({ defaultRole: "student", adminRoles: ["admin"], }), ], rateLimit: { enabled: true, window: 60, max: 100, customRules: { // Дефолт Better Auth (3/60с) слишком жёсткий для миграции: // массовый сброс паролей + общие IP (офис/NAT). 10/мин/IP — защита // от брутфорса сохраняется, но терпимо к повторам и опечаткам. "/sign-in/email": { window: 60, max: 10 }, }, }, trustedOrigins: [ process.env.BETTER_AUTH_URL ?? "http://localhost:3000", "https://school.second-brain.ru", ], user: { additionalFields: { role: { type: "string", defaultValue: "student", input: false, }, }, }, }); export type Session = typeof auth.$Infer.Session; export type User = typeof auth.$Infer.Session.user;