feat(admin): 添加系统日志功能
## 新功能 1. **系统日志数据库** - 新增 system_logs 表 - 支持日志级别:debug/info/warn/error - 支持日志分类:auth/user/file/share/system/security - 记录用户ID、用户名、IP地址、User-Agent 2. **日志记录** - 用户注册成功/失败 - 用户登录成功/失败(密码错误) - 系统操作(日志清理等) 3. **管理员API** - GET /api/admin/logs - 查询日志(支持分页和筛选) - GET /api/admin/logs/stats - 获取日志统计 - POST /api/admin/logs/cleanup - 清理旧日志 4. **前端界面** - 日志列表展示(时间、级别、分类、内容、用户、IP) - 筛选功能(级别、分类、关键词搜索) - 分页导航 - 清理旧日志功能 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
137
frontend/app.js
137
frontend/app.js
@@ -157,6 +157,21 @@ createApp({
|
||||
checks: []
|
||||
},
|
||||
|
||||
// 系统日志
|
||||
systemLogs: {
|
||||
loading: false,
|
||||
logs: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 30,
|
||||
totalPages: 0,
|
||||
filters: {
|
||||
level: '',
|
||||
category: '',
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
|
||||
// Toast通知
|
||||
toasts: [],
|
||||
toastIdCounter: 0,
|
||||
@@ -2050,11 +2065,12 @@ handleDragLeave(e) {
|
||||
this.loadShares();
|
||||
break;
|
||||
case 'admin':
|
||||
// 切换到管理后台时,重新加载用户列表和健康检测
|
||||
// 切换到管理后台时,重新加载用户列表、健康检测和系统日志
|
||||
if (this.user && this.user.is_admin) {
|
||||
this.loadUsers();
|
||||
this.loadServerStorageStats();
|
||||
this.loadHealthCheck();
|
||||
this.loadSystemLogs(1);
|
||||
}
|
||||
break;
|
||||
case 'settings':
|
||||
@@ -2331,6 +2347,125 @@ handleDragLeave(e) {
|
||||
return texts[status] || '未知';
|
||||
},
|
||||
|
||||
// ===== 系统日志 =====
|
||||
|
||||
async loadSystemLogs(page = 1) {
|
||||
this.systemLogs.loading = true;
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
page: page,
|
||||
pageSize: this.systemLogs.pageSize
|
||||
});
|
||||
|
||||
if (this.systemLogs.filters.level) {
|
||||
params.append('level', this.systemLogs.filters.level);
|
||||
}
|
||||
if (this.systemLogs.filters.category) {
|
||||
params.append('category', this.systemLogs.filters.category);
|
||||
}
|
||||
if (this.systemLogs.filters.keyword) {
|
||||
params.append('keyword', this.systemLogs.filters.keyword);
|
||||
}
|
||||
|
||||
const response = await axios.get(`${this.apiBase}/api/admin/logs?${params}`, {
|
||||
headers: { Authorization: `Bearer ${this.token}` }
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
this.systemLogs.logs = response.data.logs;
|
||||
this.systemLogs.total = response.data.total;
|
||||
this.systemLogs.page = response.data.page;
|
||||
this.systemLogs.totalPages = response.data.totalPages;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系统日志失败:', error);
|
||||
this.showToast('error', '错误', '加载系统日志失败');
|
||||
} finally {
|
||||
this.systemLogs.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
filterLogs() {
|
||||
this.loadSystemLogs(1);
|
||||
},
|
||||
|
||||
clearLogFilters() {
|
||||
this.systemLogs.filters = { level: '', category: '', keyword: '' };
|
||||
this.loadSystemLogs(1);
|
||||
},
|
||||
|
||||
getLogLevelColor(level) {
|
||||
const colors = {
|
||||
debug: 'background: #6c757d; color: white;',
|
||||
info: 'background: #17a2b8; color: white;',
|
||||
warn: 'background: #ffc107; color: black;',
|
||||
error: 'background: #dc3545; color: white;'
|
||||
};
|
||||
return colors[level] || 'background: #6c757d; color: white;';
|
||||
},
|
||||
|
||||
getLogLevelText(level) {
|
||||
const texts = { debug: '调试', info: '信息', warn: '警告', error: '错误' };
|
||||
return texts[level] || level;
|
||||
},
|
||||
|
||||
getLogCategoryText(category) {
|
||||
const texts = {
|
||||
auth: '认证',
|
||||
user: '用户',
|
||||
file: '文件',
|
||||
share: '分享',
|
||||
system: '系统',
|
||||
security: '安全'
|
||||
};
|
||||
return texts[category] || category;
|
||||
},
|
||||
|
||||
getLogCategoryIcon(category) {
|
||||
const icons = {
|
||||
auth: 'fa-key',
|
||||
user: 'fa-user',
|
||||
file: 'fa-file',
|
||||
share: 'fa-share-alt',
|
||||
system: 'fa-cog',
|
||||
security: 'fa-shield-alt'
|
||||
};
|
||||
return icons[category] || 'fa-info';
|
||||
},
|
||||
|
||||
formatLogTime(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
},
|
||||
|
||||
async cleanupLogs() {
|
||||
if (!confirm('确定要清理90天前的日志吗?此操作不可恢复。')) return;
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.apiBase}/api/admin/logs/cleanup`,
|
||||
{ keepDays: 90 },
|
||||
{ headers: { Authorization: `Bearer ${this.token}` } }
|
||||
);
|
||||
|
||||
if (response.data.success) {
|
||||
this.showToast('success', '成功', response.data.message);
|
||||
this.loadSystemLogs(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('清理日志失败:', error);
|
||||
this.showToast('error', '错误', '清理日志失败');
|
||||
}
|
||||
},
|
||||
|
||||
// ===== 上传工具管理 =====
|
||||
|
||||
// 检测上传工具是否存在
|
||||
|
||||
Reference in New Issue
Block a user