@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
text/typescript
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();
});
});
}
}