Files
lms-sb/src/lib/auth.ts
T
admins 7bf3935c81 Add freemium variant C: archetype persistence, dashboard banner, lesson gate
- User.archetype/utmSource/utmMedium/utmCampaign fields + migration
- register flow reads ?archetype/?utm_* and saves them on the User row
- dashboard ArchetypeBanner: per-archetype 'твой путь' block
- lesson gate after wow-moment lesson (WOW_MOMENT_LESSON_ID=obs-start-l2-006) → tripwire + full-course offer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:26:34 +05:00

85 lines
2.7 KiB
TypeScript

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 },
// Эндпоинты, отправляющие письма на произвольный адрес: глобальный
// лимит 100/мин позволял абузить почтовую репутацию домена.
"/send-verification-email": { window: 60, max: 3 },
"/forget-password": { window: 60, max: 3 },
},
},
trustedOrigins: [
process.env.BETTER_AUTH_URL ?? "http://localhost:3000",
"https://school.second-brain.ru",
],
user: {
additionalFields: {
role: {
type: "string",
defaultValue: "student",
input: false,
},
archetype: {
type: "string",
required: false,
input: false,
},
},
},
});
export type Session = typeof auth.$Infer.Session;
export type User = typeof auth.$Infer.Session.user;