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 * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import express from 'express';
|
||||
import { GrpcClient } from './modules/grpc-client';
|
||||
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() {
|
||||
// 初始化数据库 (必须首先初始化)
|
||||
database = new DatabaseManager();
|
||||
|
||||
// 初始化 gRPC 客户端
|
||||
grpcClient = new GrpcClient();
|
||||
|
||||
// 初始化数据库
|
||||
database = new DatabaseManager();
|
||||
|
||||
// 初始化 Kava 交易服务
|
||||
kavaTxService = new KavaTxService(KAVA_MAINNET_TX_CONFIG);
|
||||
|
||||
|
|
@ -102,6 +116,39 @@ async function initServices() {
|
|||
|
||||
// 设置 IPC 处理器
|
||||
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 通信处理器
|
||||
|
|
@ -111,9 +158,9 @@ function setupIpcHandlers() {
|
|||
// ===========================================================================
|
||||
|
||||
// gRPC 连接
|
||||
ipcMain.handle('grpc:connect', async (_event, { host, port }) => {
|
||||
ipcMain.handle('grpc:connect', async (_event, { url }) => {
|
||||
try {
|
||||
await grpcClient?.connect(host, port);
|
||||
await grpcClient?.connect(url);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
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)
|
||||
ipcMain.handle('grpc:validateSigningSession', async (_event, { code }) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,19 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||
getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) =>
|
||||
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) =>
|
||||
ipcRenderer.invoke('grpc:validateSigningSession', { code }),
|
||||
|
|
|
|||
|
|
@ -101,25 +101,61 @@ export const useAppStore = create<AppState>((set, get) => ({
|
|||
checkAllServices: async () => {
|
||||
const { setMessageRouterStatus, setKavaApiStatus, setDatabaseStatus, setAppReady } = get();
|
||||
|
||||
// 检测 Message Router
|
||||
// 检测 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';
|
||||
const result = await window.electronAPI.grpc.testConnection(routerUrl);
|
||||
if (result.success) {
|
||||
|
||||
// 1. 检查是否已连接
|
||||
const connResult = await window.electronAPI.grpc.isConnected();
|
||||
if (!connResult.connected) {
|
||||
setMessageRouterStatus({
|
||||
status: 'connected',
|
||||
message: routerUrl,
|
||||
status: 'disconnected',
|
||||
message: '未连接到 ' + routerUrl,
|
||||
lastChecked: new Date(),
|
||||
});
|
||||
} else {
|
||||
setMessageRouterStatus({
|
||||
status: 'error',
|
||||
message: result.error || '连接失败',
|
||||
lastChecked: new Date(),
|
||||
});
|
||||
// 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({
|
||||
|
|
|
|||
|
|
@ -430,6 +430,14 @@ interface ElectronAPI {
|
|||
testConnection: (url: string) => Promise<TestConnectionResult>;
|
||||
// 获取已注册的参与方列表
|
||||
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>;
|
||||
joinSigningSession: (params: JoinSigningSessionParams) => Promise<JoinSigningSessionResult>;
|
||||
|
|
|
|||
Loading…
Reference in New Issue