refactor(frontend): move views into app and pages structure
Reorganize the frontend around app-level routing and page modules so the runtime and feature screens share a clearer navigation and composition layout for future work. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
import api from './index'
|
||||
|
||||
export interface MessageAttachment {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
size: number
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
@@ -7,6 +14,7 @@ export interface Message {
|
||||
model?: string
|
||||
tokens_used?: number
|
||||
created_at: string
|
||||
attachments?: MessageAttachment[]
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
|
||||
@@ -1,27 +1,81 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000',
|
||||
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:9527',
|
||||
timeout: 30000,
|
||||
})
|
||||
|
||||
function createRequestId() {
|
||||
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
return `req-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||
}
|
||||
|
||||
function isDev() {
|
||||
return Boolean(import.meta.env.DEV)
|
||||
}
|
||||
|
||||
function debugLog(stage: string, payload: Record<string, unknown>) {
|
||||
if (!isDev()) return
|
||||
console.debug(`[api:${stage}]`, payload)
|
||||
}
|
||||
|
||||
// 请求拦截器:添加 Token
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
const requestId = createRequestId()
|
||||
config.headers = config.headers || {}
|
||||
config.headers['X-Request-ID'] = requestId
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
;(config as typeof config & { metadata?: { startedAt: number; requestId: string } }).metadata = {
|
||||
startedAt: Date.now(),
|
||||
requestId,
|
||||
}
|
||||
debugLog('request', {
|
||||
requestId,
|
||||
method: config.method,
|
||||
url: config.url,
|
||||
params: config.params,
|
||||
data: config.data,
|
||||
})
|
||||
return config
|
||||
})
|
||||
|
||||
// 响应拦截器:处理错误
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(response) => {
|
||||
const metadata = (response.config as typeof response.config & { metadata?: { startedAt: number; requestId: string } }).metadata
|
||||
debugLog('response', {
|
||||
requestId: response.headers['x-request-id'] || metadata?.requestId,
|
||||
method: response.config.method,
|
||||
url: response.config.url,
|
||||
status: response.status,
|
||||
durationMs: metadata ? Date.now() - metadata.startedAt : undefined,
|
||||
data: response.data,
|
||||
})
|
||||
return response
|
||||
},
|
||||
async (error) => {
|
||||
const metadata = (error.config as typeof error.config & { metadata?: { startedAt: number; requestId: string } })?.metadata
|
||||
const requestId = error.response?.headers?.['x-request-id'] || metadata?.requestId
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('access_token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
debugLog('error', {
|
||||
requestId,
|
||||
method: error.config?.method,
|
||||
url: error.config?.url,
|
||||
status: error.response?.status,
|
||||
durationMs: metadata ? Date.now() - metadata.startedAt : undefined,
|
||||
detail: error.response?.data,
|
||||
})
|
||||
if (requestId) {
|
||||
error.requestId = requestId
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user