refactor(frontend): P0 API 层收口与 http 统一

- 统一 http.ts baseURL:get/post/download/upload 均走 resolveApiUrl
- agentUtils 下沉至 lib/agent-utils,API 层移除对 components 的依赖
- 扩展 metricsApi/snmpApi/prometheusProxyApi/zabbixProxyApi
- 扩展 pipelineApi/credentialApi 覆盖流水线与凭证场景
- 14 个组件/页面禁止直连 http,统一走 @/services/api
This commit is contained in:
Logan.Li committed 2026-06-25 23:49:46 +08:00
1 parent bfd6431b9d
commit 7fb2236b4d
27 files changed
+194 -150

No files matched your search

+9 -18
View File
@@ -7,7 +7,7 @@ import {
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import { http } from '@/services/http';
import { approvalWorkflowApi, userApi } from '@/services/api';
/* ------------------------------------------------------------------ */
/* Types */
@@ -80,16 +80,7 @@ const STEP_TYPES = [
{ value: 'execute' as const, label: '执行阶段', desc: '执行人员操作' },
];
// API helpers
const workflowApi = {
list: (params?: { category?: string }) => http.get('/approval-workflows', params),
get: (id: number) => http.get(`/approval-workflows/${id}`),
create: (data: any) => http.post('/approval-workflows', data),
update: (id: number, data: any) => http.put(`/approval-workflows/${id}`, data),
delete: (id: number) => http.delete(`/approval-workflows/${id}`),
setDefault: (id: number) => http.put(`/approval-workflows/${id}/default`),
};
const userListApi = () => http.get('/admin/users');
/* ------------------------------------------------------------------ */
/* UserSelectInline */
@@ -299,9 +290,9 @@ function WorkflowEditorModal({ workflow, users, onClose, onSaved }: EditorProps)
})),
};
if (isEdit && workflow) {
await workflowApi.update(workflow.id, payload);
await approvalWorkflowApi.update(workflow.id, payload);
} else {
await workflowApi.create(payload);
await approvalWorkflowApi.create(payload);
}
onSaved();
} catch (e: unknown) {
@@ -459,7 +450,7 @@ export default function ApprovalWorkflowPage() {
try {
setLoading(true);
setError(null);
const res: any = await workflowApi.list();
const res: any = await approvalWorkflowApi.list();
const data = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : []);
const normalized = data.map((w: any) => ({ ...w, steps: w.steps || [] }));
setItems(normalized);
@@ -472,7 +463,7 @@ export default function ApprovalWorkflowPage() {
const fetchUsers = useCallback(async () => {
try {
const res: any = await userListApi();
const res: any = await userApi.getUsers();
const data = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : []);
// Normalize: extract role code from roles array
const normalized = data.map((u: any) => ({
@@ -490,7 +481,7 @@ export default function ApprovalWorkflowPage() {
const toggleActive = useCallback(async (wf: WorkflowItem) => {
try {
await workflowApi.update(wf.id, { is_active: !wf.is_active });
await approvalWorkflowApi.update(wf.id, { is_active: !wf.is_active });
setItems((prev) => prev.map((w) => (w.id === wf.id ? { ...w, is_active: !w.is_active } : w)));
toast.success(wf.is_active ? '已停用' : '已启用');
} catch { toast.error('操作失败'); }
@@ -499,7 +490,7 @@ export default function ApprovalWorkflowPage() {
const handleDelete = useCallback(async (id: number, name: string) => {
if (!window.confirm(`确定删除审批流程「${name}」?删除后不可恢复。`)) return;
try {
await workflowApi.delete(id);
await approvalWorkflowApi.delete(id);
setItems((prev) => prev.filter((w) => w.id !== id));
toast.success('已删除');
} catch { toast.error('删除失败'); }
@@ -507,7 +498,7 @@ export default function ApprovalWorkflowPage() {
const setDefault = useCallback(async (id: number) => {
try {
await workflowApi.setDefault(id);
await approvalWorkflowApi.setDefault(id);
setItems((prev) => prev.map((w) => ({ ...w, is_default: w.id === id })));
toast.success('已设为默认');
} catch { toast.error('操作失败'); }
+4 -4
View File
@@ -5,7 +5,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Activity, Search, Filter, BarChart3, Cpu, HardDrive, Network, Database, Plus } from 'lucide-react';
import { http } from '@/services/http';
import { metricsApi } from '@/services/api';
interface _MetricName {
name: string;
@@ -51,7 +51,7 @@ export function MetricsExplorer() {
// Load targets
const loadTargets = useCallback(async () => {
try {
const res: Record<string, any> = await http.get('/metrics/targets?page_size=200');
const res: Record<string, any> = await metricsApi.getTargets({ page_size: 200 });
setTargets(res?.list || []);
} catch { /* empty */ }
finally { setLoading(false); }
@@ -60,7 +60,7 @@ export function MetricsExplorer() {
// Load metric names
const loadMetricNames = useCallback(async () => {
try {
const names: any = await http.get('/metrics/names');
const names: any = await metricsApi.getNames();
setMetricNames(names || []);
} catch { /* empty */ }
}, []);
@@ -78,7 +78,7 @@ export function MetricsExplorer() {
Promise.all(
selectedMetrics.map(mn =>
http.get('/metrics/query', { target_id: selectedTarget, metric_name: mn, from: String(from), to: String(to), limit: 200 })
metricsApi.query({ target_id: selectedTarget, metric_name: mn, from: String(from), to: String(to), limit: 200 })
.then(( data: any) => (Array.isArray(data) ? data : []))
.catch(() => [])
)
+2 -2
View File
@@ -3,7 +3,7 @@ import { useState, useEffect, useCallback } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Database, Server, Activity, Loader2, Circle } from 'lucide-react';
import { http } from '@/services/http';
import { metricsApi } from '@/services/api';
interface TargetInfo {
id: number;
@@ -68,7 +68,7 @@ export function MiddlewareMonitor() {
try {
setLoading(true);
setError('');
const data = await http.get<TargetsResponse>('/metrics/targets', { type: 'middleware', page_size: 200 });
const data = await metricsApi.getTargets({ type: 'middleware', page_size: 200 }) as TargetsResponse;
setTargets(data?.list || []);
} catch (err: any) {
setError((err as Record<string, any>).message || '加载失败');
+5 -5
View File
@@ -30,7 +30,7 @@ import {
TableRow,
} from '@/components/ui/table';
import { Radio, Router, HardDrive, Plus, Trash2 } from 'lucide-react';
import http from '@/services/http';
import { snmpApi } from '@/services/api';
// ---------- types ----------
interface SnmpStats {
@@ -81,7 +81,7 @@ export function SNMPMonitor() {
// ---------- data fetching ----------
const loadStats = useCallback(async () => {
try {
const data = await http.get<SnmpStats>('/snmp/stats');
const data = await snmpApi.getStats() as SnmpStats;
setStats(data);
} catch {
// backend unavailable — leave defaults
@@ -90,7 +90,7 @@ export function SNMPMonitor() {
const loadDevices = useCallback(async () => {
try {
const data = await http.get<DeviceListResponse>('/snmp/devices');
const data = await snmpApi.getDevices() as DeviceListResponse;
setDevices(data.list ?? []);
} catch {
setDevices([]);
@@ -113,7 +113,7 @@ export function SNMPMonitor() {
if (!form.name.trim() || !form.ip.trim()) return;
setSubmitting(true);
try {
await http.post('/snmp/devices', {
await snmpApi.createDevice({
name: form.name.trim(),
ip: form.ip.trim(),
port: Number(form.port),
@@ -133,7 +133,7 @@ export function SNMPMonitor() {
const handleDelete = async (id: number) => {
try {
await http.delete(`/snmp/devices/${id}`);
await snmpApi.deleteDevice(id);
refresh();
} catch {
// silently fail