Files
ai/backend/src/controllers/adminController.ts
2025-07-08 19:03:34 +08:00

222 lines
5.9 KiB
TypeScript

import { Request, Response } from 'express';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { prisma } from '../config/database';
import { logger } from '../utils/logger';
import { createAuditLog } from '../utils/audit';
export const adminController = {
// 管理员登录
async login(req: Request, res: Response): Promise<void> {
try {
const { username, password } = req.body;
// 验证管理员凭据
const admin = await prisma.user.findFirst({
where: {
username,
isAdmin: true
}
});
if (!admin) {
// 记录管理员登录失败审计日志
await createAuditLog({
userId: null,
action: 'ADMIN_LOGIN_FAILED',
resource: 'ADMIN',
details: JSON.stringify({ username, reason: '管理员不存在' }),
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null
});
res.status(401).json({
success: false,
message: '管理员凭据无效'
});
return;
}
// 验证密码
const isValidPassword = await bcrypt.compare(password, admin.password);
if (!isValidPassword) {
// 记录管理员登录失败审计日志
await createAuditLog({
userId: admin.id,
action: 'ADMIN_LOGIN_FAILED',
resource: 'ADMIN',
details: JSON.stringify({ username, reason: '密码错误' }),
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null
});
res.status(401).json({
success: false,
message: '管理员凭据无效'
});
return;
}
// 生成JWT token
const token = jwt.sign(
{
userId: admin.id,
username: admin.username,
role: admin.isAdmin ? 'admin' : 'user'
},
"pandora",
{ expiresIn: '24h' }
);
// 创建审计日志
await createAuditLog({
userId: admin.id,
action: 'ADMIN_LOGIN',
resource: 'ADMIN',
details: { username: admin.username },
ipAddress: req.ip ?? null,
userAgent: req.get('User-Agent') ?? null
});
res.json({
success: true,
message: '管理员登录成功',
token,
admin: {
id: admin.id,
username: admin.username,
role: admin.isAdmin ? 'admin' : 'user'
}
});
} catch (error) {
logger.error('Admin login error:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
},
// 获取统计数据
async getStats(req: Request, res: Response): Promise<void> {
try {
// 获取用户总数
const totalUsers = await prisma.user.count({
where: { isAdmin: false }
});
// 获取账号总数
const totalAccounts = await prisma.websiteAccount.count();
// 获取今日访问数(基于会话)
const today = new Date();
today.setHours(0, 0, 0, 0);
const todayVisits = await prisma.session.count({
where: {
createdAt: {
gte: today
}
}
});
// 获取系统告警数(基于审计日志中的错误)
const alerts = await prisma.auditLog.count({
where: {
action: { contains: 'ERROR' },
createdAt: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // 最近24小时
}
}
});
console.log('统计数据:', { totalUsers, totalAccounts, todayVisits, alerts });
res.json({
success: true,
data: {
totalUsers,
totalAccounts,
todayVisits,
alerts
}
});
} catch (error) {
logger.error('Get stats error:', error);
res.status(500).json({
success: false,
message: '获取统计数据失败'
});
}
},
// 获取最近活动
async getRecentActivities(req: Request, res: Response): Promise<void> {
try {
const activities = await prisma.auditLog.findMany({
take: 10,
orderBy: {
createdAt: 'desc'
},
include: {
user: {
select: {
id: true,
username: true
}
}
}
});
const formattedActivities = activities.map((activity: any) => {
let description = '';
if (activity.action === 'USER_LOGIN_FAILED' || activity.action === 'ADMIN_LOGIN_FAILED') {
// 解析失败原因
let reason = '登录失败';
let username = '未知用户';
if (activity.details) {
try {
const details = JSON.parse(activity.details);
reason = details.reason || '登录失败';
username = details.username || '未知用户';
} catch (e) {
console.error('解析活动详情失败:', e);
}
}
description = `${username} ${reason}`;
} else if (activity.user) {
description = `${activity.user.username} ${activity.action}`;
} else {
description = `系统 ${activity.action}`;
}
return {
id: activity.id,
description,
time: activity.createdAt,
details: activity.details,
ipAddress: activity.ipAddress,
userAgent: activity.userAgent,
action: activity.action
};
});
console.log('最近活动:', formattedActivities);
res.json({
success: true,
data: {
activities: formattedActivities
}
});
} catch (error) {
logger.error('Get recent activities error:', error);
res.status(500).json({
success: false,
message: '获取最近活动失败'
});
}
}
};