@lobehub/chat 1.80.5 → 1.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/electron-client-ipc/src/events/index.ts +6 -2
- package/packages/electron-client-ipc/src/events/remoteServer.ts +28 -0
- package/packages/electron-client-ipc/src/types/index.ts +1 -0
- package/packages/electron-client-ipc/src/types/remoteServer.ts +8 -0
- package/packages/electron-server-ipc/package.json +7 -1
- package/packages/electron-server-ipc/src/ipcClient.ts +54 -20
- package/packages/electron-server-ipc/src/ipcServer.ts +42 -9
- package/packages/web-crawler/src/crawImpl/__tests__/search1api.test.ts +33 -39
- package/packages/web-crawler/src/crawImpl/search1api.ts +1 -7
- package/packages/web-crawler/src/index.ts +1 -0
- package/packages/web-crawler/src/urlRules.ts +3 -1
- package/src/config/tools.ts +2 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/Debug.tsx +9 -3
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +21 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments.tsx +1 -1
- package/src/locales/default/plugin.ts +1 -0
- package/src/server/routers/tools/{__test__/search.test.ts → search.test.ts} +27 -5
- package/src/server/routers/tools/search.ts +3 -44
- package/src/server/services/search/impls/index.ts +30 -0
- package/src/server/services/search/impls/search1api/index.ts +154 -0
- package/src/server/services/search/impls/search1api/type.ts +81 -0
- package/src/server/{modules/SearXNG.ts → services/search/impls/searxng/client.ts} +32 -2
- package/src/server/{routers/tools/__tests__ → services/search/impls/searxng}/fixtures/searXNG.ts +2 -2
- package/src/server/services/search/impls/searxng/index.test.ts +26 -0
- package/src/server/services/search/impls/searxng/index.ts +62 -0
- package/src/server/services/search/impls/type.ts +11 -0
- package/src/server/services/search/index.ts +59 -0
- package/src/store/chat/slices/builtinTool/actions/index.ts +1 -1
- package/src/store/chat/slices/builtinTool/actions/{searXNG.test.ts → search.test.ts} +30 -55
- package/src/store/chat/slices/builtinTool/actions/{searXNG.ts → search.ts} +25 -32
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +1 -1
- package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/TitleExtra.tsx +2 -2
- package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/Video.tsx +9 -7
- package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/index.tsx +2 -2
- package/src/tools/web-browsing/Portal/Search/ResultList/index.tsx +3 -3
- package/src/tools/web-browsing/Portal/Search/index.tsx +4 -4
- package/src/tools/web-browsing/Portal/index.tsx +3 -1
- package/src/tools/web-browsing/Render/Search/SearchQuery/SearchView.tsx +4 -2
- package/src/tools/web-browsing/Render/Search/SearchQuery/index.tsx +6 -13
- package/src/tools/web-browsing/Render/Search/SearchResult/SearchResultItem.tsx +2 -2
- package/src/tools/web-browsing/Render/Search/SearchResult/index.tsx +5 -5
- package/src/tools/web-browsing/Render/Search/index.tsx +2 -2
- package/src/tools/web-browsing/Render/index.tsx +4 -3
- package/src/tools/web-browsing/components/SearchBar.tsx +4 -6
- package/src/tools/web-browsing/index.ts +54 -60
- package/src/tools/web-browsing/systemRole.ts +22 -13
- package/src/types/tool/search/index.ts +44 -0
- package/src/server/routers/tools/__tests__/search.test.ts +0 -48
- package/src/types/tool/search.ts +0 -48
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
## [Version 1.81.0](https://github.com/lobehub/lobe-chat/compare/v1.80.5...v1.81.0)
|
6
|
+
|
7
|
+
<sup>Released on **2025-04-17**</sup>
|
8
|
+
|
9
|
+
#### ✨ Features
|
10
|
+
|
11
|
+
- **misc**: Support search1api as search provider.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's improved
|
19
|
+
|
20
|
+
- **misc**: Support search1api as search provider, closes [#7449](https://github.com/lobehub/lobe-chat/issues/7449) ([2738cac](https://github.com/lobehub/lobe-chat/commit/2738cac))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.80.5](https://github.com/lobehub/lobe-chat/compare/v1.80.4...v1.80.5)
|
6
31
|
|
7
32
|
<sup>Released on **2025-04-17**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.81.0",
|
4
4
|
"description": "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.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { FilesDispatchEvents } from './file';
|
2
2
|
import { MenuDispatchEvents } from './menu';
|
3
|
+
import { RemoteServerBroadcastEvents, RemoteServerDispatchEvents } from './remoteServer';
|
3
4
|
import { FilesSearchDispatchEvents } from './search';
|
4
5
|
import { ShortcutDispatchEvents } from './shortcut';
|
5
6
|
import { SystemDispatchEvents } from './system';
|
@@ -17,7 +18,8 @@ export interface ClientDispatchEvents
|
|
17
18
|
MenuDispatchEvents,
|
18
19
|
FilesDispatchEvents,
|
19
20
|
AutoUpdateDispatchEvents,
|
20
|
-
ShortcutDispatchEvents
|
21
|
+
ShortcutDispatchEvents,
|
22
|
+
RemoteServerDispatchEvents {}
|
21
23
|
|
22
24
|
export type ClientDispatchEventKey = keyof ClientDispatchEvents;
|
23
25
|
|
@@ -29,7 +31,9 @@ export type ClientEventReturnType<T extends ClientDispatchEventKey> = ReturnType
|
|
29
31
|
* main -> render broadcast events
|
30
32
|
*/
|
31
33
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
32
|
-
export interface MainBroadcastEvents
|
34
|
+
export interface MainBroadcastEvents
|
35
|
+
extends AutoUpdateBroadcastEvents,
|
36
|
+
RemoteServerBroadcastEvents {}
|
33
37
|
|
34
38
|
export type MainBroadcastEventKey = keyof MainBroadcastEvents;
|
35
39
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { RemoteServerConfig } from '../types/remoteServer';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 远程服务器配置相关的事件
|
5
|
+
*/
|
6
|
+
export interface RemoteServerDispatchEvents {
|
7
|
+
clearRemoteServerConfig: () => boolean;
|
8
|
+
getRemoteServerConfig: () => RemoteServerConfig;
|
9
|
+
refreshAccessToken: () => {
|
10
|
+
error?: string;
|
11
|
+
success: boolean;
|
12
|
+
};
|
13
|
+
requestAuthorization: (serverUrl: string) => {
|
14
|
+
error?: string;
|
15
|
+
success: boolean;
|
16
|
+
};
|
17
|
+
setRemoteServerConfig: (config: RemoteServerConfig) => boolean;
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* 从主进程广播的远程服务器相关事件
|
22
|
+
*/
|
23
|
+
export interface RemoteServerBroadcastEvents {
|
24
|
+
authorizationFailed: (params: { error: string }) => void;
|
25
|
+
authorizationRequired: (params: void) => void;
|
26
|
+
authorizationSuccessful: (params: void) => void;
|
27
|
+
tokenRefreshed: (params: void) => void;
|
28
|
+
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import debug from 'debug';
|
1
2
|
import fs from 'node:fs';
|
2
3
|
import net from 'node:net';
|
3
4
|
import os from 'node:os';
|
@@ -6,6 +7,8 @@ import path from 'node:path';
|
|
6
7
|
import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
|
7
8
|
import { ServerDispatchEventKey } from './events';
|
8
9
|
|
10
|
+
const log = debug('electron-server-ipc:client');
|
11
|
+
|
9
12
|
export class ElectronIpcClient {
|
10
13
|
private socketPath: string | null = null;
|
11
14
|
private connected: boolean = false;
|
@@ -19,6 +22,7 @@ export class ElectronIpcClient {
|
|
19
22
|
private dataBuffer: string = '';
|
20
23
|
|
21
24
|
constructor() {
|
25
|
+
log('Initializing client');
|
22
26
|
this.initialize();
|
23
27
|
}
|
24
28
|
|
@@ -29,13 +33,16 @@ export class ElectronIpcClient {
|
|
29
33
|
const tempDir = os.tmpdir();
|
30
34
|
const socketInfoPath = path.join(tempDir, SOCK_INFO_FILE);
|
31
35
|
|
36
|
+
log('Looking for socket info at: %s', socketInfoPath);
|
32
37
|
if (fs.existsSync(socketInfoPath)) {
|
33
38
|
const socketInfo = JSON.parse(fs.readFileSync(socketInfoPath, 'utf8'));
|
34
39
|
this.socketPath = socketInfo.socketPath;
|
40
|
+
log('Found socket path: %s', this.socketPath);
|
35
41
|
} else {
|
36
42
|
// 如果找不到套接字信息,使用默认路径
|
37
43
|
this.socketPath =
|
38
44
|
process.platform === 'win32' ? WINDOW_PIPE_FILE : path.join(os.tmpdir(), SOCK_FILE);
|
45
|
+
log('Socket info not found, using default path: %s', this.socketPath);
|
39
46
|
}
|
40
47
|
} catch (err) {
|
41
48
|
console.error('Failed to initialize IPC client:', err);
|
@@ -46,21 +53,23 @@ export class ElectronIpcClient {
|
|
46
53
|
// 连接到 Electron IPC 服务器
|
47
54
|
private connect(): Promise<void> {
|
48
55
|
if (this.connected || !this.socketPath) {
|
56
|
+
log('Connection skipped: Connected=%s, SocketPath=%s', this.connected, !!this.socketPath);
|
49
57
|
return Promise.resolve();
|
50
58
|
}
|
51
59
|
|
60
|
+
log('Attempting to connect to socket: %s', this.socketPath);
|
52
61
|
return new Promise((resolve, reject) => {
|
53
62
|
try {
|
54
63
|
this.socket = net.createConnection(this.socketPath!, () => {
|
55
64
|
this.connected = true;
|
56
65
|
this.connectionAttempts = 0;
|
57
|
-
|
66
|
+
log('Connected to Electron IPC server');
|
58
67
|
resolve();
|
59
68
|
});
|
60
69
|
|
61
70
|
this.socket.on('data', (data) => {
|
62
71
|
const dataStr = data.toString();
|
63
|
-
|
72
|
+
log('Received data: %s', dataStr.length > 100 ? `${dataStr.slice(0, 100)}...` : dataStr);
|
64
73
|
|
65
74
|
// 将新数据添加到缓冲区
|
66
75
|
this.dataBuffer += dataStr;
|
@@ -70,6 +79,7 @@ export class ElectronIpcClient {
|
|
70
79
|
|
71
80
|
// 最后一个元素可能是不完整的消息,保留在缓冲区
|
72
81
|
this.dataBuffer = messages.pop() || '';
|
82
|
+
log('Buffer remainder: %d bytes', this.dataBuffer.length);
|
73
83
|
|
74
84
|
for (const message of messages) {
|
75
85
|
if (!message.trim()) continue; // 跳过空消息
|
@@ -77,41 +87,42 @@ export class ElectronIpcClient {
|
|
77
87
|
try {
|
78
88
|
const response = JSON.parse(message);
|
79
89
|
const { id, result, error } = response;
|
90
|
+
log('Parsed response for request ID: %s, has error: %s', id, !!error);
|
80
91
|
|
81
92
|
const pending = this.requestQueue.get(id);
|
82
93
|
if (pending) {
|
94
|
+
log('Found pending request for ID: %s', id);
|
83
95
|
this.requestQueue.delete(id);
|
84
96
|
if (error) {
|
97
|
+
console.error('Error in response for ID %s: %s', id, error);
|
85
98
|
pending.reject(new Error(error));
|
86
99
|
} else {
|
100
|
+
log('Resolving request ID: %s', id);
|
87
101
|
pending.resolve(result);
|
88
102
|
}
|
103
|
+
} else {
|
104
|
+
log('No pending request found for ID: %s', id);
|
89
105
|
}
|
90
106
|
} catch (err) {
|
91
|
-
console.error(
|
92
|
-
'[ElectronIpcClient] Failed to parse response:',
|
93
|
-
err,
|
94
|
-
'message:',
|
95
|
-
message,
|
96
|
-
);
|
107
|
+
console.error('Failed to parse response: %o, message: %s', err, message);
|
97
108
|
}
|
98
109
|
}
|
99
110
|
});
|
100
111
|
|
101
112
|
this.socket.on('error', (err) => {
|
102
|
-
console.error('
|
113
|
+
console.error('Socket error: %o', err);
|
103
114
|
this.connected = false;
|
104
115
|
this.handleDisconnect();
|
105
116
|
reject(err);
|
106
117
|
});
|
107
118
|
|
108
119
|
this.socket.on('close', () => {
|
109
|
-
|
120
|
+
log('Socket closed');
|
110
121
|
this.connected = false;
|
111
122
|
this.handleDisconnect();
|
112
123
|
});
|
113
124
|
} catch (err) {
|
114
|
-
console.error('
|
125
|
+
console.error('Failed to connect to IPC server: %o', err);
|
115
126
|
this.handleDisconnect();
|
116
127
|
reject(err);
|
117
128
|
}
|
@@ -120,18 +131,23 @@ export class ElectronIpcClient {
|
|
120
131
|
|
121
132
|
// 处理断开连接
|
122
133
|
private handleDisconnect() {
|
134
|
+
log('Handling disconnect, connection attempts: %d', this.connectionAttempts);
|
123
135
|
// 清除重连定时器
|
124
136
|
if (this.reconnectTimeout) {
|
125
137
|
clearTimeout(this.reconnectTimeout);
|
126
138
|
this.reconnectTimeout = null;
|
139
|
+
log('Cleared reconnect timeout');
|
127
140
|
}
|
128
141
|
|
129
142
|
// 清空数据缓冲区
|
130
143
|
this.dataBuffer = '';
|
131
144
|
|
132
145
|
// 拒绝所有待处理的请求
|
133
|
-
|
134
|
-
|
146
|
+
const pendingCount = this.requestQueue.size;
|
147
|
+
log('Rejecting %d pending requests', pendingCount);
|
148
|
+
for (const [id, { reject }] of this.requestQueue) {
|
149
|
+
log('Rejecting request ID: %s', id);
|
150
|
+
reject(new Error('Connection to Electron IPC server lost'));
|
135
151
|
}
|
136
152
|
this.requestQueue.clear();
|
137
153
|
|
@@ -139,52 +155,66 @@ export class ElectronIpcClient {
|
|
139
155
|
if (this.connectionAttempts < this.maxConnectionAttempts) {
|
140
156
|
this.connectionAttempts++;
|
141
157
|
const delay = Math.min(1000 * Math.pow(2, this.connectionAttempts - 1), 30_000);
|
158
|
+
log('Scheduling reconnection attempt %d in %dms', this.connectionAttempts, delay);
|
142
159
|
|
143
160
|
this.reconnectTimeout = setTimeout(() => {
|
161
|
+
log('Attempting reconnection #%d', this.connectionAttempts);
|
144
162
|
this.connect().catch((err) => {
|
145
|
-
console.error(
|
146
|
-
`[ElectronIpcClient] Reconnection attempt ${this.connectionAttempts} failed:`,
|
147
|
-
err,
|
148
|
-
);
|
163
|
+
console.error('Reconnection attempt %d failed: %o', this.connectionAttempts, err);
|
149
164
|
});
|
150
165
|
}, delay);
|
166
|
+
} else {
|
167
|
+
log('Reached maximum connection attempts, giving up');
|
151
168
|
}
|
152
169
|
}
|
153
170
|
|
154
171
|
// 发送请求到 Electron IPC 服务器
|
155
172
|
public async sendRequest<T>(method: ServerDispatchEventKey, params: any = {}): Promise<T> {
|
156
173
|
if (!this.socketPath) {
|
157
|
-
|
174
|
+
console.error('Cannot send request: Electron IPC connection not available');
|
175
|
+
throw new Error('Electron IPC connection not available');
|
158
176
|
}
|
159
177
|
|
160
178
|
// 如果未连接,先连接
|
161
179
|
if (!this.connected) {
|
180
|
+
log('Not connected, connecting before sending request');
|
162
181
|
await this.connect();
|
163
182
|
}
|
164
183
|
|
184
|
+
log('Sending request: %s %o', method, params);
|
165
185
|
return new Promise<T>((resolve, reject) => {
|
166
186
|
try {
|
167
187
|
const id = Math.random().toString(36).slice(2, 15);
|
168
188
|
const request = { id, method, params };
|
189
|
+
log('Created request with ID: %s', id);
|
169
190
|
|
170
191
|
// 将请求添加到队列
|
171
192
|
this.requestQueue.set(id, { reject, resolve });
|
193
|
+
log('Added request to queue, current queue size: %d', this.requestQueue.size);
|
172
194
|
|
173
195
|
// 设置超时
|
174
196
|
const timeout = setTimeout(() => {
|
175
197
|
this.requestQueue.delete(id);
|
176
|
-
|
198
|
+
const errorMsg = `Request timed out, method: ${method}`;
|
199
|
+
console.error('Request timed out, ID: %s, method: %s', id, method);
|
200
|
+
reject(new Error(errorMsg));
|
177
201
|
}, 5000);
|
178
202
|
|
179
203
|
// 发送请求
|
180
|
-
|
204
|
+
const requestJson = JSON.stringify(request);
|
205
|
+
log('Writing request to socket, size: %d bytes', requestJson.length);
|
206
|
+
this.socket!.write(requestJson, (err) => {
|
181
207
|
if (err) {
|
182
208
|
clearTimeout(timeout);
|
183
209
|
this.requestQueue.delete(id);
|
210
|
+
console.error('Failed to write request to socket: %o', err);
|
184
211
|
reject(err);
|
212
|
+
} else {
|
213
|
+
log('Request successfully written to socket, ID: %s', id);
|
185
214
|
}
|
186
215
|
});
|
187
216
|
} catch (err) {
|
217
|
+
console.error('Error sending request: %o', err);
|
188
218
|
reject(err);
|
189
219
|
}
|
190
220
|
});
|
@@ -192,16 +222,20 @@ export class ElectronIpcClient {
|
|
192
222
|
|
193
223
|
// 关闭连接
|
194
224
|
public close() {
|
225
|
+
log('Closing client connection');
|
195
226
|
if (this.reconnectTimeout) {
|
196
227
|
clearTimeout(this.reconnectTimeout);
|
197
228
|
this.reconnectTimeout = null;
|
229
|
+
log('Cleared reconnect timeout');
|
198
230
|
}
|
199
231
|
|
200
232
|
if (this.socket) {
|
233
|
+
log('Ending socket connection');
|
201
234
|
this.socket.end();
|
202
235
|
this.socket = null;
|
203
236
|
}
|
204
237
|
|
205
238
|
this.connected = false;
|
239
|
+
log('Client connection closed');
|
206
240
|
}
|
207
241
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import debug from 'debug';
|
1
2
|
import fs from 'node:fs';
|
2
3
|
import net from 'node:net';
|
3
4
|
import os from 'node:os';
|
@@ -7,6 +8,8 @@ import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
|
|
7
8
|
import { ServerDispatchEventKey } from './events';
|
8
9
|
import { ElectronIPCEventHandler } from './types';
|
9
10
|
|
11
|
+
const log = debug('electron-server-ipc:server');
|
12
|
+
|
10
13
|
export class ElectronIPCServer {
|
11
14
|
private server: net.Server;
|
12
15
|
private socketPath: string;
|
@@ -19,10 +22,12 @@ export class ElectronIPCServer {
|
|
19
22
|
|
20
23
|
// 如果是 Unix 套接字,确保文件不存在
|
21
24
|
if (!isWindows && fs.existsSync(this.socketPath)) {
|
25
|
+
log('Removing existing socket file at: %s', this.socketPath);
|
22
26
|
fs.unlinkSync(this.socketPath);
|
23
27
|
}
|
24
28
|
|
25
29
|
// 创建服务器
|
30
|
+
log('Creating IPC server');
|
26
31
|
this.server = net.createServer(this.handleConnection.bind(this));
|
27
32
|
|
28
33
|
this.eventHandler = eventHandler;
|
@@ -30,18 +35,20 @@ export class ElectronIPCServer {
|
|
30
35
|
|
31
36
|
// 启动服务器
|
32
37
|
public start(): Promise<void> {
|
38
|
+
log('Starting IPC server');
|
33
39
|
return new Promise((resolve, reject) => {
|
34
40
|
this.server.on('error', (err) => {
|
35
|
-
console.error('IPC Server error:', err);
|
41
|
+
console.error('IPC Server error: %o', err);
|
36
42
|
reject(err);
|
37
43
|
});
|
38
44
|
|
39
45
|
this.server.listen(this.socketPath, () => {
|
40
|
-
|
46
|
+
log('Electron IPC server listening on %s', this.socketPath);
|
41
47
|
|
42
48
|
// 将套接字路径写入临时文件,供 Next.js 服务端读取
|
43
49
|
const tempDir = os.tmpdir();
|
44
50
|
const socketInfoPath = path.join(tempDir, SOCK_INFO_FILE);
|
51
|
+
log('Writing socket info to: %s', socketInfoPath);
|
45
52
|
fs.writeFileSync(socketInfoPath, JSON.stringify({ socketPath: this.socketPath }), 'utf8');
|
46
53
|
|
47
54
|
resolve();
|
@@ -52,62 +59,88 @@ export class ElectronIPCServer {
|
|
52
59
|
// 处理客户端连接
|
53
60
|
private handleConnection(socket: net.Socket): void {
|
54
61
|
let dataBuffer = '';
|
62
|
+
log('New client connection established');
|
55
63
|
|
56
64
|
socket.on('data', (data) => {
|
57
|
-
|
65
|
+
const chunk = data.toString();
|
66
|
+
log('Received data chunk, size: %d bytes', chunk.length);
|
67
|
+
dataBuffer += chunk;
|
58
68
|
|
59
69
|
try {
|
60
70
|
// 尝试解析 JSON 消息
|
61
71
|
const message = JSON.parse(dataBuffer);
|
72
|
+
log('Successfully parsed JSON message: %o', {
|
73
|
+
id: message.id,
|
74
|
+
method: message.method,
|
75
|
+
});
|
62
76
|
dataBuffer = ''; // 重置缓冲区
|
63
77
|
|
64
78
|
// 处理请求
|
65
79
|
this.handleRequest(socket, message);
|
66
80
|
} catch {
|
67
81
|
// 如果不是有效的 JSON,可能是消息不完整,继续等待
|
82
|
+
log('Incomplete or invalid JSON, buffering for more data');
|
68
83
|
}
|
69
84
|
});
|
70
85
|
|
71
86
|
socket.on('error', (err) => {
|
72
|
-
console.error('Socket error:', err);
|
87
|
+
console.error('Socket error: %o', err);
|
88
|
+
});
|
89
|
+
|
90
|
+
socket.on('close', () => {
|
91
|
+
log('Client connection closed');
|
73
92
|
});
|
74
93
|
}
|
75
94
|
|
76
95
|
// 处理客户端请求
|
77
96
|
private handleRequest = async (socket: net.Socket, request: any) => {
|
78
97
|
const { id, method, params } = request;
|
98
|
+
log('Handling request: %s (ID: %s)', method, id);
|
79
99
|
|
80
100
|
// 根据请求方法执行相应的操作
|
81
101
|
const eventHandler = this.eventHandler[method as ServerDispatchEventKey];
|
82
|
-
if (!eventHandler)
|
102
|
+
if (!eventHandler) {
|
103
|
+
console.error('No handler found for method: %s', method);
|
104
|
+
return;
|
105
|
+
}
|
83
106
|
|
84
107
|
try {
|
108
|
+
log('Executing handler for method: %s with params: %o', method, params);
|
85
109
|
const data = await eventHandler(params, { id, method, socket });
|
110
|
+
log('Handler execution successful for method: %s', method);
|
86
111
|
|
87
112
|
this.sendResult(socket, id, data);
|
88
113
|
} catch (err) {
|
89
|
-
|
114
|
+
const errorMsg = `Failed to handle method(${method}): ${(err as Error).message}`;
|
115
|
+
console.error('Error handling request: %s', errorMsg);
|
116
|
+
this.sendError(socket, id, errorMsg);
|
90
117
|
}
|
91
118
|
};
|
92
119
|
|
93
120
|
// 发送结果
|
94
121
|
private sendResult(socket: net.Socket, id: string, result: any): void {
|
95
|
-
|
122
|
+
const response = JSON.stringify({ id, result }) + '\n';
|
123
|
+
log('Sending success response for ID: %s, size: %d bytes', id, response.length);
|
124
|
+
socket.write(response);
|
96
125
|
}
|
97
126
|
|
98
127
|
// 发送错误
|
99
128
|
private sendError(socket: net.Socket, id: string, error: string): void {
|
100
|
-
|
129
|
+
const response = JSON.stringify({ error, id }) + '\n';
|
130
|
+
log('Sending error response for ID: %s: %s', id, error);
|
131
|
+
socket.write(response);
|
101
132
|
}
|
102
133
|
|
103
134
|
// 关闭服务器
|
104
135
|
public close(): Promise<void> {
|
136
|
+
log('Closing IPC server');
|
105
137
|
return new Promise((resolve) => {
|
106
138
|
this.server.close(() => {
|
107
|
-
|
139
|
+
log('Electron IPC server closed');
|
108
140
|
|
109
141
|
// 删除套接字文件(Unix 平台)
|
110
142
|
if (process.platform !== 'win32' && fs.existsSync(this.socketPath)) {
|
143
|
+
log('Removing socket file: %s', this.socketPath);
|
111
144
|
fs.unlinkSync(this.socketPath);
|
112
145
|
}
|
113
146
|
|