Commit 11d063e3 authored by IanShaw027's avatar IanShaw027
Browse files

feat(前端API): 实现运维监控 API 客户端

- 新增 ops API 客户端(ops.ts)
- 扩展 settings API 支持 ops 配置
- 更新 admin API 索引导出 ops 模块
- 扩展 API 客户端支持 WebSocket 连接
parent e8464580
......@@ -16,6 +16,7 @@ import usageAPI from './usage'
import geminiAPI from './gemini'
import antigravityAPI from './antigravity'
import userAttributesAPI from './userAttributes'
import opsAPI from './ops'
/**
* Unified admin API object for convenient access
......@@ -33,7 +34,8 @@ export const adminAPI = {
usage: usageAPI,
gemini: geminiAPI,
antigravity: antigravityAPI,
userAttributes: userAttributesAPI
userAttributes: userAttributesAPI,
ops: opsAPI
}
export {
......@@ -49,7 +51,8 @@ export {
usageAPI,
geminiAPI,
antigravityAPI,
userAttributesAPI
userAttributesAPI,
opsAPI
}
export default adminAPI
This diff is collapsed.
......@@ -34,9 +34,22 @@ export interface SystemSettings {
turnstile_enabled: boolean
turnstile_site_key: string
turnstile_secret_key_configured: boolean
// Model fallback configuration
enable_model_fallback: boolean
fallback_model_anthropic: string
fallback_model_openai: string
fallback_model_gemini: string
fallback_model_antigravity: string
// Identity patch configuration (Claude -> Gemini)
enable_identity_patch: boolean
identity_patch_prompt: string
// Ops Monitoring (vNext)
ops_monitoring_enabled: boolean
ops_realtime_monitoring_enabled: boolean
ops_query_mode_default: 'auto' | 'raw' | 'preagg' | string
}
export interface UpdateSettingsRequest {
......@@ -60,8 +73,16 @@ export interface UpdateSettingsRequest {
turnstile_enabled?: boolean
turnstile_site_key?: string
turnstile_secret_key?: string
enable_model_fallback?: boolean
fallback_model_anthropic?: string
fallback_model_openai?: string
fallback_model_gemini?: string
fallback_model_antigravity?: string
enable_identity_patch?: boolean
identity_patch_prompt?: string
ops_monitoring_enabled?: boolean
ops_realtime_monitoring_enabled?: boolean
ops_query_mode_default?: 'auto' | 'raw' | 'preagg' | string
}
/**
......
......@@ -80,9 +80,45 @@ apiClient.interceptors.response.use(
return response
},
(error: AxiosError<ApiResponse<unknown>>) => {
// Request cancellation: keep the original axios cancellation error so callers can ignore it.
// Otherwise we'd misclassify it as a generic "network error".
if (error.code === 'ERR_CANCELED' || axios.isCancel(error)) {
return Promise.reject(error)
}
// Handle common errors
if (error.response) {
const { status, data } = error.response
const url = String(error.config?.url || '')
// Validate `data` shape to avoid HTML error pages breaking our error handling.
const apiData = (typeof data === 'object' && data !== null ? data : {}) as Record<string, any>
// Ops monitoring disabled: treat as feature-flagged 404, and proactively redirect away
// from ops pages to avoid broken UI states.
if (status === 404 && apiData.message === 'Ops monitoring is disabled') {
try {
localStorage.setItem('ops_monitoring_enabled_cached', 'false')
} catch {
// ignore localStorage failures
}
try {
window.dispatchEvent(new CustomEvent('ops-monitoring-disabled'))
} catch {
// ignore event failures
}
if (window.location.pathname.startsWith('/admin/ops')) {
window.location.href = '/admin/settings'
}
return Promise.reject({
status,
code: 'OPS_DISABLED',
message: apiData.message || error.message,
url
})
}
// 401: Unauthorized - clear token and redirect to login
if (status === 401) {
......@@ -113,8 +149,8 @@ apiClient.interceptors.response.use(
// Return structured error
return Promise.reject({
status,
code: data?.code,
message: data?.message || error.message
code: apiData.code,
message: apiData.message || apiData.detail || error.message
})
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment