feat: add gateway management features and settings
- Implemented new API routes for handling logs and settings related to the gateway. - Added a new Gateway section in the General Settings panel to manage gateway status and auto-start options. - Introduced a state management hook for gateway settings, including status, logs, and auto-start functionality. - Updated configuration service to include gateway auto-start setting. - Enhanced internationalization support for new gateway-related messages. - Refactored existing settings store to accommodate new gateway settings. - Cleaned up code and improved logging functionality.
This commit is contained in:
@@ -431,6 +431,20 @@ export const messages: I18nMessages = {
|
||||
description: 'Customize the look and feel of the application.',
|
||||
themeSection: 'Theme Settings',
|
||||
languageSection: 'Language',
|
||||
gatewayTitle: 'Gateway',
|
||||
gatewayDescription: 'View the current Gateway state and basic runtime controls.',
|
||||
gatewayStatusLabel: 'Status',
|
||||
gatewayPortLabel: 'Port',
|
||||
gatewayConnected: 'Running',
|
||||
gatewayDisconnected: 'Stopped',
|
||||
gatewayReconnecting: 'Restarting',
|
||||
gatewayRestart: 'Restart',
|
||||
gatewayLogs: 'Logs',
|
||||
gatewayHideLogs: 'Hide Logs',
|
||||
gatewayLogsEmpty: 'No logs available yet.',
|
||||
gatewayLogsLoading: 'Loading logs...',
|
||||
gatewayAutoStartTitle: 'Auto-start Gateway',
|
||||
gatewayAutoStartDescription: 'Start the Gateway automatically when the app launches',
|
||||
updatesTitle: 'Updates',
|
||||
currentVersion: 'Current Version',
|
||||
checkForUpdates: 'Check for Updates',
|
||||
@@ -893,6 +907,20 @@ export const messages: I18nMessages = {
|
||||
description: '自定义应用的外观与使用体验。',
|
||||
themeSection: '主题设置',
|
||||
languageSection: '语言',
|
||||
gatewayTitle: '网关',
|
||||
gatewayDescription: '查看当前网关状态与基础运行控制。',
|
||||
gatewayStatusLabel: '状态',
|
||||
gatewayPortLabel: '端口',
|
||||
gatewayConnected: '运行中',
|
||||
gatewayDisconnected: '已停止',
|
||||
gatewayReconnecting: '重启中',
|
||||
gatewayRestart: '重启',
|
||||
gatewayLogs: '日志',
|
||||
gatewayHideLogs: '收起日志',
|
||||
gatewayLogsEmpty: '暂无日志内容。',
|
||||
gatewayLogsLoading: '正在加载日志...',
|
||||
gatewayAutoStartTitle: '自动启动网关',
|
||||
gatewayAutoStartDescription: '应用启动时自动启动网关',
|
||||
updatesTitle: '版本更新',
|
||||
currentVersion: '当前版本',
|
||||
checkForUpdates: '检查更新',
|
||||
@@ -1355,6 +1383,20 @@ export const messages: I18nMessages = {
|
||||
description: 'アプリの見た目と操作感をカスタマイズします。',
|
||||
themeSection: 'テーマ設定',
|
||||
languageSection: '言語',
|
||||
gatewayTitle: 'ゲートウェイ',
|
||||
gatewayDescription: '現在のゲートウェイ状態と基本的なランタイム操作を確認します。',
|
||||
gatewayStatusLabel: '状態',
|
||||
gatewayPortLabel: 'ポート',
|
||||
gatewayConnected: '稼働中',
|
||||
gatewayDisconnected: '停止中',
|
||||
gatewayReconnecting: '再起動中',
|
||||
gatewayRestart: '再起動',
|
||||
gatewayLogs: 'ログ',
|
||||
gatewayHideLogs: 'ログを閉じる',
|
||||
gatewayLogsEmpty: 'まだログはありません。',
|
||||
gatewayLogsLoading: 'ログを読み込み中...',
|
||||
gatewayAutoStartTitle: 'ゲートウェイを自動起動',
|
||||
gatewayAutoStartDescription: 'アプリ起動時にゲートウェイを自動起動します',
|
||||
updatesTitle: 'アップデート',
|
||||
currentVersion: '現在のバージョン',
|
||||
checkForUpdates: '更新を確認',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Moon, Sun, Monitor, RefreshCw } from 'lucide-react';
|
||||
import { FileText, Moon, Sun, Monitor, RefreshCw } from 'lucide-react';
|
||||
import { useI18n } from '../../../i18n';
|
||||
import { SUPPORTED_LANGUAGE_CODES } from '../../../i18n/constants';
|
||||
import type { LanguageCode, ThemeMode } from '../../../types/runtime';
|
||||
import type { GatewayConnectionStatus, GatewaySettingState } from '../useGatewaySettingState';
|
||||
import type { SettingUpdateState } from '../useSettingUpdateState';
|
||||
import SectionHeader from './SectionHeader';
|
||||
import ToggleSwitch from './ToggleSwitch';
|
||||
@@ -11,6 +12,7 @@ type GeneralSettingsPanelProps = {
|
||||
language: LanguageCode;
|
||||
onThemeChange: (theme: ThemeMode) => void | Promise<void>;
|
||||
onLanguageChange: (language: LanguageCode) => void | Promise<void>;
|
||||
gatewayState: GatewaySettingState;
|
||||
updateState: SettingUpdateState;
|
||||
};
|
||||
|
||||
@@ -49,14 +51,46 @@ function getUpdateStatusText(t: ReturnType<typeof useI18n>['t'], updateState: Se
|
||||
}
|
||||
}
|
||||
|
||||
function getGatewayStatusMeta(
|
||||
t: ReturnType<typeof useI18n>['t'],
|
||||
status: GatewayConnectionStatus,
|
||||
): {
|
||||
label: string;
|
||||
className: string;
|
||||
} {
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return {
|
||||
label: t('settings.general.gatewayConnected'),
|
||||
className:
|
||||
'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300',
|
||||
};
|
||||
case 'reconnecting':
|
||||
return {
|
||||
label: t('settings.general.gatewayReconnecting'),
|
||||
className:
|
||||
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-300',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: t('settings.general.gatewayDisconnected'),
|
||||
className:
|
||||
'border-slate-200 bg-slate-100 text-slate-700 dark:border-slate-500/30 dark:bg-slate-500/10 dark:text-slate-300',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default function GeneralSettingsPanel({
|
||||
themeMode,
|
||||
language,
|
||||
onThemeChange,
|
||||
onLanguageChange,
|
||||
gatewayState,
|
||||
updateState,
|
||||
}: GeneralSettingsPanelProps) {
|
||||
const { t } = useI18n();
|
||||
const gatewayStatusMeta = getGatewayStatusMeta(t, gatewayState.status);
|
||||
const gatewayPort = gatewayState.gateway.port ?? 18789;
|
||||
|
||||
return (
|
||||
<section className="flex-1 h-full p-5 select-none">
|
||||
@@ -126,6 +160,118 @@ export default function GeneralSettingsPanel({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className="mb-6 text-[24px] font-medium text-[#171717] dark:text-gray-100">
|
||||
{t('settings.general.gatewayTitle')}
|
||||
</div>
|
||||
|
||||
<div className="p-5 dark:border-gray-700 dark:bg-[#222226]">
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="mb-2 text-[14px] text-[#525866] dark:text-gray-400">
|
||||
{t('settings.general.gatewayStatusLabel')}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<span className="text-[14px] text-[#667085] dark:text-gray-400">
|
||||
{t('settings.general.gatewayPortLabel')}: {gatewayPort}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'mt-3 text-[14px]',
|
||||
gatewayState.error ? 'text-red-500' : 'text-[#667085] dark:text-gray-400',
|
||||
].join(' ')}
|
||||
>
|
||||
{gatewayState.error ?? t('settings.general.gatewayDescription')}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 flex-wrap gap-2">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<span
|
||||
className={[
|
||||
'inline-flex items-center rounded-full border px-3.5 py-2 text-[14px] font-medium',
|
||||
gatewayStatusMeta.className,
|
||||
].join(' ')}
|
||||
>
|
||||
{gatewayStatusMeta.label}
|
||||
</span>
|
||||
{gatewayState.loading ? (
|
||||
<span className="text-[13px] text-[#7a8699] dark:text-gray-500">
|
||||
{t('common.loading')}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void gatewayState.restartGateway();
|
||||
}}
|
||||
disabled={gatewayState.loading}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[#E5E8EE] bg-white px-3.5 py-2 text-[14px] font-medium text-[#171717] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-60 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
||||
>
|
||||
<RefreshCw
|
||||
className={[
|
||||
'h-4 w-4',
|
||||
gatewayState.loading ? 'animate-spin' : '',
|
||||
].join(' ')}
|
||||
/>
|
||||
{t('settings.general.gatewayRestart')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void gatewayState.toggleLogs();
|
||||
}}
|
||||
disabled={gatewayState.loading && !gatewayState.showLogs}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[#E5E8EE] bg-white px-3.5 py-2 text-[14px] font-medium text-[#171717] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-60 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
||||
>
|
||||
<FileText className="h-4 w-4" />
|
||||
{gatewayState.showLogs
|
||||
? t('settings.general.gatewayHideLogs')
|
||||
: t('settings.general.gatewayLogs')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4 border-t border-dashed border-[#E5E8EE] pt-5 dark:border-gray-700">
|
||||
<div>
|
||||
<div className="mb-1 text-[16px] text-[#171717] dark:text-gray-100">
|
||||
{t('settings.general.gatewayAutoStartTitle')}
|
||||
</div>
|
||||
<div className="text-[14px] text-[#99A0AE] dark:text-gray-500">
|
||||
{t('settings.general.gatewayAutoStartDescription')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ToggleSwitch
|
||||
checked={gatewayState.gatewayAutoStart}
|
||||
onChange={(nextValue) => {
|
||||
if (gatewayState.loading) return;
|
||||
void gatewayState.setGatewayAutoStart(nextValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{gatewayState.showLogs ? (
|
||||
<div className="rounded-[18px] bg-[#0F172A] px-4 py-4 shadow-inner">
|
||||
<div className="mb-3 text-[13px] font-medium text-white/70">
|
||||
{t('settings.general.gatewayLogs')}
|
||||
</div>
|
||||
<pre className="max-h-72 overflow-auto whitespace-pre-wrap break-words text-[12px] leading-6 text-[#D6E4FF]">
|
||||
{gatewayState.loading && !gatewayState.logContent
|
||||
? t('settings.general.gatewayLogsLoading')
|
||||
: gatewayState.logContent || t('settings.general.gatewayLogsEmpty')}
|
||||
</pre>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className="mb-6 text-[24px] font-medium text-[#171717] dark:text-gray-100">
|
||||
{t('settings.general.updatesTitle')}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { LanguageCode, ThemeMode } from '../../types/runtime';
|
||||
import AccountSettingsPanel from './components/AccountSettingsPanel';
|
||||
import GeneralSettingsPanel from './components/GeneralSettingsPanel';
|
||||
import SettingMenu, { type SettingView } from './components/SettingMenu';
|
||||
import { useGatewaySettingState } from './useGatewaySettingState';
|
||||
import { useSettingUpdateState } from './useSettingUpdateState';
|
||||
|
||||
export default function SettingPage() {
|
||||
@@ -11,6 +12,7 @@ export default function SettingPage() {
|
||||
const themeMode = useSettingsStore((state) => state.themeMode);
|
||||
const language = useSettingsStore((state) => state.language);
|
||||
const updateState = useSettingUpdateState();
|
||||
const gatewayState = useGatewaySettingState();
|
||||
|
||||
const handleThemeChange = async (nextTheme: ThemeMode) => {
|
||||
await updateThemeMode(nextTheme);
|
||||
@@ -33,6 +35,7 @@ export default function SettingPage() {
|
||||
language={language}
|
||||
onThemeChange={handleThemeChange}
|
||||
onLanguageChange={handleLanguageChange}
|
||||
gatewayState={gatewayState}
|
||||
updateState={updateState}
|
||||
/>
|
||||
)}
|
||||
|
||||
276
src/pages/Setting/useGatewaySettingState.ts
Normal file
276
src/pages/Setting/useGatewaySettingState.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { onGatewayEvent } from '../../lib/gateway-client';
|
||||
import { hostApiFetch } from '../../lib/host-api';
|
||||
import {
|
||||
initSettingsStore,
|
||||
updateGatewayAutoStart,
|
||||
useSettingsStore,
|
||||
} from '../../stores/settings';
|
||||
|
||||
export type GatewayConnectionStatus = 'connected' | 'disconnected' | 'reconnecting';
|
||||
|
||||
type GatewayStatusResponse = {
|
||||
ok?: boolean;
|
||||
status?: GatewayConnectionStatus;
|
||||
initialized?: boolean;
|
||||
mode?: string | null;
|
||||
port?: number | string | null;
|
||||
pid?: number | string | null;
|
||||
};
|
||||
|
||||
type GatewayLogsResponse =
|
||||
| {
|
||||
content?: string;
|
||||
}
|
||||
| string;
|
||||
|
||||
export type GatewayStatusSnapshot = {
|
||||
ok: boolean;
|
||||
status: GatewayConnectionStatus;
|
||||
initialized: boolean;
|
||||
mode: string | null;
|
||||
port: number | null;
|
||||
pid: number | null;
|
||||
};
|
||||
|
||||
export type GatewaySettingState = {
|
||||
status: GatewayConnectionStatus;
|
||||
loading: boolean;
|
||||
showLogs: boolean;
|
||||
gatewayAutoStart: boolean;
|
||||
gateway: GatewayStatusSnapshot;
|
||||
logContent: string;
|
||||
statusLoading: boolean;
|
||||
logLoading: boolean;
|
||||
restarting: boolean;
|
||||
autoStartUpdating: boolean;
|
||||
error: string | null;
|
||||
lastFetchedAt: string | null;
|
||||
refreshStatus: () => Promise<void>;
|
||||
restartGateway: () => Promise<void>;
|
||||
toggleLogs: () => Promise<void>;
|
||||
loadLogs: (tailLines?: number) => Promise<void>;
|
||||
viewLogs: (tailLines?: number) => Promise<void>;
|
||||
setGatewayAutoStart: (nextValue: boolean) => Promise<void>;
|
||||
};
|
||||
|
||||
const DEFAULT_GATEWAY_STATUS: GatewayStatusSnapshot = {
|
||||
ok: false,
|
||||
status: 'disconnected',
|
||||
initialized: false,
|
||||
mode: null,
|
||||
port: null,
|
||||
pid: null,
|
||||
};
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error) return error.message;
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function normalizeStatus(status: unknown): GatewayConnectionStatus {
|
||||
if (status === 'connected' || status === 'disconnected' || status === 'reconnecting') {
|
||||
return status;
|
||||
}
|
||||
|
||||
return 'disconnected';
|
||||
}
|
||||
|
||||
function normalizeOptionalNumber(value: unknown): number | null {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeGatewayStatus(response: GatewayStatusResponse | null | undefined): GatewayStatusSnapshot {
|
||||
const status = normalizeStatus(response?.status);
|
||||
const initialized = Boolean(response?.initialized);
|
||||
|
||||
return {
|
||||
ok: typeof response?.ok === 'boolean' ? response.ok : initialized && status === 'connected',
|
||||
status,
|
||||
initialized,
|
||||
mode: typeof response?.mode === 'string' && response.mode ? response.mode : null,
|
||||
port: normalizeOptionalNumber(response?.port),
|
||||
pid: normalizeOptionalNumber(response?.pid),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeLogContent(response: GatewayLogsResponse): string {
|
||||
if (typeof response === 'string') {
|
||||
return response;
|
||||
}
|
||||
|
||||
return typeof response.content === 'string' ? response.content : '';
|
||||
}
|
||||
|
||||
export function useGatewaySettingState(): GatewaySettingState {
|
||||
const gatewayAutoStart = useSettingsStore((current) => current.gatewayAutoStart);
|
||||
const [gateway, setGateway] = useState<GatewayStatusSnapshot>(DEFAULT_GATEWAY_STATUS);
|
||||
const [logContent, setLogContent] = useState('');
|
||||
const [showLogs, setShowLogs] = useState(false);
|
||||
const [statusLoading, setStatusLoading] = useState(true);
|
||||
const [logLoading, setLogLoading] = useState(false);
|
||||
const [restarting, setRestarting] = useState(false);
|
||||
const [autoStartUpdating, setAutoStartUpdating] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [lastFetchedAt, setLastFetchedAt] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
async function init() {
|
||||
setStatusLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await initSettingsStore();
|
||||
const response = await hostApiFetch<GatewayStatusResponse>('/api/gateway/status');
|
||||
|
||||
if (!active) return;
|
||||
|
||||
setGateway(normalizeGatewayStatus(response));
|
||||
setLastFetchedAt(new Date().toISOString());
|
||||
} catch (initError) {
|
||||
if (!active) return;
|
||||
setError(getErrorMessage(initError));
|
||||
} finally {
|
||||
if (active) {
|
||||
setStatusLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init();
|
||||
|
||||
const unsubscribe = onGatewayEvent((event) => {
|
||||
if (!active || event.type !== 'gateway:status') {
|
||||
return;
|
||||
}
|
||||
|
||||
setGateway((current) => ({
|
||||
...current,
|
||||
ok: event.status === 'connected',
|
||||
status: event.status,
|
||||
initialized: event.status !== 'disconnected' || current.initialized,
|
||||
}));
|
||||
setError(null);
|
||||
});
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refreshStatus = async () => {
|
||||
setStatusLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await hostApiFetch<GatewayStatusResponse>('/api/gateway/status');
|
||||
setGateway(normalizeGatewayStatus(response));
|
||||
setLastFetchedAt(new Date().toISOString());
|
||||
} catch (refreshError) {
|
||||
setError(getErrorMessage(refreshError));
|
||||
} finally {
|
||||
setStatusLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const restartGateway = async () => {
|
||||
setRestarting(true);
|
||||
setError(null);
|
||||
setGateway((current) => ({
|
||||
...current,
|
||||
ok: false,
|
||||
status: 'reconnecting',
|
||||
}));
|
||||
|
||||
try {
|
||||
await hostApiFetch<{ success?: boolean }>('/api/gateway/restart', {
|
||||
method: 'POST',
|
||||
});
|
||||
await refreshStatus();
|
||||
} catch (restartError) {
|
||||
setError(getErrorMessage(restartError));
|
||||
} finally {
|
||||
setRestarting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loadLogs = async (tailLines = 100) => {
|
||||
setLogLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await hostApiFetch<GatewayLogsResponse>(`/api/logs?tailLines=${encodeURIComponent(String(tailLines))}`);
|
||||
setLogContent(normalizeLogContent(response));
|
||||
setLastFetchedAt(new Date().toISOString());
|
||||
} catch (logsError) {
|
||||
setLogContent('');
|
||||
setError(getErrorMessage(logsError));
|
||||
} finally {
|
||||
setLogLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleLogs = async () => {
|
||||
const nextValue = !showLogs;
|
||||
setShowLogs(nextValue);
|
||||
|
||||
if (!nextValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
await loadLogs();
|
||||
};
|
||||
|
||||
const setGatewayAutoStart = async (nextValue: boolean) => {
|
||||
setAutoStartUpdating(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await updateGatewayAutoStart(nextValue);
|
||||
setLastFetchedAt(new Date().toISOString());
|
||||
} catch (updateError) {
|
||||
setError(getErrorMessage(updateError));
|
||||
} finally {
|
||||
setAutoStartUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loading =
|
||||
statusLoading
|
||||
|| logLoading
|
||||
|| restarting
|
||||
|| autoStartUpdating;
|
||||
|
||||
return {
|
||||
status: gateway.status,
|
||||
loading,
|
||||
showLogs,
|
||||
gatewayAutoStart,
|
||||
gateway,
|
||||
logContent,
|
||||
statusLoading,
|
||||
logLoading,
|
||||
restarting,
|
||||
autoStartUpdating,
|
||||
error,
|
||||
lastFetchedAt,
|
||||
refreshStatus,
|
||||
restartGateway,
|
||||
toggleLogs,
|
||||
loadLogs,
|
||||
viewLogs: loadLogs,
|
||||
setGatewayAutoStart,
|
||||
};
|
||||
}
|
||||
@@ -13,4 +13,6 @@ export {
|
||||
updateFontSize as setFontSize,
|
||||
updateMinimizeToTray as setMinimizeToTray,
|
||||
updatePrimaryColor as setPrimaryColor,
|
||||
updateGatewayAutoStart,
|
||||
updateGatewayAutoStart as setGatewayAutoStart,
|
||||
} from './settings';
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface SettingsState {
|
||||
minimizeToTray: boolean;
|
||||
providerId: string | null;
|
||||
defaultModel: string | null;
|
||||
gatewayAutoStart: boolean;
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX = 'zn-ai-react:';
|
||||
@@ -73,6 +74,7 @@ function createInitialState(): SettingsState {
|
||||
minimizeToTray: false,
|
||||
providerId: null,
|
||||
defaultModel: null,
|
||||
gatewayAutoStart: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -203,7 +205,7 @@ async function hydrate(): Promise<SettingsState> {
|
||||
const systemTheme = detectSystemTheme();
|
||||
const systemLanguage = detectSystemLanguage();
|
||||
|
||||
const [themeMode, language, fontSize, minimizeToTray, primaryColor, providerId, defaultModel] = await Promise.all([
|
||||
const [themeMode, language, fontSize, minimizeToTray, primaryColor, providerId, defaultModel, gatewayAutoStart] = await Promise.all([
|
||||
readThemeMode(),
|
||||
readConfigValue<LanguageCode>(CONFIG_KEYS.LANGUAGE, systemLanguage),
|
||||
readConfigValue<number>(CONFIG_KEYS.FONT_SIZE, 14),
|
||||
@@ -211,6 +213,7 @@ async function hydrate(): Promise<SettingsState> {
|
||||
readConfigValue<string>(CONFIG_KEYS.PRIMARY_COLOR, '#1677ff'),
|
||||
readConfigValue<string | null>(CONFIG_KEYS.PROVIDER, null),
|
||||
readConfigValue<string | null>(CONFIG_KEYS.DEFAULT_MODEL, null),
|
||||
readConfigValue<boolean>(CONFIG_KEYS.GATEWAY_AUTO_START, true),
|
||||
]);
|
||||
|
||||
const resolvedLanguage = resolveSupportedLanguage(language ?? systemLanguage);
|
||||
@@ -230,6 +233,7 @@ async function hydrate(): Promise<SettingsState> {
|
||||
minimizeToTray: Boolean(minimizeToTray),
|
||||
providerId: providerId ?? null,
|
||||
defaultModel: defaultModel ?? null,
|
||||
gatewayAutoStart: Boolean(gatewayAutoStart),
|
||||
});
|
||||
|
||||
applyLocale(resolvedLanguage);
|
||||
@@ -317,6 +321,21 @@ async function setPrimaryColor(primaryColor: string, persist = true): Promise<Se
|
||||
return state;
|
||||
}
|
||||
|
||||
async function setGatewayAutoStart(gatewayAutoStart: boolean, persist = true): Promise<SettingsState> {
|
||||
const next = Boolean(gatewayAutoStart);
|
||||
if (state.gatewayAutoStart === next && state.initialized) return state;
|
||||
|
||||
patchState({
|
||||
gatewayAutoStart: next,
|
||||
});
|
||||
|
||||
if (persist) {
|
||||
await writeConfigValue(CONFIG_KEYS.GATEWAY_AUTO_START, next);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function getSnapshot(): SettingsState {
|
||||
return state;
|
||||
}
|
||||
@@ -335,6 +354,7 @@ export const settingsStore = {
|
||||
setFontSize,
|
||||
setMinimizeToTray,
|
||||
setPrimaryColor,
|
||||
setGatewayAutoStart,
|
||||
hostApiFetch,
|
||||
};
|
||||
|
||||
@@ -371,4 +391,8 @@ export async function updateMinimizeToTray(minimizeToTray: boolean, persist = tr
|
||||
return setMinimizeToTray(minimizeToTray, persist);
|
||||
}
|
||||
|
||||
export async function updateGatewayAutoStart(gatewayAutoStart: boolean, persist = true): Promise<SettingsState> {
|
||||
return setGatewayAutoStart(gatewayAutoStart, persist);
|
||||
}
|
||||
|
||||
export { i18n };
|
||||
|
||||
@@ -30,6 +30,7 @@ export const CONFIG_KEYS = {
|
||||
MINIMIZE_TO_TRAY: 'minimizeToTray',
|
||||
PROVIDER: 'provider',
|
||||
DEFAULT_MODEL: 'defaultModel',
|
||||
GATEWAY_AUTO_START: 'gatewayAutoStart',
|
||||
SELECTED_CHANNELS: 'selectedChannels',
|
||||
IMAGE_CACHE: 'imageCache',
|
||||
TASK_LIST: 'taskList',
|
||||
@@ -47,6 +48,7 @@ export interface ConfigValueMap {
|
||||
minimizeToTray: boolean;
|
||||
provider: string | null;
|
||||
defaultModel: string | null;
|
||||
gatewayAutoStart: boolean;
|
||||
selectedChannels: Array<{ id: string; channelName: string; channelUrl: string }>;
|
||||
imageCache: Array<[string, unknown]>;
|
||||
taskList: Task[];
|
||||
|
||||
Reference in New Issue
Block a user