feat: 全面系统检查与修复 — 51项问题修复
## 重大修复 ### 安全性修复 - 修复 notification read-all SQL 运算符优先级 bug - 修复 link_url XSS 漏洞(验证 URL 协议) - 修复 LIKE 通配符注入(转义 % 和 _) - 修复 uploadImageIfNeeded 运算符优先级 bug - 401 时清理所有本地存储(token/nickname/avatar/role/group) ### 数据完整性修复 - 修复公告已读状态共享问题(新建 notification_reads 表) - 修复群组账单数据缺失(退出群组时保留 group_id) - 修复群组解散后邀请码仍可加入 - 修复分类迁移未校验目标类型 - 修复群组公告缺少 group_id 必填校验 ### 功能修复 - 修复 category PUT /sort 路由冲突 - 修复 GROUP BY 不完整问题 - 修复 budget API 类型不匹配 - 修复 categoryStore.migrateCategory 不刷新本地数据 - 修复 groupStore 并发请求问题 - 修复账单页覆盖 store 数据 - 修复群组预算查询返回 0 而非 null - 修复通知页面 onShow 不刷新列表 - 修复统计页面不必要的重复请求 ### 用户体验优化 - 添加通知详情查看功能(弹窗) - 添加通知图片服务器上传 - 添加 Markdown 富文本工具栏 - 添加管理页面客户端认证检查 - 添加管理员公告页面下拉刷新 - 添加数据导出进度反馈 - 添加账单删除后筛选金额更新 - Numpad 添加安全区域 padding ### 代码质量提升 - 提取 requireAdmin 为共享中间件 - filter-panel 使用设计 token - 修复 getCurrentMonth 时区不一致 - 备份功能使用分页查询避免内存问题 - 管理后台仪表盘添加缓存 - 邀请码碰撞重试后报错 ## 新增文件 - server/src/middleware/requireAdmin.ts — 共享管理员权限中间件 - client/src/pages/admin/notifications/index.vue — 公告管理页面 ## 数据库变更 - 新增 notification_reads 表(公告已读记录) - 群组解散时保留 groups 记录和 transactions.group_id
This commit is contained in:
@@ -1,29 +1,18 @@
|
||||
import { Router, Response } from 'express'
|
||||
import { AuthRequest } from '../middleware/auth'
|
||||
import { backupDatabase, getBackupList } from '../utils/backup'
|
||||
import { requireAdmin } from '../middleware/requireAdmin'
|
||||
|
||||
const router = Router()
|
||||
|
||||
// 管理员用户 ID 列表(逗号分隔),未配置时默认仅 ID=1
|
||||
const ADMIN_IDS = process.env.ADMIN_USER_IDS
|
||||
? process.env.ADMIN_USER_IDS.split(',').map(Number)
|
||||
: [1]
|
||||
|
||||
/** 检查是否为管理员,非管理员返回 403 */
|
||||
function requireAdmin(req: AuthRequest, res: Response): boolean {
|
||||
if (!req.userId || !ADMIN_IDS.includes(req.userId)) {
|
||||
res.status(403).json({ code: 40300, message: '无权限' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 所有路由都需要管理员权限
|
||||
router.use((req, res, next) => requireAdmin(req, res, next))
|
||||
|
||||
// 手动触发备份
|
||||
router.post('/', async (req: AuthRequest, res: Response) => {
|
||||
if (!requireAdmin(req, res)) return
|
||||
try {
|
||||
const filepath = await backupDatabase()
|
||||
res.json({ code: 0, data: { filepath } })
|
||||
await backupDatabase()
|
||||
res.json({ code: 0, message: '备份成功' })
|
||||
} catch (err) {
|
||||
console.error('[Backup] 手动备份失败:', err)
|
||||
res.status(500).json({ code: 50000, message: '备份失败' })
|
||||
@@ -32,7 +21,6 @@ router.post('/', async (req: AuthRequest, res: Response) => {
|
||||
|
||||
// 获取备份列表
|
||||
router.get('/', async (req: AuthRequest, res: Response) => {
|
||||
if (!requireAdmin(req, res)) return
|
||||
try {
|
||||
const list = getBackupList()
|
||||
res.json({ code: 0, data: list })
|
||||
|
||||
Reference in New Issue
Block a user