diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 1d6fb17..dc94ba7 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -63,6 +63,7 @@
"custom_connection_placeholder": "Eg: https://example.com:8317",
"custom_connection_hint": "By default the current URL is used. Override it here if needed.",
"use_current_address": "Use Current URL",
+ "remember_password_label": "Remember password",
"management_key_label": "Management Key:",
"management_key_placeholder": "Enter the management key",
"connect_button": "Connect",
@@ -745,7 +746,11 @@
"link_webui_repo": "WebUI Repository",
"link_webui_repo_desc": "Management Center frontend source code",
"link_docs": "Documentation",
- "link_docs_desc": "Usage tutorials and configuration guides"
+ "link_docs_desc": "Usage tutorials and configuration guides",
+ "clear_login_title": "Local Login Data",
+ "clear_login_desc": "Clear locally saved login data and sign out. Usage stats pricing settings will remain untouched.",
+ "clear_login_button": "Clear login data",
+ "clear_login_confirm": "Clear local login data and sign out now?"
},
"notification": {
"debug_updated": "Debug settings updated",
@@ -758,6 +763,7 @@
"logging_to_file_updated": "Logging settings updated",
"request_log_updated": "Request logging setting updated",
"ws_auth_updated": "WebSocket authentication setting updated",
+ "login_storage_cleared": "Local login data cleared",
"api_key_added": "API key added successfully",
"api_key_updated": "API key updated successfully",
"api_key_deleted": "API key deleted successfully",
diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json
index edc8b75..84ad6d8 100644
--- a/src/i18n/locales/zh-CN.json
+++ b/src/i18n/locales/zh-CN.json
@@ -63,6 +63,7 @@
"custom_connection_placeholder": "例如: https://example.com:8317",
"custom_connection_hint": "默认使用当前访问地址,若需要可手动输入其他地址。",
"use_current_address": "使用当前地址",
+ "remember_password_label": "记住密码",
"management_key_label": "管理密钥:",
"management_key_placeholder": "请输入管理密钥",
"connect_button": "连接",
@@ -745,7 +746,11 @@
"link_webui_repo": "WebUI 仓库",
"link_webui_repo_desc": "管理中心前端界面源代码",
"link_docs": "使用教程",
- "link_docs_desc": "配置指南和使用说明"
+ "link_docs_desc": "配置指南和使用说明",
+ "clear_login_title": "本地登录信息",
+ "clear_login_desc": "清理本地保存的登录信息并退出登录,不会影响使用统计中的价格设置。",
+ "clear_login_button": "清理登录信息",
+ "clear_login_confirm": "确认清理本地登录信息并退出登录?"
},
"notification": {
"debug_updated": "调试设置已更新",
@@ -758,6 +763,7 @@
"logging_to_file_updated": "日志记录设置已更新",
"request_log_updated": "请求日志设置已更新",
"ws_auth_updated": "WebSocket 鉴权设置已更新",
+ "login_storage_cleared": "本地登录信息已清理",
"api_key_added": "API密钥添加成功",
"api_key_updated": "API密钥更新成功",
"api_key_deleted": "API密钥删除成功",
diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx
index 140da2c..63a81d5 100644
--- a/src/pages/LoginPage.tsx
+++ b/src/pages/LoginPage.tsx
@@ -19,11 +19,13 @@ export function LoginPage() {
const restoreSession = useAuthStore((state) => state.restoreSession);
const storedBase = useAuthStore((state) => state.apiBase);
const storedKey = useAuthStore((state) => state.managementKey);
+ const storedRememberPassword = useAuthStore((state) => state.rememberPassword);
const [apiBase, setApiBase] = useState('');
const [managementKey, setManagementKey] = useState('');
const [showCustomBase, setShowCustomBase] = useState(false);
const [showKey, setShowKey] = useState(false);
+ const [rememberPassword, setRememberPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [autoLoading, setAutoLoading] = useState(true);
const [error, setError] = useState('');
@@ -38,6 +40,7 @@ export function LoginPage() {
if (!autoLoggedIn) {
setApiBase(storedBase || detectedBase);
setManagementKey(storedKey || '');
+ setRememberPassword(storedRememberPassword || Boolean(storedKey));
}
} finally {
setAutoLoading(false);
@@ -45,7 +48,7 @@ export function LoginPage() {
};
init();
- }, [detectedBase, restoreSession, storedBase, storedKey]);
+ }, [detectedBase, restoreSession, storedBase, storedKey, storedRememberPassword]);
if (isAuthenticated) {
const redirect = (location.state as any)?.from?.pathname || '/';
@@ -62,7 +65,11 @@ export function LoginPage() {
setLoading(true);
setError('');
try {
- await login({ apiBase: baseToUse, managementKey: managementKey.trim() });
+ await login({
+ apiBase: baseToUse,
+ managementKey: managementKey.trim(),
+ rememberPassword
+ });
showNotification(t('common.connected_status'), 'success');
navigate('/', { replace: true });
} catch (err: any) {
@@ -148,6 +155,16 @@ export function LoginPage() {
}
/>
+
+ setRememberPassword(e.target.checked)}
+ />
+
+
+
diff --git a/src/pages/SystemPage.module.scss b/src/pages/SystemPage.module.scss
index e5b72fd..5ae8435 100644
--- a/src/pages/SystemPage.module.scss
+++ b/src/pages/SystemPage.module.scss
@@ -34,6 +34,12 @@
margin: 0 0 $spacing-md 0;
}
+.clearLoginActions {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+}
+
.infoGrid {
display: grid;
gap: $spacing-sm;
diff --git a/src/pages/SystemPage.tsx b/src/pages/SystemPage.tsx
index 491bfe9..445b34b 100644
--- a/src/pages/SystemPage.tsx
+++ b/src/pages/SystemPage.tsx
@@ -6,6 +6,7 @@ import { IconGithub, IconBookOpen, IconExternalLink, IconCode } from '@/componen
import { useAuthStore, useConfigStore, useNotificationStore, useModelsStore } from '@/stores';
import { apiKeysApi } from '@/services/api/apiKeys';
import { classifyModels } from '@/utils/models';
+import { STORAGE_KEY_AUTH } from '@/utils/constants';
import styles from './SystemPage.module.scss';
export function SystemPage() {
@@ -104,6 +105,15 @@ export function SystemPage() {
}
};
+ const handleClearLoginStorage = () => {
+ if (!window.confirm(t('system_info.clear_login_confirm'))) return;
+ auth.logout();
+ if (typeof localStorage === 'undefined') return;
+ const keysToRemove = [STORAGE_KEY_AUTH, 'isLoggedIn', 'apiBase', 'apiUrl', 'managementKey'];
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
+ showNotification(t('notification.login_storage_cleared'), 'success');
+ };
+
useEffect(() => {
fetchConfig().catch(() => {
// ignore
@@ -248,6 +258,15 @@ export function SystemPage() {
)}
+
+
+ {t('system_info.clear_login_desc')}
+
+
+
+
);
diff --git a/src/stores/useAuthStore.ts b/src/stores/useAuthStore.ts
index 96a35b5..cf7905d 100644
--- a/src/stores/useAuthStore.ts
+++ b/src/stores/useAuthStore.ts
@@ -34,6 +34,7 @@ export const useAuthStore = create()(
isAuthenticated: false,
apiBase: '',
managementKey: '',
+ rememberPassword: false,
serverVersion: null,
serverBuildDate: null,
connectionStatus: 'disconnected',
@@ -52,16 +53,25 @@ export const useAuthStore = create()(
secureStorage.getItem('apiUrl', { encrypt: true });
const legacyKey = secureStorage.getItem('managementKey');
- const { apiBase, managementKey } = get();
+ const { apiBase, managementKey, rememberPassword } = get();
const resolvedBase = normalizeApiBase(apiBase || legacyBase || detectApiBaseFromLocation());
const resolvedKey = managementKey || legacyKey || '';
+ const resolvedRememberPassword = rememberPassword || Boolean(managementKey) || Boolean(legacyKey);
- set({ apiBase: resolvedBase, managementKey: resolvedKey });
+ set({
+ apiBase: resolvedBase,
+ managementKey: resolvedKey,
+ rememberPassword: resolvedRememberPassword
+ });
apiClient.setConfig({ apiBase: resolvedBase, managementKey: resolvedKey });
if (wasLoggedIn && resolvedBase && resolvedKey) {
try {
- await get().login({ apiBase: resolvedBase, managementKey: resolvedKey });
+ await get().login({
+ apiBase: resolvedBase,
+ managementKey: resolvedKey,
+ rememberPassword: resolvedRememberPassword
+ });
return true;
} catch (error) {
console.warn('Auto login failed:', error);
@@ -79,6 +89,7 @@ export const useAuthStore = create()(
login: async (credentials) => {
const apiBase = normalizeApiBase(credentials.apiBase);
const managementKey = credentials.managementKey.trim();
+ const rememberPassword = credentials.rememberPassword ?? get().rememberPassword ?? false;
try {
set({ connectionStatus: 'connecting' });
@@ -97,10 +108,15 @@ export const useAuthStore = create()(
isAuthenticated: true,
apiBase,
managementKey,
+ rememberPassword,
connectionStatus: 'connected',
connectionError: null
});
- localStorage.setItem('isLoggedIn', 'true');
+ if (rememberPassword) {
+ localStorage.setItem('isLoggedIn', 'true');
+ } else {
+ localStorage.removeItem('isLoggedIn');
+ }
} catch (error: any) {
set({
connectionStatus: 'error',
@@ -185,7 +201,8 @@ export const useAuthStore = create()(
})),
partialize: (state) => ({
apiBase: state.apiBase,
- managementKey: state.managementKey,
+ ...(state.rememberPassword ? { managementKey: state.managementKey } : {}),
+ rememberPassword: state.rememberPassword,
serverVersion: state.serverVersion,
serverBuildDate: state.serverBuildDate
})
diff --git a/src/types/auth.ts b/src/types/auth.ts
index 8ac68bb..374bd9f 100644
--- a/src/types/auth.ts
+++ b/src/types/auth.ts
@@ -7,6 +7,7 @@
export interface LoginCredentials {
apiBase: string;
managementKey: string;
+ rememberPassword?: boolean;
}
// 认证状态
@@ -14,6 +15,7 @@ export interface AuthState {
isAuthenticated: boolean;
apiBase: string;
managementKey: string;
+ rememberPassword: boolean;
serverVersion: string | null;
serverBuildDate: string | null;
}