feat: 初始化项目基础架构和核心功能

- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件
- 实现前端Vue3项目结构:路由、登录页面、设备管理页面
- 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等
- 实现客户端监控模块:系统状态收集、资产收集
- 实现服务端基础API和插件系统
- 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等
- 实现前端设备状态展示和基本交互
- 添加使用时长统计和水印功能插件
This commit is contained in:
iven
2026-04-05 00:57:51 +08:00
commit fd6fb5cca0
87 changed files with 19576 additions and 0 deletions

63
web/src/router/index.ts Normal file
View File

@@ -0,0 +1,63 @@
import { createRouter, createWebHistory } from 'vue-router'
import AppLayout from '../views/Layout.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/login', name: 'Login', component: () => import('../views/Login.vue') },
{
path: '/',
component: AppLayout,
redirect: '/dashboard',
children: [
{ path: 'dashboard', name: 'Dashboard', component: () => import('../views/Dashboard.vue') },
{ path: 'devices', name: 'Devices', component: () => import('../views/Devices.vue') },
{ path: 'devices/:uid', name: 'DeviceDetail', component: () => import('../views/DeviceDetail.vue') },
{ path: 'assets', name: 'Assets', component: () => import('../views/Assets.vue') },
{ path: 'usb', name: 'UsbPolicy', component: () => import('../views/UsbPolicy.vue') },
{ path: 'alerts', name: 'Alerts', component: () => import('../views/Alerts.vue') },
{ path: 'settings', name: 'Settings', component: () => import('../views/Settings.vue') },
// Phase 2: Plugin pages
{ path: 'plugins/web-filter', name: 'WebFilter', component: () => import('../views/plugins/WebFilter.vue') },
{ path: 'plugins/usage-timer', name: 'UsageTimer', component: () => import('../views/plugins/UsageTimer.vue') },
{ path: 'plugins/software-blocker', name: 'SoftwareBlocker', component: () => import('../views/plugins/SoftwareBlocker.vue') },
{ path: 'plugins/popup-blocker', name: 'PopupBlocker', component: () => import('../views/plugins/PopupBlocker.vue') },
{ path: 'plugins/usb-file-audit', name: 'UsbFileAudit', component: () => import('../views/plugins/UsbFileAudit.vue') },
{ path: 'plugins/watermark', name: 'Watermark', component: () => import('../views/plugins/Watermark.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
}
}
router.beforeEach((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 {
next()
}
})
export default router