feat: 新增补丁管理和异常检测插件及相关功能

feat(protocol): 添加补丁管理和行为指标协议类型
feat(client): 实现补丁管理插件采集功能
feat(server): 添加补丁管理和异常检测API
feat(database): 新增补丁状态和异常检测相关表
feat(web): 添加补丁管理和异常检测前端页面
fix(security): 增强输入验证和防注入保护
refactor(auth): 重构认证检查逻辑
perf(service): 优化Windows服务恢复策略
style: 统一健康评分显示样式
docs: 更新知识库文档
This commit is contained in:
iven
2026-04-11 15:59:53 +08:00
parent b5333d8c93
commit 60ee38a3c2
49 changed files with 3988 additions and 461 deletions

View File

@@ -27,40 +27,43 @@ const router = createRouter({
{ path: 'plugins/print-audit', name: 'PrintAudit', component: () => import('../views/plugins/PrintAudit.vue') },
{ path: 'plugins/clipboard-control', name: 'ClipboardControl', component: () => import('../views/plugins/ClipboardControl.vue') },
{ path: 'plugins/plugin-control', name: 'PluginControl', component: () => import('../views/plugins/PluginControl.vue') },
{ path: 'plugins/patch', name: 'PatchManagement', component: () => import('../views/plugins/PatchManagement.vue') },
{ path: 'plugins/anomaly', name: 'AnomalyDetection', component: () => import('../views/plugins/AnomalyDetection.vue') },
],
},
],
})
/** Check if a JWT token is structurally valid and not expired */
function isTokenValid(token: string): boolean {
if (!token || token.trim() === '') return false
try {
const parts = token.split('.')
if (parts.length !== 3) return false
const payload = JSON.parse(atob(parts[1]))
if (!payload.exp) return false
// Reject if token expires within 30 seconds
return payload.exp * 1000 > Date.now() + 30_000
} catch {
return false
}
}
/** Track whether we've already validated auth this session */
let authChecked = false
router.beforeEach((to, _from, next) => {
router.beforeEach(async (to, _from, next) => {
if (to.path === '/login') {
next()
return
}
const token = localStorage.getItem('token')
if (!token || !isTokenValid(token)) {
localStorage.removeItem('token')
localStorage.removeItem('refresh_token')
next('/login')
} else {
// If we've already verified auth this session, allow navigation
// (cookies are sent automatically, no need to check on every route change)
if (authChecked) {
next()
return
}
// Check auth status via /api/auth/me (reads access_token cookie)
try {
const { me } = await import('../lib/api')
await me()
authChecked = true
next()
} catch {
next('/login')
}
})
/** Reset auth check flag (called after logout) */
export function resetAuthCheck() {
authChecked = false
}
export default router