first commit

This commit is contained in:
2025-07-08 00:52:10 +08:00
commit aa2416c5d6
69 changed files with 16628 additions and 0 deletions

View File

@@ -0,0 +1,387 @@
import { Request, Response } from 'express';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { prisma } from '../config/database';
import { logger } from '../utils/logger';
import { AuthRequest } from '../middleware/authMiddleware';
import type { Secret, SignOptions } from 'jsonwebtoken';
// Generate JWT token
function generateToken(userId: string): string {
const secret = process.env.JWT_SECRET;
if (!secret) {
throw new Error('JWT_SECRET is not configured');
}
const expiresIn = process.env.JWT_EXPIRES_IN || '7d';
return jwt.sign(
{ userId },
secret,
{ expiresIn: expiresIn as any }
);
}
// Create session
async function createSession(userId: string, token: string, req: Request) {
console.log('创建session:', { userId, token: token.substring(0, 20) + '...' })
const session = await prisma.session.create({
data: {
userId,
token,
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
},
});
console.log('Session创建成功:', {
sessionId: session.id,
expiresAt: session.expiresAt,
currentTime: new Date()
})
return session;
}
export const authController = {
// 用户注册
async register(req: Request, res: Response) {
const { username, password, confirmPassword, firstName, lastName } = req.body;
// 验证密码确认
if (password !== confirmPassword) {
return res.status(400).json({
error: '密码和确认密码不匹配'
});
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { username }
});
if (existingUser) {
return res.status(400).json({
error: '用户名已存在'
});
}
// Hash password
const hashedPassword = await bcrypt.hash(password, parseInt(process.env.BCRYPT_ROUNDS || '12'));
// Create user with isActive set to false by default
const user = await prisma.user.create({
data: {
username,
password: hashedPassword,
firstName,
lastName,
isActive: false, // 新注册用户默认为禁用状态
},
});
// Create audit log
await prisma.auditLog.create({
data: {
userId: user.id,
action: 'USER_REGISTERED',
resource: 'user',
resourceId: user.id,
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
return res.status(201).json({
message: '注册成功,请等待管理员激活您的账户',
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
isActive: user.isActive,
}
});
},
// 用户登录
async login(req: Request, res: Response) {
const { username, password } = req.body;
// Find user
const user = await prisma.user.findUnique({
where: { username }
});
if (!user) {
// 记录登录失败审计日志
await prisma.auditLog.create({
data: {
userId: null,
action: 'USER_LOGIN_FAILED',
resource: 'user',
resourceId: null,
details: JSON.stringify({ username, reason: '用户不存在' }),
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
return res.status(401).json({ error: '用户不存在' });
}
// Check if user is active
if (!user.isActive) {
// 记录登录失败审计日志
await prisma.auditLog.create({
data: {
userId: user.id,
action: 'USER_LOGIN_FAILED',
resource: 'user',
resourceId: user.id,
details: JSON.stringify({ username, reason: '账户已被禁用' }),
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
return res.status(401).json({ error: '账户已被禁用,请联系管理员激活' });
}
// Verify password
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
// 增加登录失败次数
const loginAttempts = (user.loginAttempts || 0) + 1;
const updateData: any = { loginAttempts };
let userDisabled = false;
// 如果失败次数达到5次禁用账户
if (loginAttempts >= 5) {
updateData.isActive = false;
userDisabled = true;
}
await prisma.user.update({
where: { id: user.id },
data: updateData
});
// 记录登录失败审计日志
await prisma.auditLog.create({
data: {
userId: user.id,
action: 'USER_LOGIN_FAILED',
resource: 'user',
resourceId: user.id,
details: JSON.stringify({
username,
reason: userDisabled ? '密码错误且账户已被禁用' : '密码错误',
loginAttempts,
isDisabled: userDisabled
}),
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
if (userDisabled) {
return res.status(401).json({ error: '登录失败次数过多,账户已被禁用,请联系管理员' });
}
return res.status(401).json({
error: '用户名或密码错误',
remainingAttempts: 5 - loginAttempts
});
}
// 登录成功,重置登录失败次数和锁定时间
await prisma.user.update({
where: { id: user.id },
data: {
loginAttempts: 0,
lockedUntil: null
}
});
// Generate token
const token = generateToken(user.id);
// Delete existing sessions for this user (optional - for single session per user)
await prisma.session.deleteMany({
where: { userId: user.id }
});
// Create session
await createSession(user.id, token, req);
// Update last login
await prisma.user.update({
where: { id: user.id },
data: { lastLoginAt: new Date() }
});
// Create audit log
await prisma.auditLog.create({
data: {
userId: user.id,
action: 'USER_LOGIN',
resource: 'user',
resourceId: user.id,
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
return res.json({
message: '登录成功',
token,
user: {
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin,
}
});
},
// 用户登出
async logout(req: AuthRequest, res: Response) {
const token = req.headers.authorization?.substring(7);
if (token) {
// Delete session
await prisma.session.deleteMany({
where: { token }
});
}
// Create audit log
if (req.user) {
await prisma.auditLog.create({
data: {
userId: req.user.id,
action: 'USER_LOGOUT',
resource: 'user',
resourceId: req.user.id,
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null,
}
});
}
res.json({ message: '登出成功' });
},
// 获取当前用户信息
async getCurrentUser(req: AuthRequest, res: Response) {
if (!req.user) {
return res.status(401).json({ error: '未授权' });
}
const user = await prisma.user.findUnique({
where: { id: req.user.id },
select: {
id: true,
username: true,
firstName: true,
lastName: true,
isAdmin: true,
isActive: true,
lastLoginAt: true,
createdAt: true,
updatedAt: true,
}
});
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
return res.json({
user: user
});
},
// 刷新token
async refreshToken(req: Request, res: Response) {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(400).json({ error: '刷新令牌是必需的' });
}
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET!) as any;
const session = await prisma.session.findFirst({
where: {
token: refreshToken,
expiresAt: {
gt: new Date()
}
},
include: {
user: {
select: {
id: true,
username: true,
isAdmin: true,
isActive: true
}
}
}
});
if (!session || !session.user || !session.user.isActive) {
return res.status(401).json({ error: '无效的刷新令牌' });
}
// Generate new token
const newToken = generateToken(session.user.id);
// Update session
await prisma.session.update({
where: { id: session.id },
data: {
token: newToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
}
});
return res.json({
token: newToken,
user: session.user
});
} catch (error) {
return res.status(401).json({ error: '无效的刷新令牌' });
}
},
// 调试端点 - 检查用户session状态
async debugSession(req: AuthRequest, res: Response) {
if (!req.user) {
return res.status(401).json({ error: '未授权' });
}
try {
const sessions = await prisma.session.findMany({
where: { userId: req.user.id },
orderBy: { createdAt: 'desc' }
});
return res.json({
userId: req.user.id,
username: req.user.username,
sessions: sessions.map((s: any) => ({
id: s.id,
token: s.token.substring(0, 20) + '...',
expiresAt: s.expiresAt,
createdAt: s.createdAt,
isExpired: s.expiresAt < new Date()
}))
});
} catch (error) {
return res.status(500).json({ error: '获取session信息失败' });
}
}
};