feat: 初始化项目基础架构和核心功能
- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件 - 实现前端Vue3项目结构:路由、登录页面、设备管理页面 - 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等 - 实现客户端监控模块:系统状态收集、资产收集 - 实现服务端基础API和插件系统 - 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等 - 实现前端设备状态展示和基本交互 - 添加使用时长统计和水印功能插件
This commit is contained in:
126
web/src/lib/api.ts
Normal file
126
web/src/lib/api.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Shared API client with authentication and error handling
|
||||
*/
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE || ''
|
||||
|
||||
export interface ApiResult<T> {
|
||||
success: boolean
|
||||
data?: T
|
||||
error?: string
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
public status: number,
|
||||
public code: string,
|
||||
message: string,
|
||||
) {
|
||||
super(message)
|
||||
this.name = 'ApiError'
|
||||
}
|
||||
}
|
||||
|
||||
function getToken(): string | null {
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token || token.trim() === '') return null
|
||||
return token
|
||||
}
|
||||
|
||||
function clearAuth() {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
async function request<T>(
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<T> {
|
||||
const token = getToken()
|
||||
const headers = new Headers(options.headers || {})
|
||||
|
||||
if (token) {
|
||||
headers.set('Authorization', `Bearer ${token}`)
|
||||
}
|
||||
|
||||
if (options.body && typeof options.body === 'string') {
|
||||
headers.set('Content-Type', 'application/json')
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
...options,
|
||||
headers,
|
||||
})
|
||||
|
||||
// Handle 401 - token expired or invalid
|
||||
if (response.status === 401) {
|
||||
clearAuth()
|
||||
throw new ApiError(401, 'UNAUTHORIZED', 'Session expired')
|
||||
}
|
||||
|
||||
// Handle 403 - insufficient permissions
|
||||
if (response.status === 403) {
|
||||
throw new ApiError(403, 'FORBIDDEN', 'Insufficient permissions')
|
||||
}
|
||||
|
||||
// Handle non-JSON responses (502, 503, etc.)
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (!contentType || !contentType.includes('application/json')) {
|
||||
throw new ApiError(response.status, 'NON_JSON_RESPONSE', `Server returned ${response.status}`)
|
||||
}
|
||||
|
||||
const result: ApiResult<T> = await response.json()
|
||||
|
||||
if (!result.success) {
|
||||
throw new ApiError(response.status, 'API_ERROR', result.error || 'Unknown error')
|
||||
}
|
||||
|
||||
return result.data as T
|
||||
}
|
||||
|
||||
export const api = {
|
||||
get<T>(path: string): Promise<T> {
|
||||
return request<T>(path)
|
||||
},
|
||||
|
||||
post<T>(path: string, body?: unknown): Promise<T> {
|
||||
return request<T>(path, {
|
||||
method: 'POST',
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
},
|
||||
|
||||
put<T>(path: string, body?: unknown): Promise<T> {
|
||||
return request<T>(path, {
|
||||
method: 'PUT',
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
},
|
||||
|
||||
delete<T = void>(path: string): Promise<T> {
|
||||
return request<T>(path, { method: 'DELETE' })
|
||||
},
|
||||
|
||||
/** Login doesn't use the auth header */
|
||||
async login(username: string, password: string): Promise<{ access_token: string; refresh_token: string; user: { id: number; username: string; role: string } }> {
|
||||
const response = await fetch(`${API_BASE}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (!result.success) {
|
||||
throw new ApiError(response.status, 'LOGIN_FAILED', result.error || 'Login failed')
|
||||
}
|
||||
|
||||
localStorage.setItem('token', result.data.access_token)
|
||||
localStorage.setItem('refresh_token', result.data.refresh_token)
|
||||
return result.data
|
||||
},
|
||||
|
||||
logout() {
|
||||
clearAuth()
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user