refactor(runtime): remove Home detection

This commit is contained in:
Supra4E8C
2026-07-26 02:46:56 +08:00
parent cf3c617444
commit 0a2be7dc57
11 changed files with 17 additions and 172 deletions
-5
View File
@@ -680,9 +680,6 @@
},
"logs": {
"title": "Logs Viewer",
"runtime_unknown": "Current runtime: detecting",
"runtime_cpa": "Current runtime: CPA",
"runtime_home": "Current runtime: Home",
"refresh_button": "Refresh Logs",
"clear_button": "Clear Logs",
"fullscreen_button": "Full Screen",
@@ -690,7 +687,6 @@
"download_button": "Download Logs",
"error_logs_modal_title": "Error Request Logs",
"error_logs_description": "Pick an error request log file to open or download (only generated when request logging is off).",
"error_logs_home_unavailable": "In Home mode, use Log Content for database logs. Error request log files only apply to CPA file logs.",
"error_logs_request_log_enabled": "Request logging is enabled, so this list will always be empty. Disable request logging and refresh to view error logs.",
"error_logs_empty": "No error request log files found",
"error_logs_load_error": "Failed to load error log list",
@@ -714,7 +710,6 @@
"file_logging_required": "This logs endpoint requires logging to file before file logs can be read.",
"file_logging_required_title": "File logging is required",
"file_logging_required_desc": "This logs endpoint reads local log files. Enable logging-to-file in the config, then refresh.",
"home_clear_unavailable": "Home logs come from database queries and cannot be cleared here.",
"log_content": "Log Content",
"loading": "Loading logs...",
"load_error": "Failed to load logs",
-5
View File
@@ -667,9 +667,6 @@
},
"logs": {
"title": "Просмотр журналов",
"runtime_unknown": "Текущий режим: определяется",
"runtime_cpa": "Текущий режим: CPA",
"runtime_home": "Текущий режим: Home",
"refresh_button": "Обновить журналы",
"clear_button": "Очистить журналы",
"fullscreen_button": "На весь экран",
@@ -677,7 +674,6 @@
"download_button": "Скачать журналы",
"error_logs_modal_title": "Журналы ошибок запросов",
"error_logs_description": "Выберите файл журнала ошибок запроса, чтобы открыть или скачать его (создаётся только при отключённом журналировании запросов).",
"error_logs_home_unavailable": "В режиме Home смотрите журналы базы данных на вкладке содержимого. Файлы ошибок запросов относятся только к файловым журналам CPA.",
"error_logs_request_log_enabled": "Журналирование запросов включено, поэтому этот список всегда будет пустым. Отключите журналирование запросов и обновите список, чтобы просмотреть журналы ошибок.",
"error_logs_empty": "Файлы журнала ошибок запросов не найдены",
"error_logs_load_error": "Не удалось загрузить список журналов ошибок",
@@ -701,7 +697,6 @@
"file_logging_required": "Этот endpoint журналов требует включить запись журналов в файл перед чтением файловых журналов.",
"file_logging_required_title": "Требуется файловое журналирование",
"file_logging_required_desc": "Этот endpoint журналов читает локальные файлы. Включите logging-to-file в конфигурации и обновите страницу.",
"home_clear_unavailable": "Журналы Home загружаются из базы данных и не могут быть очищены здесь.",
"log_content": "Содержимое журнала",
"loading": "Загрузка журналов...",
"load_error": "Не удалось загрузить журналы",
-5
View File
@@ -680,9 +680,6 @@
},
"logs": {
"title": "日志查看",
"runtime_unknown": "当前运行端:识别中",
"runtime_cpa": "当前运行端:CPA",
"runtime_home": "当前运行端:Home",
"refresh_button": "刷新日志",
"clear_button": "清空日志",
"fullscreen_button": "全屏显示",
@@ -690,7 +687,6 @@
"download_button": "下载日志",
"error_logs_modal_title": "错误请求日志",
"error_logs_description": "请选择要打开或下载的错误请求日志文件(仅在关闭请求日志时生成)。",
"error_logs_home_unavailable": "Home 模式下请在日志内容页查看数据库日志,错误请求日志列表仅适用于 CPA 文件日志。",
"error_logs_request_log_enabled": "当前已开启请求日志,按接口约定错误请求日志列表会始终为空。关闭请求日志后再刷新即可查看。",
"error_logs_empty": "暂无错误请求日志文件",
"error_logs_load_error": "加载错误日志列表失败",
@@ -714,7 +710,6 @@
"file_logging_required": "当前日志接口需要先开启“日志记录到文件”后才能读取文件日志。",
"file_logging_required_title": "需要开启文件日志",
"file_logging_required_desc": "当前日志接口读取本地日志文件。请在配置中开启 logging-to-file 后再刷新查看。",
"home_clear_unavailable": "Home 日志来自数据库查询,当前不支持从这里清空。",
"log_content": "日志内容",
"loading": "正在加载日志...",
"load_error": "加载日志失败",
-5
View File
@@ -706,9 +706,6 @@
},
"logs": {
"title": "記錄檢視",
"runtime_unknown": "目前執行端:識別中",
"runtime_cpa": "目前執行端:CPA",
"runtime_home": "目前執行端:Home",
"refresh_button": "重新整理記錄",
"clear_button": "清空記錄",
"fullscreen_button": "全螢幕顯示",
@@ -716,7 +713,6 @@
"download_button": "下載記錄",
"error_logs_modal_title": "錯誤請求記錄",
"error_logs_description": "請選擇要開啟或下載的錯誤請求記錄檔案(僅在關閉請求記錄時產生)。",
"error_logs_home_unavailable": "Home 模式下請在記錄內容頁查看資料庫記錄,錯誤請求記錄清單僅適用於 CPA 檔案記錄。",
"error_logs_request_log_enabled": "目前已開啟請求記錄,按介面約定錯誤請求記錄清單會始終為空。關閉請求記錄後再重新整理即可查看。",
"error_logs_empty": "暫無錯誤請求記錄檔案",
"error_logs_load_error": "載入錯誤記錄清單失敗",
@@ -740,7 +736,6 @@
"file_logging_required": "目前記錄介面需要先開啟「記錄到檔案」後才能讀取檔案記錄。",
"file_logging_required_title": "需要開啟檔案記錄",
"file_logging_required_desc": "目前記錄介面會讀取本機記錄檔。請在設定中開啟 logging-to-file 後再重新整理查看。",
"home_clear_unavailable": "Home 記錄來自資料庫查詢,目前不支援從這裡清空。",
"log_content": "記錄內容",
"loading": "正在載入記錄...",
"load_error": "載入記錄失敗",
+1 -32
View File
@@ -13,25 +13,11 @@
}
}
.pageHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: $spacing-md;
margin: 0 0 $spacing-lg 0;
@include mobile {
align-items: flex-start;
flex-direction: column;
gap: $spacing-xs;
}
}
.pageTitle {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
margin: 0;
margin: 0 0 $spacing-lg 0;
}
.tabBar {
@@ -98,17 +84,6 @@
}
}
.runtimeNotice {
flex: 0 0 auto;
padding: 4px 10px;
border: 1px solid var(--border-color);
border-radius: $radius-full;
color: var(--text-secondary);
background: var(--bg-secondary);
font-size: 12px;
line-height: 1.3;
}
.toolbar {
display: flex;
align-items: center;
@@ -695,9 +670,6 @@
@media (max-height: 820px) {
.pageTitle {
font-size: 24px;
}
.pageHeader {
margin-bottom: $spacing-md;
}
@@ -747,9 +719,6 @@
@media (max-height: 600px) {
.pageTitle {
font-size: 20px;
}
.pageHeader {
margin-bottom: $spacing-sm;
}
+4 -41
View File
@@ -28,7 +28,6 @@ import { useHeaderRefresh } from '@/hooks/useHeaderRefresh';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
import { logsApi, type LogsQuery } from '@/services/api/logs';
import { versionApi } from '@/services/api/version';
import { copyToClipboard } from '@/utils/clipboard';
import { getErrorMessage } from '@/utils/helpers';
import { downloadBlob } from '@/utils/download';
@@ -143,13 +142,10 @@ export function LogsPage() {
const { t } = useTranslation();
const { showNotification, showConfirmation } = useNotificationStore();
const connectionStatus = useAuthStore((state) => state.connectionStatus);
const serverRuntimeKind = useAuthStore((state) => state.serverRuntimeKind);
const updateServerRuntimeKind = useAuthStore((state) => state.updateServerRuntimeKind);
const config = useConfigStore((state) => state.config);
const requestLogEnabled = config?.requestLog ?? false;
const loggingToFileEnabled = config?.loggingToFile ?? false;
const cpaNeedsFileLogging = serverRuntimeKind === 'cpa' && !loggingToFileEnabled;
const isHomeRuntime = serverRuntimeKind === 'home';
const cpaNeedsFileLogging = !loggingToFileEnabled;
const [fileLoggingRequired, setFileLoggingRequired] = useState(false);
const showFileLoggingRequired = cpaNeedsFileLogging || fileLoggingRequired;
@@ -218,7 +214,7 @@ export function LogsPage() {
const disableControls = connectionStatus !== 'connected';
const refreshDisabled = disableControls || loading || cpaNeedsFileLogging;
const autoRefreshDisabled = disableControls || showFileLoggingRequired;
const clearDisabled = disableControls || showFileLoggingRequired || isHomeRuntime;
const clearDisabled = disableControls || showFileLoggingRequired;
async function loadLogs(incremental = false) {
if (connectionStatus !== 'connected') {
@@ -330,10 +326,6 @@ export function LogsPage() {
useHeaderRefresh(() => loadLogs(false));
const clearLogs = async () => {
if (isHomeRuntime) {
showNotification(t('logs.home_clear_unavailable'), 'warning');
return;
}
if (cpaNeedsFileLogging) {
showNotification(t('logs.cpa_file_logging_required'), 'warning');
return;
@@ -377,13 +369,6 @@ export function LogsPage() {
setLoadingErrors(false);
return;
}
if (isHomeRuntime) {
setLoadingErrors(false);
setErrorLogs([]);
setErrorLogsError('');
return;
}
setLoadingErrors(true);
setErrorLogsError('');
try {
@@ -470,21 +455,6 @@ export function LogsPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectionStatus, loggingToFileEnabled]);
useEffect(() => {
if (connectionStatus !== 'connected' || serverRuntimeKind !== 'unknown') return;
let cancelled = false;
const detectRuntime = async () => {
const runtimeKind = await versionApi.detectRuntimeKind();
if (!cancelled && (runtimeKind === 'cpa' || runtimeKind === 'home')) {
updateServerRuntimeKind(runtimeKind);
}
};
void detectRuntime();
return () => {
cancelled = true;
};
}, [connectionStatus, serverRuntimeKind, updateServerRuntimeKind]);
useEffect(() => {
if (activeTab !== 'errors') return;
if (connectionStatus !== 'connected') return;
@@ -697,10 +667,7 @@ export function LogsPage() {
return (
<div className={styles.container}>
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>{t('logs.title')}</h1>
<div className={styles.runtimeNotice}>{t(`logs.runtime_${serverRuntimeKind}`)}</div>
</div>
<h1 className={styles.pageTitle}>{t('logs.title')}</h1>
<div className={styles.tabBar}>
<button
@@ -1149,11 +1116,7 @@ export function LogsPage() {
<div className="stack">
<div className="hint">{t('logs.error_logs_description')}</div>
{isHomeRuntime && (
<div className="status-badge warning">{t('logs.error_logs_home_unavailable')}</div>
)}
{requestLogEnabled && !isHomeRuntime && (
{requestLogEnabled && (
<div>
<div className="status-badge warning">
{t('logs.error_logs_request_log_enabled')}
+4 -12
View File
@@ -10,13 +10,10 @@ import {
CPA_BUILD_DATE_HEADER_KEYS,
CPA_SUPPORT_PLUGIN_HEADER_KEYS,
CPA_VERSION_HEADER_KEYS,
HOME_BUILD_DATE_HEADER_KEYS,
HOME_VERSION_HEADER_KEYS,
REQUEST_TIMEOUT_MS,
VERSION_HEADER_KEYS,
} from '@/utils/constants';
import { computeApiUrl } from '@/utils/connection';
import type { ServerRuntimeKind } from '@/types';
import { parseApiErrorResponse } from './apiError';
class ApiClient {
@@ -124,22 +121,17 @@ class ApiClient {
this.instance.interceptors.response.use(
(response) => {
const headers = response.headers as Record<string, string | undefined>;
const homeVersion = this.readHeader(headers, HOME_VERSION_HEADER_KEYS);
const homeBuildDate = this.readHeader(headers, HOME_BUILD_DATE_HEADER_KEYS);
const cpaVersion = this.readHeader(headers, CPA_VERSION_HEADER_KEYS);
const cpaBuildDate = this.readHeader(headers, CPA_BUILD_DATE_HEADER_KEYS);
const version = homeVersion || cpaVersion || this.readHeader(headers, VERSION_HEADER_KEYS);
const buildDate =
homeBuildDate || cpaBuildDate || this.readHeader(headers, BUILD_DATE_HEADER_KEYS);
const version = cpaVersion || this.readHeader(headers, VERSION_HEADER_KEYS);
const buildDate = cpaBuildDate || this.readHeader(headers, BUILD_DATE_HEADER_KEYS);
const supportsPlugin = this.readBooleanHeader(headers, CPA_SUPPORT_PLUGIN_HEADER_KEYS);
const runtimeKind: ServerRuntimeKind | null =
homeVersion || homeBuildDate ? 'home' : cpaVersion || cpaBuildDate ? 'cpa' : null;
// 触发版本更新事件(后续通过 store 处理)
if (version || buildDate || runtimeKind) {
if (version || buildDate) {
window.dispatchEvent(
new CustomEvent('server-version-update', {
detail: { version: version || null, buildDate: buildDate || null, runtimeKind },
detail: { version: version || null, buildDate: buildDate || null },
})
);
}
-15
View File
@@ -3,22 +3,7 @@
*/
import { apiClient } from './client';
import type { ServerRuntimeKind } from '@/types';
import { isRecord } from '@/utils/helpers';
export const versionApi = {
checkLatest: () => apiClient.get<Record<string, unknown>>('/latest-version'),
async detectRuntimeKind(): Promise<ServerRuntimeKind> {
try {
const data = await apiClient.get('/nodes');
return isRecord(data) && Array.isArray(data.nodes) ? 'home' : 'unknown';
} catch (error: unknown) {
const status = isRecord(error) ? error.status : undefined;
if (status === 404 || status === 405) {
return 'cpa';
}
return 'unknown';
}
},
};
+6 -38
View File
@@ -5,11 +5,10 @@
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import type { AuthState, LoginCredentials, ConnectionStatus, ServerRuntimeKind } from '@/types';
import type { AuthState, LoginCredentials, ConnectionStatus } from '@/types';
import { STORAGE_KEY_AUTH } from '@/utils/constants';
import { obfuscatedStorage } from '@/services/storage/secureStorage';
import { apiClient } from '@/services/api/client';
import { versionApi } from '@/services/api/version';
import { useConfigStore } from './useConfigStore';
import { useModelsStore } from './useModelsStore';
import { useQuotaStore } from './useQuotaStore';
@@ -23,26 +22,12 @@ interface AuthStoreState extends AuthState {
logout: () => void;
checkAuth: () => Promise<boolean>;
restoreSession: () => Promise<boolean>;
updateServerVersion: (
version: string | null,
buildDate?: string | null,
runtimeKind?: ServerRuntimeKind | null
) => void;
updateServerRuntimeKind: (runtimeKind: ServerRuntimeKind) => void;
updateServerVersion: (version: string | null, buildDate?: string | null) => void;
updateServerPluginSupport: (supportsPlugin: boolean) => void;
}
let restoreSessionPromise: Promise<boolean> | null = null;
const detectRuntimeKind = async (): Promise<ServerRuntimeKind> => {
try {
return await versionApi.detectRuntimeKind();
} catch (error) {
console.warn('Runtime kind detection failed:', error);
return 'unknown';
}
};
export const useAuthStore = create<AuthStoreState>()(
persist(
(set, get) => ({
@@ -53,7 +38,6 @@ export const useAuthStore = create<AuthStoreState>()(
rememberPassword: false,
serverVersion: null,
serverBuildDate: null,
serverRuntimeKind: 'unknown',
supportsPlugin: false,
connectionStatus: 'disconnected',
@@ -116,7 +100,6 @@ export const useAuthStore = create<AuthStoreState>()(
connectionStatus: 'connecting',
serverVersion: null,
serverBuildDate: null,
serverRuntimeKind: 'unknown',
supportsPlugin: false,
});
useModelsStore.getState().clearCache();
@@ -130,7 +113,6 @@ export const useAuthStore = create<AuthStoreState>()(
// 测试连接 - 获取配置
await useConfigStore.getState().fetchConfig(true);
const runtimeKind = await detectRuntimeKind();
// 登录成功
set({
@@ -139,7 +121,6 @@ export const useAuthStore = create<AuthStoreState>()(
managementKey,
rememberPassword,
connectionStatus: 'connected',
...(runtimeKind !== 'unknown' ? { serverRuntimeKind: runtimeKind } : {}),
});
if (rememberPassword) {
localStorage.setItem('isLoggedIn', 'true');
@@ -164,7 +145,6 @@ export const useAuthStore = create<AuthStoreState>()(
managementKey: '',
serverVersion: null,
serverBuildDate: null,
serverRuntimeKind: 'unknown',
supportsPlugin: false,
connectionStatus: 'disconnected',
});
@@ -186,12 +166,10 @@ export const useAuthStore = create<AuthStoreState>()(
// 验证连接
await useConfigStore.getState().fetchConfig();
const runtimeKind = await detectRuntimeKind();
set({
isAuthenticated: true,
connectionStatus: 'connected',
...(runtimeKind !== 'unknown' ? { serverRuntimeKind: runtimeKind } : {}),
});
return true;
@@ -206,16 +184,11 @@ export const useAuthStore = create<AuthStoreState>()(
},
// 更新服务器版本
updateServerVersion: (version, buildDate, runtimeKind) => {
set((state) => ({
updateServerVersion: (version, buildDate) => {
set({
serverVersion: version || null,
serverBuildDate: buildDate || null,
serverRuntimeKind: runtimeKind || state.serverRuntimeKind,
}));
},
updateServerRuntimeKind: (runtimeKind) => {
set({ serverRuntimeKind: runtimeKind });
});
},
updateServerPluginSupport: (supportsPlugin) => {
@@ -242,7 +215,6 @@ export const useAuthStore = create<AuthStoreState>()(
rememberPassword: state.rememberPassword,
serverVersion: state.serverVersion,
serverBuildDate: state.serverBuildDate,
serverRuntimeKind: state.serverRuntimeKind,
}),
}
)
@@ -256,11 +228,7 @@ if (typeof window !== 'undefined') {
window.addEventListener('server-version-update', ((e: CustomEvent) => {
const detail = e.detail || {};
const runtimeKind =
detail.runtimeKind === 'cpa' || detail.runtimeKind === 'home' ? detail.runtimeKind : null;
useAuthStore
.getState()
.updateServerVersion(detail.version || null, detail.buildDate || null, runtimeKind);
useAuthStore.getState().updateServerVersion(detail.version || null, detail.buildDate || null);
}) as EventListener);
window.addEventListener('server-plugin-support-update', ((e: CustomEvent) => {
-2
View File
@@ -18,10 +18,8 @@ export interface AuthState {
rememberPassword: boolean;
serverVersion: string | null;
serverBuildDate: string | null;
serverRuntimeKind: ServerRuntimeKind;
supportsPlugin: boolean;
}
// 连接状态
export type ConnectionStatus = 'connected' | 'disconnected' | 'connecting' | 'error';
export type ServerRuntimeKind = 'unknown' | 'cpa' | 'home';
+2 -12
View File
@@ -19,18 +19,8 @@ export const REQUEST_TIMEOUT_MS = 30 * 1000;
export const CPA_VERSION_HEADER_KEYS = ['x-cpa-version'];
export const CPA_BUILD_DATE_HEADER_KEYS = ['x-cpa-build-date'];
export const CPA_SUPPORT_PLUGIN_HEADER_KEYS = ['x-cpa-support-plugin'];
export const HOME_VERSION_HEADER_KEYS = ['x-cpa-home-version'];
export const HOME_BUILD_DATE_HEADER_KEYS = ['x-cpa-home-build-date'];
export const VERSION_HEADER_KEYS = [
...HOME_VERSION_HEADER_KEYS,
...CPA_VERSION_HEADER_KEYS,
'x-server-version',
];
export const BUILD_DATE_HEADER_KEYS = [
...HOME_BUILD_DATE_HEADER_KEYS,
...CPA_BUILD_DATE_HEADER_KEYS,
'x-server-build-date',
];
export const VERSION_HEADER_KEYS = [...CPA_VERSION_HEADER_KEYS, 'x-server-version'];
export const BUILD_DATE_HEADER_KEYS = [...CPA_BUILD_DATE_HEADER_KEYS, 'x-server-build-date'];
// 日志相关
export const LOGS_TIMEOUT_MS = 60 * 1000;