From eda6cd361ee5a937984ffcf42905256d683ebb76 Mon Sep 17 00:00:00 2001 From: kongkongyo Date: Fri, 16 Jan 2026 22:11:54 +0800 Subject: [PATCH] Enhance monitoring page functionality and user experience Changes: - Add provider type filtering for request logs to quickly locate specific provider usage - Optimize request log display by showing provider type in a dedicated column and removing auth index column for better readability - Add friendly prompts for providers that don't support auto-disable (e.g., Claude, Gemini) with manual operation guides - Point WebUI repository to this repository (forked from official for modifications) Co-Authored-By: Claude Sonnet 4.5 --- .gitignore | 3 +- src/components/monitor/RequestLogs.tsx | 47 ++++++++-- .../monitor/UnsupportedDisableModal.tsx | 82 +++++++++++++++++ src/hooks/useDisableModel.ts | 55 +++++++++-- src/i18n/locales/en.json | 12 ++- src/i18n/locales/zh-CN.json | 26 ++++-- src/pages/MonitorPage.module.scss | 10 +- src/pages/MonitorPage.tsx | 92 +++++++++++++++++-- src/pages/SystemPage.tsx | 2 +- 9 files changed, 287 insertions(+), 42 deletions(-) create mode 100644 src/components/monitor/UnsupportedDisableModal.tsx diff --git a/.gitignore b/.gitignore index 84c834a..1890811 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ settings.local.json *.sw? 自定义 Web UI tmpclaude* -.claude \ No newline at end of file +.claude +CLIProxyAPI-服务端 \ No newline at end of file diff --git a/src/components/monitor/RequestLogs.tsx b/src/components/monitor/RequestLogs.tsx index 688a59f..6aa22f6 100644 --- a/src/components/monitor/RequestLogs.tsx +++ b/src/components/monitor/RequestLogs.tsx @@ -6,6 +6,7 @@ import { usageApi } from '@/services/api'; import { useDisableModel } from '@/hooks'; import { TimeRangeSelector, formatTimeRangeCaption, type TimeRange } from './TimeRangeSelector'; import { DisableModelModal } from './DisableModelModal'; +import { UnsupportedDisableModal } from './UnsupportedDisableModal'; import { maskSecret, formatProviderDisplay, @@ -21,6 +22,7 @@ interface RequestLogsProps { data: UsageData | null; loading: boolean; providerMap: Record; + providerTypeMap: Record; apiFilter: string; } @@ -33,8 +35,8 @@ interface LogEntry { source: string; displayName: string; providerName: string | null; + providerType: string; maskedKey: string; - authIndex: string; failed: boolean; inputTokens: number; outputTokens: number; @@ -56,12 +58,13 @@ interface PrecomputedStats { // 虚拟滚动行高 const ROW_HEIGHT = 40; -export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilter }: RequestLogsProps) { +export function RequestLogs({ data, loading: parentLoading, providerMap, providerTypeMap, apiFilter }: RequestLogsProps) { const { t } = useTranslation(); const [filterApi, setFilterApi] = useState(''); const [filterModel, setFilterModel] = useState(''); const [filterSource, setFilterSource] = useState(''); const [filterStatus, setFilterStatus] = useState<'' | 'success' | 'failed'>(''); + const [filterProviderType, setFilterProviderType] = useState(''); const [autoRefresh, setAutoRefresh] = useState(10); const [countdown, setCountdown] = useState(0); const countdownRef = useRef | null>(null); @@ -92,12 +95,14 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt // 使用禁用模型 Hook const { disableState, + unsupportedState, disabling, isModelDisabled, handleDisableClick, handleConfirmDisable, handleCancelDisable, - } = useDisableModel({ providerMap }); + handleCloseUnsupported, + } = useDisableModel({ providerMap, providerTypeMap }); // 处理时间范围变化 const handleTimeRangeChange = useCallback((range: TimeRange, custom?: DateRange) => { @@ -261,6 +266,8 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt const { provider, masked } = getProviderDisplayParts(source, providerMap); const displayName = provider ? `${provider} (${masked})` : masked; const timestampMs = detail.timestamp ? new Date(detail.timestamp).getTime() : 0; + // 获取提供商类型 + const providerType = providerTypeMap[source] || '--'; entries.push({ id: `${idCounter++}`, timestamp: detail.timestamp, @@ -270,8 +277,8 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt source, displayName, providerName: provider, + providerType, maskedKey: masked, - authIndex: detail.auth_index || '--', failed: detail.failed, inputTokens: detail.tokens.input_tokens || 0, outputTokens: detail.tokens.output_tokens || 0, @@ -283,7 +290,7 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt // 按时间倒序排序 return entries.sort((a, b) => b.timestampMs - a.timestampMs); - }, [effectiveData, providerMap]); + }, [effectiveData, providerMap, providerTypeMap]); // 预计算所有条目的统计数据(一次性计算,避免渲染时重复计算) const precomputedStats = useMemo(() => { @@ -339,21 +346,26 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt }, [logEntries]); // 获取筛选选项 - const { apis, models, sources } = useMemo(() => { + const { apis, models, sources, providerTypes } = useMemo(() => { const apiSet = new Set(); const modelSet = new Set(); const sourceSet = new Set(); + const providerTypeSet = new Set(); logEntries.forEach((entry) => { apiSet.add(entry.apiKey); modelSet.add(entry.model); sourceSet.add(entry.source); + if (entry.providerType && entry.providerType !== '--') { + providerTypeSet.add(entry.providerType); + } }); return { apis: Array.from(apiSet).sort(), models: Array.from(modelSet).sort(), sources: Array.from(sourceSet).sort(), + providerTypes: Array.from(providerTypeSet).sort(), }; }, [logEntries]); @@ -365,9 +377,10 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt if (filterSource && entry.source !== filterSource) return false; if (filterStatus === 'success' && entry.failed) return false; if (filterStatus === 'failed' && !entry.failed) return false; + if (filterProviderType && entry.providerType !== filterProviderType) return false; return true; }); - }, [logEntries, filterApi, filterModel, filterSource, filterStatus]); + }, [logEntries, filterApi, filterModel, filterSource, filterStatus, filterProviderType]); // 虚拟滚动配置 const rowVirtualizer = useVirtualizer({ @@ -399,10 +412,10 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt return ( <> - {entry.authIndex} {maskSecret(entry.apiKey)} + {entry.providerType} {entry.model} @@ -494,6 +507,16 @@ export function RequestLogs({ data, loading: parentLoading, providerMap, apiFilt ))} +