mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-18 18:50:49 +08:00
feat(logs): redesign LogsPage with structured log parsing and virtual scrolling
- Add log line parser to extract timestamp, level, status code, latency, IP, HTTP method, and path - Implement virtual scrolling with load-more on scroll-up to handle large log files efficiently - Replace monolithic pre block with structured grid layout for better readability - Add visual badges for log levels and HTTP status codes with color-coded severity - Add IconRefreshCw icon component - Update ToggleSwitch to accept ReactNode as label - Fix fetchConfig calls to use default parameters consistently - Add request deduplication in useConfigStore to prevent duplicate /config API calls - Add i18n keys for load_more_hint and hidden_lines
This commit is contained in:
@@ -27,6 +27,9 @@ interface ConfigState {
|
||||
isCacheValid: (section?: RawConfigSection) => boolean;
|
||||
}
|
||||
|
||||
let configRequestToken = 0;
|
||||
let inFlightConfigRequest: { id: number; promise: Promise<Config> } | null = null;
|
||||
|
||||
const SECTION_KEYS: RawConfigSection[] = [
|
||||
'debug',
|
||||
'proxy-url',
|
||||
@@ -102,13 +105,35 @@ export const useConfigStore = create<ConfigState>((set, get) => ({
|
||||
}
|
||||
}
|
||||
|
||||
// section 缓存未命中但 full 缓存可用时,直接复用已获取到的配置,避免重复 /config 请求
|
||||
if (!forceRefresh && section && isCacheValid()) {
|
||||
const fullCached = cache.get('__full__');
|
||||
if (fullCached?.data) {
|
||||
return extractSectionValue(fullCached.data as Config, section);
|
||||
}
|
||||
}
|
||||
|
||||
// 同一时刻合并多个 /config 请求(如 StrictMode 或多个页面同时触发)
|
||||
if (inFlightConfigRequest) {
|
||||
const data = await inFlightConfigRequest.promise;
|
||||
return section ? extractSectionValue(data, section) : data;
|
||||
}
|
||||
|
||||
// 获取新数据
|
||||
set({ loading: true, error: null });
|
||||
|
||||
const requestId = (configRequestToken += 1);
|
||||
try {
|
||||
const data = await configApi.getConfig();
|
||||
const requestPromise = configApi.getConfig();
|
||||
inFlightConfigRequest = { id: requestId, promise: requestPromise };
|
||||
const data = await requestPromise;
|
||||
const now = Date.now();
|
||||
|
||||
// 如果在请求过程中连接已被切换/登出,则忽略旧请求的结果,避免覆盖新会话的状态
|
||||
if (requestId !== configRequestToken) {
|
||||
return section ? extractSectionValue(data, section) : data;
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
const newCache = new Map(cache);
|
||||
newCache.set('__full__', { data, timestamp: now });
|
||||
@@ -127,11 +152,17 @@ export const useConfigStore = create<ConfigState>((set, get) => ({
|
||||
|
||||
return section ? extractSectionValue(data, section) : data;
|
||||
} catch (error: any) {
|
||||
set({
|
||||
error: error.message || 'Failed to fetch config',
|
||||
loading: false
|
||||
});
|
||||
if (requestId === configRequestToken) {
|
||||
set({
|
||||
error: error.message || 'Failed to fetch config',
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (inFlightConfigRequest?.id === requestId) {
|
||||
inFlightConfigRequest = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -206,11 +237,18 @@ export const useConfigStore = create<ConfigState>((set, get) => ({
|
||||
newCache.delete(section);
|
||||
// 同时清除完整配置缓存
|
||||
newCache.delete('__full__');
|
||||
|
||||
set({ cache: newCache });
|
||||
return;
|
||||
} else {
|
||||
newCache.clear();
|
||||
}
|
||||
|
||||
set({ cache: newCache });
|
||||
// 清除全部缓存一般代表“切换连接/登出/全量刷新”,需要让 in-flight 的旧请求失效
|
||||
configRequestToken += 1;
|
||||
inFlightConfigRequest = null;
|
||||
|
||||
set({ config: null, cache: newCache, loading: false, error: null });
|
||||
},
|
||||
|
||||
isCacheValid: (section) => {
|
||||
|
||||
Reference in New Issue
Block a user