mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
210 lines
6.2 KiB
TypeScript
210 lines
6.2 KiB
TypeScript
/**
|
|
* 认证状态管理
|
|
* 从原项目 src/modules/login.js 和 src/core/connection.js 迁移
|
|
*/
|
|
|
|
import { create } from 'zustand';
|
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
import type { AuthState, LoginCredentials, ConnectionStatus } from '@/types';
|
|
import { STORAGE_KEY_AUTH } from '@/utils/constants';
|
|
import { secureStorage } from '@/services/storage/secureStorage';
|
|
import { apiClient } from '@/services/api/client';
|
|
import { useConfigStore } from './useConfigStore';
|
|
import { detectApiBaseFromLocation, normalizeApiBase } from '@/utils/connection';
|
|
|
|
interface AuthStoreState extends AuthState {
|
|
connectionStatus: ConnectionStatus;
|
|
connectionError: string | null;
|
|
|
|
// 操作
|
|
login: (credentials: LoginCredentials) => Promise<void>;
|
|
logout: () => void;
|
|
checkAuth: () => Promise<boolean>;
|
|
restoreSession: () => Promise<boolean>;
|
|
updateServerVersion: (version: string | null, buildDate?: string | null) => void;
|
|
updateConnectionStatus: (status: ConnectionStatus, error?: string | null) => void;
|
|
}
|
|
|
|
let restoreSessionPromise: Promise<boolean> | null = null;
|
|
|
|
export const useAuthStore = create<AuthStoreState>()(
|
|
persist(
|
|
(set, get) => ({
|
|
// 初始状态
|
|
isAuthenticated: false,
|
|
apiBase: '',
|
|
managementKey: '',
|
|
serverVersion: null,
|
|
serverBuildDate: null,
|
|
connectionStatus: 'disconnected',
|
|
connectionError: null,
|
|
|
|
// 恢复会话并自动登录
|
|
restoreSession: () => {
|
|
if (restoreSessionPromise) return restoreSessionPromise;
|
|
|
|
restoreSessionPromise = (async () => {
|
|
secureStorage.migratePlaintextKeys(['apiBase', 'apiUrl', 'managementKey']);
|
|
|
|
const wasLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
|
const legacyBase =
|
|
secureStorage.getItem<string>('apiBase') ||
|
|
secureStorage.getItem<string>('apiUrl', { encrypt: true });
|
|
const legacyKey = secureStorage.getItem<string>('managementKey');
|
|
|
|
const { apiBase, managementKey } = get();
|
|
const resolvedBase = normalizeApiBase(apiBase || legacyBase || detectApiBaseFromLocation());
|
|
const resolvedKey = managementKey || legacyKey || '';
|
|
|
|
set({ apiBase: resolvedBase, managementKey: resolvedKey });
|
|
apiClient.setConfig({ apiBase: resolvedBase, managementKey: resolvedKey });
|
|
|
|
if (wasLoggedIn && resolvedBase && resolvedKey) {
|
|
try {
|
|
await get().login({ apiBase: resolvedBase, managementKey: resolvedKey });
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('Auto login failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
})();
|
|
|
|
return restoreSessionPromise;
|
|
},
|
|
|
|
// 登录
|
|
login: async (credentials) => {
|
|
const apiBase = normalizeApiBase(credentials.apiBase);
|
|
const managementKey = credentials.managementKey.trim();
|
|
|
|
try {
|
|
set({ connectionStatus: 'connecting' });
|
|
|
|
// 配置 API 客户端
|
|
apiClient.setConfig({
|
|
apiBase,
|
|
managementKey
|
|
});
|
|
|
|
// 测试连接 - 获取配置
|
|
await useConfigStore.getState().fetchConfig(undefined, true);
|
|
|
|
// 登录成功
|
|
set({
|
|
isAuthenticated: true,
|
|
apiBase,
|
|
managementKey,
|
|
connectionStatus: 'connected',
|
|
connectionError: null
|
|
});
|
|
localStorage.setItem('isLoggedIn', 'true');
|
|
} catch (error: any) {
|
|
set({
|
|
connectionStatus: 'error',
|
|
connectionError: error.message || 'Connection failed'
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// 登出
|
|
logout: () => {
|
|
restoreSessionPromise = null;
|
|
useConfigStore.getState().clearCache();
|
|
set({
|
|
isAuthenticated: false,
|
|
apiBase: '',
|
|
managementKey: '',
|
|
serverVersion: null,
|
|
serverBuildDate: null,
|
|
connectionStatus: 'disconnected',
|
|
connectionError: null
|
|
});
|
|
localStorage.removeItem('isLoggedIn');
|
|
},
|
|
|
|
// 检查认证状态
|
|
checkAuth: async () => {
|
|
const { managementKey, apiBase } = get();
|
|
|
|
if (!managementKey || !apiBase) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// 重新配置客户端
|
|
apiClient.setConfig({ apiBase, managementKey });
|
|
|
|
// 验证连接
|
|
await useConfigStore.getState().fetchConfig();
|
|
|
|
set({
|
|
isAuthenticated: true,
|
|
connectionStatus: 'connected'
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
set({
|
|
isAuthenticated: false,
|
|
connectionStatus: 'error'
|
|
});
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// 更新服务器版本
|
|
updateServerVersion: (version, buildDate) => {
|
|
set({ serverVersion: version || null, serverBuildDate: buildDate || null });
|
|
},
|
|
|
|
// 更新连接状态
|
|
updateConnectionStatus: (status, error = null) => {
|
|
set({
|
|
connectionStatus: status,
|
|
connectionError: error
|
|
});
|
|
}
|
|
}),
|
|
{
|
|
name: STORAGE_KEY_AUTH,
|
|
storage: createJSONStorage(() => ({
|
|
getItem: (name) => {
|
|
const data = secureStorage.getItem<AuthStoreState>(name);
|
|
return data ? JSON.stringify(data) : null;
|
|
},
|
|
setItem: (name, value) => {
|
|
secureStorage.setItem(name, JSON.parse(value));
|
|
},
|
|
removeItem: (name) => {
|
|
secureStorage.removeItem(name);
|
|
}
|
|
})),
|
|
partialize: (state) => ({
|
|
apiBase: state.apiBase,
|
|
managementKey: state.managementKey,
|
|
serverVersion: state.serverVersion,
|
|
serverBuildDate: state.serverBuildDate
|
|
})
|
|
}
|
|
)
|
|
);
|
|
|
|
// 监听全局未授权事件
|
|
if (typeof window !== 'undefined') {
|
|
window.addEventListener('unauthorized', () => {
|
|
useAuthStore.getState().logout();
|
|
});
|
|
|
|
window.addEventListener(
|
|
'server-version-update',
|
|
((e: CustomEvent) => {
|
|
const detail = e.detail || {};
|
|
useAuthStore.getState().updateServerVersion(detail.version || null, detail.buildDate || null);
|
|
}) as EventListener
|
|
);
|
|
}
|