UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

161 lines (133 loc) 5.1 kB
import debug from 'debug'; import fs from 'node:fs'; import net from 'node:net'; import os from 'node:os'; import path from 'node:path'; import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const'; import { ServerDispatchEventKey } from './events'; import { ElectronIPCEventHandler } from './types'; const log = debug('electron-server-ipc:server'); export class ElectronIPCServer { private server: net.Server; private socketPath: string; private appId: string; private eventHandler: ElectronIPCEventHandler; constructor(appId: string, eventHandler: ElectronIPCEventHandler) { this.appId = appId; const isWindows = process.platform === 'win32'; // 创建唯一的套接字路径,避免冲突 this.socketPath = isWindows ? WINDOW_PIPE_FILE(appId) : path.join(os.tmpdir(), SOCK_FILE(appId)); // 如果是 Unix 套接字,确保文件不存在 if (!isWindows && fs.existsSync(this.socketPath)) { log('Removing existing socket file at: %s', this.socketPath); fs.unlinkSync(this.socketPath); } // 创建服务器 log('Creating IPC server'); this.server = net.createServer(this.handleConnection.bind(this)); this.eventHandler = eventHandler; } // 启动服务器 public start(): Promise<void> { log('Starting IPC server'); return new Promise((resolve, reject) => { this.server.on('error', (err) => { console.error('IPC Server error: %o', err); reject(err); }); this.server.listen(this.socketPath, () => { log('Electron IPC server listening on %s', this.socketPath); // 将套接字路径写入临时文件,供 Next.js 服务端读取 const tempDir = os.tmpdir(); const socketInfoPath = path.join(tempDir, SOCK_INFO_FILE(this.appId)); log('Writing socket info to: %s', socketInfoPath); fs.writeFileSync(socketInfoPath, JSON.stringify({ socketPath: this.socketPath }), 'utf8'); resolve(); }); }); } // 处理客户端连接 private handleConnection(socket: net.Socket): void { let dataBuffer = ''; log('New client connection established'); socket.on('data', (data) => { const chunk = data.toString(); log('Received data chunk, size: %d bytes', chunk.length); dataBuffer += chunk; // 按 \n\n 分割消息 const messages = dataBuffer.split('\n'); // 保留最后一个可能不完整的消息 dataBuffer = messages.pop() || ''; // 处理每个完整的消息 for (const message of messages) { if (!message.trim()) continue; try { const parsedMessage = JSON.parse(message); log('Successfully parsed JSON message: %o', { id: parsedMessage.id, method: parsedMessage.method, }); this.handleRequest(socket, parsedMessage); } catch (err) { console.error('Failed to parse message: %s', err); } } }); socket.on('error', (err) => { console.error('Socket error: %o', err); }); socket.on('close', () => { log('Client connection closed'); }); } // 处理客户端请求 private handleRequest = async (socket: net.Socket, request: any) => { const { id, method, params } = request; log('Handling request: %s (ID: %s)', method, id); // 根据请求方法执行相应的操作 const eventHandler = this.eventHandler[method as ServerDispatchEventKey]; if (!eventHandler) { console.error('No handler found for method: %s', method); return; } try { log('Executing handler for method: %s with params: %o', method, params); const data = await eventHandler(params, { id, method, socket }); log('Handler execution successful for method: %s', method); this.sendResult(socket, id, data); } catch (err) { const errorMsg = `Failed to handle method(${method}): ${(err as Error).message}`; console.error('Error handling request: %s', errorMsg); this.sendError(socket, id, errorMsg); } }; // 发送结果 private sendResult(socket: net.Socket, id: string, result: any): void { const response = JSON.stringify({ id, result }) + '\n\n'; log('Sending success response for ID: %s, size: %d bytes', id, response.length); socket.write(response); } // 发送错误 private sendError(socket: net.Socket, id: string, error: string): void { const response = JSON.stringify({ error, id }) + '\n\n'; log('Sending error response for ID: %s: %s', id, error); socket.write(response); } // 关闭服务器 public close(): Promise<void> { log('Closing IPC server'); return new Promise((resolve) => { this.server.close(() => { log('Electron IPC server closed'); // 删除套接字文件(Unix 平台) if (process.platform !== 'win32' && fs.existsSync(this.socketPath)) { log('Removing socket file: %s', this.socketPath); fs.unlinkSync(this.socketPath); } resolve(); }); }); } }