299 lines
8.9 KiB
TypeScript
299 lines
8.9 KiB
TypeScript
import { create } from 'zustand';
|
|
|
|
// =============================================================================
|
|
// 类型定义
|
|
// =============================================================================
|
|
|
|
export type ConnectionStatus = 'unknown' | 'checking' | 'connected' | 'disconnected' | 'error';
|
|
export type AppReadyState = 'initializing' | 'ready' | 'error';
|
|
export type OperationStatus = 'idle' | 'connecting' | 'in_progress' | 'completed' | 'failed';
|
|
|
|
export interface ServiceStatus {
|
|
status: ConnectionStatus;
|
|
message?: string;
|
|
lastChecked?: Date;
|
|
}
|
|
|
|
export interface OperationProgress {
|
|
status: OperationStatus;
|
|
type?: 'keygen' | 'sign';
|
|
sessionId?: string;
|
|
currentStep?: number;
|
|
totalSteps?: number;
|
|
stepDescription?: string;
|
|
error?: string;
|
|
}
|
|
|
|
export interface EnvironmentCheck {
|
|
messageRouter: ServiceStatus;
|
|
kavaApi: ServiceStatus;
|
|
database: ServiceStatus;
|
|
}
|
|
|
|
interface AppState {
|
|
// 应用就绪状态
|
|
appReady: AppReadyState;
|
|
appError?: string;
|
|
|
|
// 环境检测结果
|
|
environment: EnvironmentCheck;
|
|
|
|
// 当前操作进度
|
|
operation: OperationProgress;
|
|
|
|
// Actions
|
|
setAppReady: (state: AppReadyState, error?: string) => void;
|
|
setMessageRouterStatus: (status: ServiceStatus) => void;
|
|
setKavaApiStatus: (status: ServiceStatus) => void;
|
|
setDatabaseStatus: (status: ServiceStatus) => void;
|
|
setOperation: (operation: Partial<OperationProgress>) => void;
|
|
resetOperation: () => void;
|
|
checkAllServices: () => Promise<void>;
|
|
}
|
|
|
|
// =============================================================================
|
|
// Store
|
|
// =============================================================================
|
|
|
|
export const useAppStore = create<AppState>((set, get) => ({
|
|
// 初始状态
|
|
appReady: 'initializing',
|
|
appError: undefined,
|
|
|
|
environment: {
|
|
messageRouter: { status: 'unknown' },
|
|
kavaApi: { status: 'unknown' },
|
|
database: { status: 'unknown' },
|
|
},
|
|
|
|
operation: {
|
|
status: 'idle',
|
|
},
|
|
|
|
// Actions
|
|
setAppReady: (state, error) => set({ appReady: state, appError: error }),
|
|
|
|
setMessageRouterStatus: (status) =>
|
|
set((s) => ({
|
|
environment: { ...s.environment, messageRouter: status },
|
|
})),
|
|
|
|
setKavaApiStatus: (status) =>
|
|
set((s) => ({
|
|
environment: { ...s.environment, kavaApi: status },
|
|
})),
|
|
|
|
setDatabaseStatus: (status) =>
|
|
set((s) => ({
|
|
environment: { ...s.environment, database: status },
|
|
})),
|
|
|
|
setOperation: (operation) =>
|
|
set((s) => ({
|
|
operation: { ...s.operation, ...operation },
|
|
})),
|
|
|
|
resetOperation: () =>
|
|
set({
|
|
operation: { status: 'idle' },
|
|
}),
|
|
|
|
checkAllServices: async () => {
|
|
const { setMessageRouterStatus, setKavaApiStatus, setDatabaseStatus, setAppReady } = get();
|
|
|
|
// 检测 Message Router - 真实验证注册状态
|
|
setMessageRouterStatus({ status: 'checking', message: '正在检测...' });
|
|
try {
|
|
if (window.electronAPI) {
|
|
const settings = await window.electronAPI.storage.getSettings();
|
|
const routerUrl = settings?.messageRouterUrl || 'mpc-grpc.szaiai.com:443';
|
|
|
|
// 1. 检查是否已连接
|
|
const connResult = await window.electronAPI.grpc.isConnected();
|
|
if (!connResult.connected) {
|
|
setMessageRouterStatus({
|
|
status: 'disconnected',
|
|
message: '未连接到 ' + routerUrl,
|
|
lastChecked: new Date(),
|
|
});
|
|
} else {
|
|
// 2. 已连接,检查是否已注册
|
|
const partyIdResult = await window.electronAPI.grpc.getPartyId();
|
|
if (!partyIdResult.partyId) {
|
|
setMessageRouterStatus({
|
|
status: 'disconnected',
|
|
message: '已连接但未注册',
|
|
lastChecked: new Date(),
|
|
});
|
|
} else {
|
|
// 3. 已注册,从 Message Router 验证自己是否在注册列表中
|
|
const partiesResult = await window.electronAPI.grpc.getRegisteredParties();
|
|
if (partiesResult.success) {
|
|
const selfRegistered = partiesResult.parties.some(
|
|
(p) => p.partyId === partyIdResult.partyId
|
|
);
|
|
if (selfRegistered) {
|
|
const selfParty = partiesResult.parties.find(
|
|
(p) => p.partyId === partyIdResult.partyId
|
|
);
|
|
setMessageRouterStatus({
|
|
status: 'connected',
|
|
message: `已注册 (${selfParty?.online ? '在线' : '离线'})`,
|
|
lastChecked: new Date(),
|
|
});
|
|
} else {
|
|
setMessageRouterStatus({
|
|
status: 'error',
|
|
message: '注册验证失败',
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
} else {
|
|
setMessageRouterStatus({
|
|
status: 'error',
|
|
message: partiesResult.error || '获取注册列表失败',
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
setMessageRouterStatus({
|
|
status: 'disconnected',
|
|
message: '非 Electron 环境',
|
|
});
|
|
}
|
|
} catch (error) {
|
|
setMessageRouterStatus({
|
|
status: 'error',
|
|
message: (error as Error).message,
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
|
|
// 检测 Kava API
|
|
setKavaApiStatus({ status: 'checking', message: '正在检测...' });
|
|
try {
|
|
if (window.electronAPI) {
|
|
// 使用健康检查 API (查询最新区块)
|
|
const result = await window.electronAPI.kava.healthCheck();
|
|
if (result.success && result.data?.ok) {
|
|
const config = await window.electronAPI.kava.getConfig();
|
|
const latencyMsg = result.data.latency ? ` (${result.data.latency}ms)` : '';
|
|
setKavaApiStatus({
|
|
status: 'connected',
|
|
message: (config?.lcdEndpoint || 'Kava Mainnet') + latencyMsg,
|
|
lastChecked: new Date(),
|
|
});
|
|
} else {
|
|
setKavaApiStatus({
|
|
status: 'error',
|
|
message: result.data?.error || result.error || 'API 响应异常',
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
} else {
|
|
setKavaApiStatus({
|
|
status: 'disconnected',
|
|
message: '非 Electron 环境',
|
|
});
|
|
}
|
|
} catch (error) {
|
|
setKavaApiStatus({
|
|
status: 'error',
|
|
message: (error as Error).message,
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
|
|
// 检测数据库
|
|
setDatabaseStatus({ status: 'checking', message: '正在检测...' });
|
|
try {
|
|
if (window.electronAPI) {
|
|
const result = await window.electronAPI.storage.listShares();
|
|
if (result.success) {
|
|
setDatabaseStatus({
|
|
status: 'connected',
|
|
message: `${result.data?.length || 0} 个钱包`,
|
|
lastChecked: new Date(),
|
|
});
|
|
} else {
|
|
setDatabaseStatus({
|
|
status: 'error',
|
|
message: result.error || '数据库错误',
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
} else {
|
|
setDatabaseStatus({
|
|
status: 'disconnected',
|
|
message: '非 Electron 环境',
|
|
});
|
|
}
|
|
} catch (error) {
|
|
setDatabaseStatus({
|
|
status: 'error',
|
|
message: (error as Error).message,
|
|
lastChecked: new Date(),
|
|
});
|
|
}
|
|
|
|
// 判断整体就绪状态
|
|
const env = get().environment;
|
|
const hasError =
|
|
env.messageRouter.status === 'error' ||
|
|
env.kavaApi.status === 'error' ||
|
|
env.database.status === 'error';
|
|
|
|
if (hasError) {
|
|
setAppReady('error', '部分服务连接失败,请检查设置');
|
|
} else {
|
|
setAppReady('ready');
|
|
}
|
|
},
|
|
}));
|
|
|
|
// =============================================================================
|
|
// 辅助函数
|
|
// =============================================================================
|
|
|
|
export function getOverallStatus(env: EnvironmentCheck): ConnectionStatus {
|
|
const statuses = [env.messageRouter.status, env.kavaApi.status, env.database.status];
|
|
|
|
if (statuses.includes('checking')) return 'checking';
|
|
if (statuses.includes('error')) return 'error';
|
|
if (statuses.every((s) => s === 'connected')) return 'connected';
|
|
if (statuses.includes('disconnected')) return 'disconnected';
|
|
return 'unknown';
|
|
}
|
|
|
|
export function getStatusColor(status: ConnectionStatus): string {
|
|
switch (status) {
|
|
case 'connected':
|
|
return '#22c55e'; // green
|
|
case 'checking':
|
|
return '#f59e0b'; // yellow
|
|
case 'disconnected':
|
|
return '#6b7280'; // gray
|
|
case 'error':
|
|
return '#ef4444'; // red
|
|
default:
|
|
return '#6b7280'; // gray
|
|
}
|
|
}
|
|
|
|
export function getStatusText(status: ConnectionStatus): string {
|
|
switch (status) {
|
|
case 'connected':
|
|
return '已连接';
|
|
case 'checking':
|
|
return '检测中...';
|
|
case 'disconnected':
|
|
return '未连接';
|
|
case 'error':
|
|
return '连接失败';
|
|
default:
|
|
return '未知';
|
|
}
|
|
}
|