feat(service-party-app): 实现启动时自动注册到 Message Router,状态验证真实化
## 主要变更 ### 1. 启动时自动连接并注册 - main.ts: 添加 `connectAndRegisterToMessageRouter()` 函数 - 应用启动时自动连接到 Message Router 并注册为 temporary 角色 - 自动生成并持久化 partyId(使用 crypto.randomUUID) - 自动订阅会话事件 ### 2. 状态验证真实化 - appStore.ts: 重写 `checkAllServices()` 消息路由检测逻辑 - 不再只检测连接成功,而是: 1. 调用 isConnected() 检查连接状态 2. 调用 getPartyId() 检查是否已注册 3. 调用 getRegisteredParties() 从 Message Router 获取注册列表 4. 验证自己的 partyId 是否在列表中 - 状态显示更准确: - "未连接到 xxx" - 未连接 - "已连接但未注册" - 已连接但没注册 - "已注册 (在线)" - 完全正常 - "注册验证失败" - 注册了但验证失败 ### 3. 新增 IPC API - grpc:getPartyId - 获取当前 partyId - grpc:isConnected - 检查连接状态 - grpc:connect - 连接到 Message Router - grpc:register - 注册为参与方 ### 修改的文件 - electron/main.ts - electron/preload.ts - src/stores/appStore.ts - src/types/electron.d.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
73034c072c
commit
28da7f6807
|
|
@ -1,6 +1,7 @@
|
||||||
import { app, BrowserWindow, ipcMain, shell, dialog } from 'electron';
|
import { app, BrowserWindow, ipcMain, shell, dialog } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { GrpcClient } from './modules/grpc-client';
|
import { GrpcClient } from './modules/grpc-client';
|
||||||
import { DatabaseManager } from './modules/database';
|
import { DatabaseManager } from './modules/database';
|
||||||
|
|
@ -83,14 +84,27 @@ function startHttpServer() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成或获取持久化的 partyId
|
||||||
|
function getOrCreatePartyId(db: DatabaseManager): string {
|
||||||
|
const settings = db.getAllSettings();
|
||||||
|
let partyId = settings['party_id'];
|
||||||
|
if (!partyId) {
|
||||||
|
// 生成一个新的 UUID 作为 partyId
|
||||||
|
partyId = crypto.randomUUID();
|
||||||
|
db.setSetting('party_id', partyId);
|
||||||
|
console.log('Generated new partyId:', partyId);
|
||||||
|
}
|
||||||
|
return partyId;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化服务
|
// 初始化服务
|
||||||
async function initServices() {
|
async function initServices() {
|
||||||
|
// 初始化数据库 (必须首先初始化)
|
||||||
|
database = new DatabaseManager();
|
||||||
|
|
||||||
// 初始化 gRPC 客户端
|
// 初始化 gRPC 客户端
|
||||||
grpcClient = new GrpcClient();
|
grpcClient = new GrpcClient();
|
||||||
|
|
||||||
// 初始化数据库
|
|
||||||
database = new DatabaseManager();
|
|
||||||
|
|
||||||
// 初始化 Kava 交易服务
|
// 初始化 Kava 交易服务
|
||||||
kavaTxService = new KavaTxService(KAVA_MAINNET_TX_CONFIG);
|
kavaTxService = new KavaTxService(KAVA_MAINNET_TX_CONFIG);
|
||||||
|
|
||||||
|
|
@ -102,6 +116,39 @@ async function initServices() {
|
||||||
|
|
||||||
// 设置 IPC 处理器
|
// 设置 IPC 处理器
|
||||||
setupIpcHandlers();
|
setupIpcHandlers();
|
||||||
|
|
||||||
|
// 启动时自动连接并注册到 Message Router
|
||||||
|
await connectAndRegisterToMessageRouter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接并注册到 Message Router
|
||||||
|
async function connectAndRegisterToMessageRouter() {
|
||||||
|
if (!grpcClient || !database) {
|
||||||
|
console.error('gRPC client or database not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const settings = database.getAllSettings();
|
||||||
|
const routerUrl = settings['message_router_url'] || 'mpc-grpc.szaiai.com:443';
|
||||||
|
const partyId = getOrCreatePartyId(database);
|
||||||
|
const role = 'temporary'; // Service-Party-App 使用 temporary 角色
|
||||||
|
|
||||||
|
console.log(`Connecting to Message Router: ${routerUrl}...`);
|
||||||
|
await grpcClient.connect(routerUrl);
|
||||||
|
console.log('Connected to Message Router');
|
||||||
|
|
||||||
|
console.log(`Registering as party: ${partyId} (role: ${role})...`);
|
||||||
|
await grpcClient.registerParty(partyId, role);
|
||||||
|
console.log('Registered to Message Router successfully');
|
||||||
|
|
||||||
|
// 订阅会话事件
|
||||||
|
grpcClient.subscribeSessionEvents(partyId);
|
||||||
|
console.log('Subscribed to session events');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to connect/register to Message Router:', (error as Error).message);
|
||||||
|
// 不抛出错误,允许应用继续启动,用户可以稍后手动重试
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 IPC 通信处理器
|
// 设置 IPC 通信处理器
|
||||||
|
|
@ -111,9 +158,9 @@ function setupIpcHandlers() {
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
// gRPC 连接
|
// gRPC 连接
|
||||||
ipcMain.handle('grpc:connect', async (_event, { host, port }) => {
|
ipcMain.handle('grpc:connect', async (_event, { url }) => {
|
||||||
try {
|
try {
|
||||||
await grpcClient?.connect(host, port);
|
await grpcClient?.connect(url);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error: (error as Error).message };
|
return { success: false, error: (error as Error).message };
|
||||||
|
|
@ -227,6 +274,26 @@ function setupIpcHandlers() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// gRPC - 获取当前 partyId
|
||||||
|
ipcMain.handle('grpc:getPartyId', async () => {
|
||||||
|
try {
|
||||||
|
const partyId = grpcClient?.getPartyId();
|
||||||
|
return { success: true, partyId: partyId || null };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: (error as Error).message, partyId: null };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// gRPC - 检查连接状态
|
||||||
|
ipcMain.handle('grpc:isConnected', async () => {
|
||||||
|
try {
|
||||||
|
const connected = grpcClient?.isConnected() || false;
|
||||||
|
return { success: true, connected };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: (error as Error).message, connected: false };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// gRPC - 验证签名会话 (通过 Account 服务 HTTP API)
|
// gRPC - 验证签名会话 (通过 Account 服务 HTTP API)
|
||||||
ipcMain.handle('grpc:validateSigningSession', async (_event, { code }) => {
|
ipcMain.handle('grpc:validateSigningSession', async (_event, { code }) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,19 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) =>
|
getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) =>
|
||||||
ipcRenderer.invoke('grpc:getRegisteredParties', { roleFilter, onlyOnline }),
|
ipcRenderer.invoke('grpc:getRegisteredParties', { roleFilter, onlyOnline }),
|
||||||
|
|
||||||
|
// 获取当前 partyId
|
||||||
|
getPartyId: () => ipcRenderer.invoke('grpc:getPartyId'),
|
||||||
|
|
||||||
|
// 检查连接状态
|
||||||
|
isConnected: () => ipcRenderer.invoke('grpc:isConnected'),
|
||||||
|
|
||||||
|
// 连接到 Message Router
|
||||||
|
connect: (url: string) => ipcRenderer.invoke('grpc:connect', { url }),
|
||||||
|
|
||||||
|
// 注册为参与方
|
||||||
|
register: (partyId: string, role: string) =>
|
||||||
|
ipcRenderer.invoke('grpc:register', { partyId, role }),
|
||||||
|
|
||||||
// 签名相关
|
// 签名相关
|
||||||
validateSigningSession: (code: string) =>
|
validateSigningSession: (code: string) =>
|
||||||
ipcRenderer.invoke('grpc:validateSigningSession', { code }),
|
ipcRenderer.invoke('grpc:validateSigningSession', { code }),
|
||||||
|
|
|
||||||
|
|
@ -101,25 +101,61 @@ export const useAppStore = create<AppState>((set, get) => ({
|
||||||
checkAllServices: async () => {
|
checkAllServices: async () => {
|
||||||
const { setMessageRouterStatus, setKavaApiStatus, setDatabaseStatus, setAppReady } = get();
|
const { setMessageRouterStatus, setKavaApiStatus, setDatabaseStatus, setAppReady } = get();
|
||||||
|
|
||||||
// 检测 Message Router
|
// 检测 Message Router - 真实验证注册状态
|
||||||
setMessageRouterStatus({ status: 'checking', message: '正在检测...' });
|
setMessageRouterStatus({ status: 'checking', message: '正在检测...' });
|
||||||
try {
|
try {
|
||||||
if (window.electronAPI) {
|
if (window.electronAPI) {
|
||||||
const settings = await window.electronAPI.storage.getSettings();
|
const settings = await window.electronAPI.storage.getSettings();
|
||||||
const routerUrl = settings?.messageRouterUrl || 'mpc-grpc.szaiai.com:443';
|
const routerUrl = settings?.messageRouterUrl || 'mpc-grpc.szaiai.com:443';
|
||||||
const result = await window.electronAPI.grpc.testConnection(routerUrl);
|
|
||||||
if (result.success) {
|
// 1. 检查是否已连接
|
||||||
|
const connResult = await window.electronAPI.grpc.isConnected();
|
||||||
|
if (!connResult.connected) {
|
||||||
setMessageRouterStatus({
|
setMessageRouterStatus({
|
||||||
status: 'connected',
|
status: 'disconnected',
|
||||||
message: routerUrl,
|
message: '未连接到 ' + routerUrl,
|
||||||
lastChecked: new Date(),
|
lastChecked: new Date(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setMessageRouterStatus({
|
// 2. 已连接,检查是否已注册
|
||||||
status: 'error',
|
const partyIdResult = await window.electronAPI.grpc.getPartyId();
|
||||||
message: result.error || '连接失败',
|
if (!partyIdResult.partyId) {
|
||||||
lastChecked: new Date(),
|
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 {
|
} else {
|
||||||
setMessageRouterStatus({
|
setMessageRouterStatus({
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,14 @@ interface ElectronAPI {
|
||||||
testConnection: (url: string) => Promise<TestConnectionResult>;
|
testConnection: (url: string) => Promise<TestConnectionResult>;
|
||||||
// 获取已注册的参与方列表
|
// 获取已注册的参与方列表
|
||||||
getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) => Promise<GetRegisteredPartiesResult>;
|
getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) => Promise<GetRegisteredPartiesResult>;
|
||||||
|
// 获取当前 partyId
|
||||||
|
getPartyId: () => Promise<{ success: boolean; partyId: string | null; error?: string }>;
|
||||||
|
// 检查连接状态
|
||||||
|
isConnected: () => Promise<{ success: boolean; connected: boolean; error?: string }>;
|
||||||
|
// 连接到 Message Router
|
||||||
|
connect: (url: string) => Promise<{ success: boolean; error?: string }>;
|
||||||
|
// 注册为参与方
|
||||||
|
register: (partyId: string, role: string) => Promise<{ success: boolean; error?: string }>;
|
||||||
// 签名相关
|
// 签名相关
|
||||||
validateSigningSession: (code: string) => Promise<ValidateSigningSessionResult>;
|
validateSigningSession: (code: string) => Promise<ValidateSigningSessionResult>;
|
||||||
joinSigningSession: (params: JoinSigningSessionParams) => Promise<JoinSigningSessionResult>;
|
joinSigningSession: (params: JoinSigningSessionParams) => Promise<JoinSigningSessionResult>;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue