fix: 全面检查修复 — 路由顺序 + 安全加固

- logs.ts: /track/stats 和 /track/list 移到 /:date 之前,修复路由匹配问题
- config.ts: 添加配置键白名单校验,限制值长度 500 字符
- track.ts: 添加输入校验,限制事件数量和字段长度
- index.ts: /api/track 添加 rate limiter
This commit is contained in:
2026-06-08 15:27:57 +08:00
parent a1e3106899
commit fc3491742f
4 changed files with 99 additions and 86 deletions

View File

@@ -92,7 +92,87 @@ router.get('/stats', requireAdmin, (_req, res) => {
}
})
/** 查询指定日期的日志 */
/** 获取埋点统计(必须在 /:date 之前注册) */
router.get('/track/stats', requireAdmin, async (_req, res) => {
try {
// 今日统计
const [todayRows] = await pool.execute(
`SELECT event, COUNT(*) as count FROM track_events
WHERE DATE(created_at) = CURDATE()
GROUP BY event ORDER BY count DESC`
)
// 最近7天每日统计
const [dailyRows] = await pool.execute(
`SELECT DATE(created_at) as date, COUNT(*) as count FROM track_events
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(created_at) ORDER BY date DESC`
)
// 最近7天事件分布
const [eventRows] = await pool.execute(
`SELECT event, COUNT(*) as count FROM track_events
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY event ORDER BY count DESC LIMIT 10`
)
res.json({
code: 0,
data: {
today: todayRows,
daily: dailyRows,
events: eventRows
}
})
} catch (err) {
console.error('[Track] Stats error:', err)
res.status(500).json({ code: 50000, message: '获取埋点统计失败' })
}
})
/** 查询埋点事件(必须在 /:date 之前注册) */
router.get('/track/list', requireAdmin, async (req, res) => {
try {
const { event, page = '1', pageSize = '50' } = req.query
const pNum = Math.max(1, parseInt(page as string) || 1)
const pSize = Math.min(200, Math.max(1, parseInt(pageSize as string) || 50))
const offset = (pNum - 1) * pSize
let where = '1=1'
const params: any[] = []
if (event && typeof event === 'string') {
where += ' AND t.event = ?'
params.push(event)
}
const [countResult] = await pool.execute(
`SELECT COUNT(*) as total FROM track_events t WHERE ${where}`,
params
)
const total = (countResult as any[])[0].total
const [rows] = await pool.execute(
`SELECT t.*, u.nickname
FROM track_events t
LEFT JOIN users u ON t.user_id = u.id
WHERE ${where}
ORDER BY t.created_at DESC
LIMIT ? OFFSET ?`,
[...params, pSize, offset]
)
res.json({
code: 0,
data: { list: rows, total, page: pNum, pageSize: pSize }
})
} catch (err) {
console.error('[Track] List error:', err)
res.status(500).json({ code: 50000, message: '查询埋点失败' })
}
})
/** 查询指定日期的日志(必须在 /track/* 之后注册) */
router.get('/:date', requireAdmin, (req, res) => {
try {
const { date } = req.params
@@ -144,84 +224,4 @@ router.get('/:date', requireAdmin, (req, res) => {
}
})
/** 获取埋点统计 */
router.get('/track/stats', requireAdmin, async (_req, res) => {
try {
// 今日统计
const [todayRows] = await pool.execute(
`SELECT event, COUNT(*) as count FROM track_events
WHERE DATE(created_at) = CURDATE()
GROUP BY event ORDER BY count DESC`
)
// 最近7天每日统计
const [dailyRows] = await pool.execute(
`SELECT DATE(created_at) as date, COUNT(*) as count FROM track_events
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(created_at) ORDER BY date DESC`
)
// 最近7天事件分布
const [eventRows] = await pool.execute(
`SELECT event, COUNT(*) as count FROM track_events
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY event ORDER BY count DESC LIMIT 10`
)
res.json({
code: 0,
data: {
today: todayRows,
daily: dailyRows,
events: eventRows
}
})
} catch (err) {
console.error('[Track] Stats error:', err)
res.status(500).json({ code: 50000, message: '获取埋点统计失败' })
}
})
/** 查询埋点事件 */
router.get('/track/list', requireAdmin, async (req, res) => {
try {
const { event, page = '1', pageSize = '50' } = req.query
const pNum = Math.max(1, parseInt(page as string) || 1)
const pSize = Math.min(200, Math.max(1, parseInt(pageSize as string) || 50))
const offset = (pNum - 1) * pSize
let where = '1=1'
const params: any[] = []
if (event && typeof event === 'string') {
where += ' AND t.event = ?'
params.push(event)
}
const [countResult] = await pool.execute(
`SELECT COUNT(*) as total FROM track_events t WHERE ${where}`,
params
)
const total = (countResult as any[])[0].total
const [rows] = await pool.execute(
`SELECT t.*, u.nickname
FROM track_events t
LEFT JOIN users u ON t.user_id = u.id
WHERE ${where}
ORDER BY t.created_at DESC
LIMIT ? OFFSET ?`,
[...params, pSize, offset]
)
res.json({
code: 0,
data: { list: rows, total, page: pNum, pageSize: pSize }
})
} catch (err) {
console.error('[Track] List error:', err)
res.status(500).json({ code: 50000, message: '查询埋点失败' })
}
})
export default router