300 lines
8.4 KiB
TypeScript
300 lines
8.4 KiB
TypeScript
import { app, BrowserWindow, ipcMain, shell, dialog } from 'electron';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
import express from 'express';
|
|
import { GrpcClient } from './modules/grpc-client';
|
|
import { SecureStorage } from './modules/storage';
|
|
|
|
// 内置 HTTP 服务器端口
|
|
const HTTP_PORT = 3456;
|
|
|
|
let mainWindow: BrowserWindow | null = null;
|
|
let grpcClient: GrpcClient | null = null;
|
|
let storage: SecureStorage | null = null;
|
|
let httpServer: ReturnType<typeof express.application.listen> | null = null;
|
|
|
|
// 创建主窗口
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1200,
|
|
height: 800,
|
|
minWidth: 800,
|
|
minHeight: 600,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
},
|
|
titleBarStyle: 'hiddenInset',
|
|
show: false,
|
|
});
|
|
|
|
// 开发模式下加载 Vite 开发服务器
|
|
if (process.env.NODE_ENV === 'development') {
|
|
mainWindow.loadURL('http://localhost:5173');
|
|
mainWindow.webContents.openDevTools();
|
|
} else {
|
|
// 生产模式下加载打包后的文件
|
|
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
|
|
}
|
|
|
|
mainWindow.once('ready-to-show', () => {
|
|
mainWindow?.show();
|
|
});
|
|
|
|
mainWindow.on('closed', () => {
|
|
mainWindow = null;
|
|
});
|
|
|
|
// 处理外部链接
|
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
shell.openExternal(url);
|
|
return { action: 'deny' };
|
|
});
|
|
}
|
|
|
|
// 启动内置 HTTP 服务器
|
|
function startHttpServer() {
|
|
const expressApp = express();
|
|
|
|
expressApp.use(express.json());
|
|
expressApp.use(express.static(path.join(__dirname, '../dist')));
|
|
|
|
// API 路由
|
|
expressApp.get('/api/status', (_req, res) => {
|
|
res.json({
|
|
connected: grpcClient?.isConnected() ?? false,
|
|
partyId: grpcClient?.getPartyId() ?? null,
|
|
});
|
|
});
|
|
|
|
// 所有其他路由返回 index.html (SPA)
|
|
expressApp.get('*', (_req, res) => {
|
|
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
|
});
|
|
|
|
httpServer = expressApp.listen(HTTP_PORT, '127.0.0.1', () => {
|
|
console.log(`HTTP server running at http://127.0.0.1:${HTTP_PORT}`);
|
|
});
|
|
}
|
|
|
|
// 初始化服务
|
|
async function initServices() {
|
|
// 初始化 gRPC 客户端
|
|
grpcClient = new GrpcClient();
|
|
|
|
// 初始化安全存储
|
|
storage = new SecureStorage();
|
|
|
|
// 设置 IPC 处理器
|
|
setupIpcHandlers();
|
|
}
|
|
|
|
// 设置 IPC 通信处理器
|
|
function setupIpcHandlers() {
|
|
// gRPC 连接
|
|
ipcMain.handle('grpc:connect', async (_event, { host, port }) => {
|
|
try {
|
|
await grpcClient?.connect(host, port);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 注册为参与方
|
|
ipcMain.handle('grpc:register', async (_event, { partyId, role }) => {
|
|
try {
|
|
await grpcClient?.registerParty(partyId, role);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 加入会话
|
|
ipcMain.handle('grpc:joinSession', async (_event, { sessionId, partyId, joinToken }) => {
|
|
try {
|
|
const result = await grpcClient?.joinSession(sessionId, partyId, joinToken);
|
|
return { success: true, data: result };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 保存 share
|
|
ipcMain.handle('storage:saveShare', async (_event, { share, password }) => {
|
|
try {
|
|
storage?.saveShare(share, password);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 获取 share 列表
|
|
ipcMain.handle('storage:listShares', async () => {
|
|
try {
|
|
const shares = storage?.listShares() ?? [];
|
|
return { success: true, data: shares };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 导出 share
|
|
ipcMain.handle('storage:exportShare', async (_event, { id, password }) => {
|
|
try {
|
|
const data = storage?.exportShare(id, password);
|
|
return { success: true, data };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 导入 share (从文件路径)
|
|
ipcMain.handle('storage:importShare', async (_event, { filePath, password }) => {
|
|
try {
|
|
const data = fs.readFileSync(filePath);
|
|
const share = storage?.importShare(data, password);
|
|
return { success: true, share };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 获取单个 share
|
|
ipcMain.handle('storage:getShare', async (_event, { id, password }) => {
|
|
try {
|
|
const share = storage?.getShare(id, password);
|
|
return share;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// 存储 - 删除 share
|
|
ipcMain.handle('storage:deleteShare', async (_event, { id }) => {
|
|
try {
|
|
storage?.deleteShare(id);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// 存储 - 获取设置
|
|
ipcMain.handle('storage:getSettings', async () => {
|
|
try {
|
|
return storage?.getSettings() ?? null;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// 存储 - 保存设置
|
|
ipcMain.handle('storage:saveSettings', async (_event, { settings }) => {
|
|
try {
|
|
storage?.saveSettings(settings);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// gRPC - 创建会话
|
|
ipcMain.handle('grpc:createSession', async (_event, params) => {
|
|
// TODO: 实现创建会话逻辑
|
|
return { success: false, error: '功能尚未实现 - 需要连接到 Session Coordinator' };
|
|
});
|
|
|
|
// gRPC - 验证邀请码
|
|
ipcMain.handle('grpc:validateInviteCode', async (_event, { code }) => {
|
|
// TODO: 实现验证邀请码逻辑
|
|
return { success: false, error: '功能尚未实现 - 需要连接到 Session Coordinator' };
|
|
});
|
|
|
|
// gRPC - 获取会话状态
|
|
ipcMain.handle('grpc:getSessionStatus', async (_event, { sessionId }) => {
|
|
// TODO: 实现获取会话状态逻辑
|
|
return { success: false, error: '功能尚未实现' };
|
|
});
|
|
|
|
// gRPC - 测试连接
|
|
ipcMain.handle('grpc:testConnection', async (_event, { url }) => {
|
|
try {
|
|
// 地址格式: host:port (例如 mpc-grpc.szaiai.com:443)
|
|
await grpcClient?.connect(url);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: (error as Error).message };
|
|
}
|
|
});
|
|
|
|
// gRPC - 验证签名会话
|
|
ipcMain.handle('grpc:validateSigningSession', async (_event, { code }) => {
|
|
// TODO: 实现验证签名会话逻辑
|
|
return { success: false, error: '功能尚未实现 - 需要连接到 Session Coordinator' };
|
|
});
|
|
|
|
// gRPC - 加入签名会话
|
|
ipcMain.handle('grpc:joinSigningSession', async (_event, params) => {
|
|
// TODO: 实现加入签名会话逻辑
|
|
return { success: false, error: '功能尚未实现' };
|
|
});
|
|
|
|
// 对话框 - 选择目录
|
|
ipcMain.handle('dialog:selectDirectory', async () => {
|
|
const result = await dialog.showOpenDialog(mainWindow!, {
|
|
properties: ['openDirectory'],
|
|
});
|
|
return result.canceled ? null : result.filePaths[0];
|
|
});
|
|
|
|
// 对话框 - 选择文件
|
|
ipcMain.handle('dialog:selectFile', async (_event, { filters }) => {
|
|
const result = await dialog.showOpenDialog(mainWindow!, {
|
|
properties: ['openFile'],
|
|
filters: filters || [],
|
|
});
|
|
return result.canceled ? null : result.filePaths[0];
|
|
});
|
|
|
|
// 对话框 - 保存文件
|
|
ipcMain.handle('dialog:saveFile', async (_event, { defaultPath, filters }) => {
|
|
const result = await dialog.showSaveDialog(mainWindow!, {
|
|
defaultPath,
|
|
filters: filters || [],
|
|
});
|
|
return result.canceled ? null : result.filePath;
|
|
});
|
|
}
|
|
|
|
// 应用生命周期
|
|
app.whenReady().then(async () => {
|
|
await initServices();
|
|
// HTTP 服务器仅在开发模式下启动
|
|
if (process.env.NODE_ENV === 'development') {
|
|
startHttpServer();
|
|
}
|
|
createWindow();
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createWindow();
|
|
}
|
|
});
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') {
|
|
app.quit();
|
|
}
|
|
});
|
|
|
|
app.on('before-quit', () => {
|
|
// 清理资源
|
|
grpcClient?.disconnect();
|
|
httpServer?.close();
|
|
});
|