first commit
This commit is contained in:
222
backend/src/controllers/adminController.ts
Normal file
222
backend/src/controllers/adminController.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
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'
|
||||
},
|
||||
process.env.JWT_SECRET!,
|
||||
{ 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: '获取最近活动失败'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user