flnxi-Wu/lib/auth.ts
WuYongJie d1c6efc79d
Some checks failed
CI Check / check (push) Has been cancelled
feat: NAS 部署与 Harbor 推送流程
2025-12-03 14:39:06 +08:00

117 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextAuthOptions } from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
throw new Error('请输入邮箱和密码')
}
const user = await prisma.user.findUnique({
where: { email: credentials.email },
})
if (!user) {
throw new Error('用户不存在')
}
const isPasswordValid = await bcrypt.compare(
credentials.password,
user.password
)
if (!isPasswordValid) {
throw new Error('密码错误')
}
// 检查用户是否已激活(如果启用了激活功能且用户不是管理员或超级管理员)
const settings = await prisma.systemSettings.findFirst()
if (settings?.enableUserActivation && !user.isActive && user.role !== 'ADMIN' && !(user as any).isSuperAdmin) {
throw new Error('账号未激活,请联系管理员')
}
return {
id: user.id,
email: user.email,
name: user.username,
role: user.role as "ADMIN" | "USER",
isSuperAdmin: (user as any).isSuperAdmin,
avatar: user.avatar,
}
},
}),
],
callbacks: {
async jwt({ token, user, trigger }) {
// 登录时,从 user 对象获取信息
if (user) {
token.role = (user as any).role;
token.id = user.id;
(token as any).isSuperAdmin = (user as any).isSuperAdmin || false;
(token as any).avatar = (user as any).avatar;
(token as any).avatarUpdatedAt = Date.now();
}
// 如果 token 中有 id,定期从数据库刷新用户信息(包括头像)
// 每5分钟刷新一次,或者如果 trigger 是 'update'(手动刷新)
if (token.id && typeof token.id === 'string') {
const avatarUpdatedAt = (token as any).avatarUpdatedAt || 0;
const now = Date.now();
const fiveMinutes = 5 * 60 * 1000;
// 如果超过5分钟未刷新,或者手动触发更新,则从数据库获取最新信息
if (trigger === 'update' || (now - avatarUpdatedAt) > fiveMinutes) {
try {
const dbUser = await prisma.user.findUnique({
where: { id: token.id as string },
select: {
avatar: true,
role: true,
isSuperAdmin: true,
},
});
if (dbUser) {
(token as any).avatar = dbUser.avatar;
(token as any).role = dbUser.role;
(token as any).isSuperAdmin = (dbUser as any).isSuperAdmin || false;
(token as any).avatarUpdatedAt = now;
}
} catch (error) {
// 如果查询失败,继续使用 token 中的旧值
console.error('[Auth] Failed to refresh user info:', error);
}
}
}
return token;
},
async session({ session, token }) {
if (session?.user && token) {
(session.user as any).role = token.role || 'USER';
(session.user as any).id = token.id;
(session.user as any).isSuperAdmin = (token as any).isSuperAdmin || false;
(session.user as any).avatar = (token as any).avatar;
}
return session;
},
},
pages: {
signIn: '/login',
signOut: '/login',
},
session: {
strategy: 'jwt',
},
secret: process.env.NEXTAUTH_SECRET,
}