@zhin.js/client 1.0.16 → 1.0.18
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/client/app.ts +228 -0
- package/client/index.ts +42 -14
- package/client/websocket/hooks.ts +218 -510
- package/client/websocket/index.ts +6 -43
- package/client/websocket/instance.ts +10 -32
- package/client/websocket/manager.ts +173 -390
- package/client/websocket/types.ts +58 -222
- package/dist/app.d.ts +95 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +147 -0
- package/dist/app.js.map +1 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -14
- package/dist/index.js.map +1 -1
- package/dist/websocket/hooks.d.ts +35 -58
- package/dist/websocket/hooks.d.ts.map +1 -1
- package/dist/websocket/hooks.js +134 -410
- package/dist/websocket/hooks.js.map +1 -1
- package/dist/websocket/index.d.ts +5 -10
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +5 -25
- package/dist/websocket/index.js.map +1 -1
- package/dist/websocket/instance.d.ts +1 -14
- package/dist/websocket/instance.d.ts.map +1 -1
- package/dist/websocket/instance.js +2 -20
- package/dist/websocket/instance.js.map +1 -1
- package/dist/websocket/manager.d.ts +23 -93
- package/dist/websocket/manager.d.ts.map +1 -1
- package/dist/websocket/manager.js +87 -227
- package/dist/websocket/manager.js.map +1 -1
- package/dist/websocket/types.d.ts +35 -122
- package/dist/websocket/types.d.ts.map +1 -1
- package/dist/websocket/types.js +4 -13
- package/dist/websocket/types.js.map +1 -1
- package/package.json +12 -11
- package/client/router/index.tsx +0 -54
- package/client/store/index.ts +0 -132
- package/client/store/reducers/config.ts +0 -135
- package/client/store/reducers/index.ts +0 -19
- package/client/store/reducers/route.ts +0 -126
- package/client/store/reducers/script.ts +0 -109
- package/client/store/reducers/ui.ts +0 -31
- package/client/websocket/messageHandler.ts +0 -208
- package/dist/router/index.d.ts +0 -25
- package/dist/router/index.d.ts.map +0 -1
- package/dist/router/index.js +0 -48
- package/dist/router/index.js.map +0 -1
- package/dist/store/index.d.ts +0 -19
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/index.js +0 -67
- package/dist/store/index.js.map +0 -1
- package/dist/store/reducers/config.d.ts +0 -54
- package/dist/store/reducers/config.d.ts.map +0 -1
- package/dist/store/reducers/config.js +0 -78
- package/dist/store/reducers/config.js.map +0 -1
- package/dist/store/reducers/index.d.ts +0 -13
- package/dist/store/reducers/index.d.ts.map +0 -1
- package/dist/store/reducers/index.js +0 -11
- package/dist/store/reducers/index.js.map +0 -1
- package/dist/store/reducers/route.d.ts +0 -30
- package/dist/store/reducers/route.d.ts.map +0 -1
- package/dist/store/reducers/route.js +0 -85
- package/dist/store/reducers/route.js.map +0 -1
- package/dist/store/reducers/script.d.ts +0 -13
- package/dist/store/reducers/script.d.ts.map +0 -1
- package/dist/store/reducers/script.js +0 -78
- package/dist/store/reducers/script.js.map +0 -1
- package/dist/store/reducers/ui.d.ts +0 -8
- package/dist/store/reducers/ui.d.ts.map +0 -1
- package/dist/store/reducers/ui.js +0 -23
- package/dist/store/reducers/ui.js.map +0 -1
- package/dist/websocket/messageHandler.d.ts +0 -58
- package/dist/websocket/messageHandler.d.ts.map +0 -1
- package/dist/websocket/messageHandler.js +0 -174
- package/dist/websocket/messageHandler.js.map +0 -1
|
@@ -1,510 +1,293 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocket 核心管理器
|
|
3
|
-
* 负责 WebSocket 连接管理、消息发送、重连逻辑等核心功能
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { store, setConnected, updateConfigs, updateSchemas } from '../store'
|
|
7
|
-
import { MessageHandler } from './messageHandler'
|
|
8
1
|
import type {
|
|
9
2
|
WebSocketMessage,
|
|
10
3
|
WebSocketConfig,
|
|
11
4
|
WebSocketCallbacks,
|
|
12
|
-
|
|
13
|
-
DatabaseInfo,
|
|
14
|
-
TableInfo,
|
|
15
|
-
SelectResult,
|
|
16
|
-
KvEntry
|
|
17
|
-
} from './types'
|
|
5
|
+
} from "./types";
|
|
18
6
|
import {
|
|
19
7
|
ConnectionState,
|
|
20
8
|
WebSocketError,
|
|
21
9
|
ConnectionError,
|
|
22
10
|
MessageError,
|
|
23
|
-
RequestTimeoutError
|
|
24
|
-
} from
|
|
11
|
+
RequestTimeoutError,
|
|
12
|
+
} from "./types";
|
|
25
13
|
|
|
26
14
|
export class WebSocketManager {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
private
|
|
33
|
-
private
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
// 请求管理
|
|
41
|
-
private requestId = 0
|
|
42
|
-
private pendingRequests = new Map<number, {
|
|
43
|
-
resolve: (value: any) => void
|
|
44
|
-
reject: (error: Error) => void
|
|
45
|
-
timer: NodeJS.Timeout
|
|
46
|
-
}>()
|
|
47
|
-
|
|
48
|
-
// ============================================================================
|
|
49
|
-
// 构造函数
|
|
50
|
-
// ============================================================================
|
|
15
|
+
private ws: WebSocket | null = null;
|
|
16
|
+
private config: Required<WebSocketConfig>;
|
|
17
|
+
private callbacks: WebSocketCallbacks;
|
|
18
|
+
private state: ConnectionState = ConnectionState.DISCONNECTED;
|
|
19
|
+
private reconnectAttempts = 0;
|
|
20
|
+
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
21
|
+
private requestId = 0;
|
|
22
|
+
private pendingRequests = new Map<
|
|
23
|
+
number,
|
|
24
|
+
{ resolve: (value: unknown) => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }
|
|
25
|
+
>();
|
|
26
|
+
private connectedListeners = new Set<(connected: boolean) => void>();
|
|
51
27
|
|
|
52
28
|
constructor(config: WebSocketConfig = {}, callbacks: WebSocketCallbacks = {}) {
|
|
53
29
|
this.config = {
|
|
54
30
|
url: this.buildWebSocketUrl(config.url),
|
|
55
31
|
reconnectInterval: config.reconnectInterval ?? 3000,
|
|
56
32
|
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
57
|
-
requestTimeout: config.requestTimeout ?? 10000
|
|
58
|
-
}
|
|
59
|
-
this.callbacks = callbacks
|
|
33
|
+
requestTimeout: config.requestTimeout ?? 10000,
|
|
34
|
+
};
|
|
35
|
+
this.callbacks = callbacks;
|
|
60
36
|
}
|
|
61
37
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
38
|
+
onConnectionChange(listener: (connected: boolean) => void): () => void {
|
|
39
|
+
this.connectedListeners.add(listener);
|
|
40
|
+
return () => this.connectedListeners.delete(listener);
|
|
41
|
+
}
|
|
65
42
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
connect(): void {
|
|
70
|
-
if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) {
|
|
71
|
-
return
|
|
72
|
-
}
|
|
43
|
+
private notifyConnection(connected: boolean) {
|
|
44
|
+
for (const l of this.connectedListeners) l(connected);
|
|
45
|
+
}
|
|
73
46
|
|
|
74
|
-
|
|
75
|
-
|
|
47
|
+
connect(): void {
|
|
48
|
+
if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) return;
|
|
49
|
+
this.setState(ConnectionState.CONNECTING);
|
|
76
50
|
try {
|
|
77
|
-
this.ws = new WebSocket(this.config.url)
|
|
78
|
-
this.attachEventHandlers()
|
|
51
|
+
this.ws = new WebSocket(this.config.url);
|
|
52
|
+
this.attachEventHandlers();
|
|
79
53
|
} catch (error) {
|
|
80
|
-
this.handleConnectionError(new ConnectionError(
|
|
54
|
+
this.handleConnectionError(new ConnectionError("Failed to create WebSocket", error as Error));
|
|
81
55
|
}
|
|
82
56
|
}
|
|
83
57
|
|
|
84
|
-
/**
|
|
85
|
-
* 断开 WebSocket 连接
|
|
86
|
-
*/
|
|
87
58
|
disconnect(): void {
|
|
88
|
-
this.clearReconnectTimer()
|
|
89
|
-
this.clearPendingRequests()
|
|
90
|
-
|
|
59
|
+
this.clearReconnectTimer();
|
|
60
|
+
this.clearPendingRequests();
|
|
91
61
|
if (this.ws) {
|
|
92
|
-
this.ws.close()
|
|
93
|
-
this.ws = null
|
|
62
|
+
this.ws.close();
|
|
63
|
+
this.ws = null;
|
|
94
64
|
}
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
store.dispatch(setConnected(false))
|
|
65
|
+
this.setState(ConnectionState.DISCONNECTED);
|
|
66
|
+
this.notifyConnection(false);
|
|
98
67
|
}
|
|
99
68
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*/
|
|
103
|
-
send(message: any): void {
|
|
104
|
-
if (!this.isConnected()) {
|
|
105
|
-
throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED')
|
|
106
|
-
}
|
|
107
|
-
|
|
69
|
+
send(message: unknown): void {
|
|
70
|
+
if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
|
|
108
71
|
try {
|
|
109
|
-
this.ws!.send(JSON.stringify(message))
|
|
72
|
+
this.ws!.send(JSON.stringify(message));
|
|
110
73
|
} catch (error) {
|
|
111
|
-
throw new MessageError(
|
|
74
|
+
throw new MessageError("Failed to send message", error as Error);
|
|
112
75
|
}
|
|
113
76
|
}
|
|
114
77
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*/
|
|
118
|
-
async sendRequest<T = any>(message: any): Promise<T> {
|
|
119
|
-
if (!this.isConnected()) {
|
|
120
|
-
throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED')
|
|
121
|
-
}
|
|
122
|
-
|
|
78
|
+
async sendRequest<T = unknown>(message: unknown): Promise<T> {
|
|
79
|
+
if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
|
|
123
80
|
return new Promise((resolve, reject) => {
|
|
124
|
-
const requestId = ++this.requestId
|
|
125
|
-
const messageWithId = { ...message, requestId }
|
|
126
|
-
|
|
127
|
-
// 设置超时计时器
|
|
81
|
+
const requestId = ++this.requestId;
|
|
82
|
+
const messageWithId = { ...(message as Record<string, unknown>), requestId };
|
|
128
83
|
const timer = setTimeout(() => {
|
|
129
|
-
this.pendingRequests.delete(requestId)
|
|
130
|
-
reject(new RequestTimeoutError(requestId))
|
|
131
|
-
}, this.config.requestTimeout)
|
|
132
|
-
|
|
133
|
-
// 存储请求信息
|
|
134
|
-
this.pendingRequests.set(requestId, { resolve, reject, timer })
|
|
135
|
-
|
|
84
|
+
this.pendingRequests.delete(requestId);
|
|
85
|
+
reject(new RequestTimeoutError(requestId));
|
|
86
|
+
}, this.config.requestTimeout);
|
|
87
|
+
this.pendingRequests.set(requestId, { resolve: resolve as (v: unknown) => void, reject, timer });
|
|
136
88
|
try {
|
|
137
|
-
this.ws!.send(JSON.stringify(messageWithId))
|
|
89
|
+
this.ws!.send(JSON.stringify(messageWithId));
|
|
138
90
|
} catch (error) {
|
|
139
|
-
this.pendingRequests.delete(requestId)
|
|
140
|
-
clearTimeout(timer)
|
|
141
|
-
reject(new MessageError(
|
|
91
|
+
this.pendingRequests.delete(requestId);
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
reject(new MessageError("Failed to send request", error as Error));
|
|
142
94
|
}
|
|
143
|
-
})
|
|
95
|
+
});
|
|
144
96
|
}
|
|
145
97
|
|
|
146
|
-
/**
|
|
147
|
-
* 检查连接状态
|
|
148
|
-
*/
|
|
149
98
|
isConnected(): boolean {
|
|
150
|
-
return this.ws?.readyState === WebSocket.OPEN
|
|
99
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
151
100
|
}
|
|
152
101
|
|
|
153
|
-
/**
|
|
154
|
-
* 获取当前状态
|
|
155
|
-
*/
|
|
156
102
|
getState(): ConnectionState {
|
|
157
|
-
return this.state
|
|
103
|
+
return this.state;
|
|
158
104
|
}
|
|
159
105
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// ============================================================================
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* 获取插件配置
|
|
166
|
-
*/
|
|
167
|
-
async getConfig(pluginName: string): Promise<any> {
|
|
168
|
-
return this.sendRequest({
|
|
169
|
-
type: 'config:get',
|
|
170
|
-
pluginName
|
|
171
|
-
})
|
|
106
|
+
async getConfig(pluginName: string) {
|
|
107
|
+
return this.sendRequest<unknown>({ type: "config:get", pluginName });
|
|
172
108
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
* 设置插件配置
|
|
176
|
-
*/
|
|
177
|
-
async setConfig(pluginName: string, config: any): Promise<{ success?: boolean; reloaded?: boolean; message?: string }> {
|
|
178
|
-
return this.sendRequest({
|
|
179
|
-
type: 'config:set',
|
|
180
|
-
pluginName,
|
|
181
|
-
data: config
|
|
182
|
-
})
|
|
109
|
+
async setConfig(pluginName: string, config: unknown) {
|
|
110
|
+
return this.sendRequest<{ success?: boolean; reloaded?: boolean; message?: string }>({ type: "config:set", pluginName, data: config });
|
|
183
111
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
* 获取插件 Schema
|
|
187
|
-
*/
|
|
188
|
-
async getSchema(pluginName: string): Promise<any> {
|
|
189
|
-
return this.sendRequest({
|
|
190
|
-
type: 'schema:get',
|
|
191
|
-
pluginName
|
|
192
|
-
})
|
|
112
|
+
async getSchema(pluginName: string) {
|
|
113
|
+
return this.sendRequest<unknown>({ type: "schema:get", pluginName });
|
|
193
114
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
* 获取所有配置
|
|
197
|
-
*/
|
|
198
|
-
async getAllConfigs(): Promise<Record<string, any>> {
|
|
199
|
-
return this.sendRequest({
|
|
200
|
-
type: 'config:get-all'
|
|
201
|
-
})
|
|
115
|
+
async getAllConfigs() {
|
|
116
|
+
return this.sendRequest<Record<string, unknown>>({ type: "config:get-all" });
|
|
202
117
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
* 获取所有 Schema
|
|
206
|
-
*/
|
|
207
|
-
async getAllSchemas(): Promise<Record<string, any>> {
|
|
208
|
-
return this.sendRequest({
|
|
209
|
-
type: 'schema:get-all'
|
|
210
|
-
})
|
|
118
|
+
async getAllSchemas() {
|
|
119
|
+
return this.sendRequest<Record<string, unknown>>({ type: "schema:get-all" });
|
|
211
120
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// 配置文件原始 YAML 读写
|
|
215
|
-
// ============================================================================
|
|
216
|
-
|
|
217
|
-
async getConfigYaml(): Promise<{ yaml: string; pluginKeys: string[] }> {
|
|
218
|
-
return this.sendRequest({ type: 'config:get-yaml' })
|
|
121
|
+
async getConfigYaml() {
|
|
122
|
+
return this.sendRequest<{ yaml: string; pluginKeys: string[] }>({ type: "config:get-yaml" });
|
|
219
123
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return this.sendRequest({ type: 'config:save-yaml', yaml })
|
|
124
|
+
async saveConfigYaml(yaml: string) {
|
|
125
|
+
return this.sendRequest<{ success: boolean; message?: string }>({ type: "config:save-yaml", yaml });
|
|
223
126
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// 环境变量文件管理
|
|
227
|
-
// ============================================================================
|
|
228
|
-
|
|
229
|
-
async getEnvList(): Promise<{ files: Array<{ name: string; exists: boolean }> }> {
|
|
230
|
-
return this.sendRequest({ type: 'env:list' })
|
|
127
|
+
async getEnvList() {
|
|
128
|
+
return this.sendRequest<{ files: Array<{ name: string; exists: boolean }> }>({ type: "env:list" });
|
|
231
129
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return this.sendRequest({ type: 'env:get', filename })
|
|
130
|
+
async getEnvFile(filename: string) {
|
|
131
|
+
return this.sendRequest<{ content: string }>({ type: "env:get", filename });
|
|
235
132
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return this.sendRequest({ type: 'env:save', filename, content })
|
|
133
|
+
async saveEnvFile(filename: string, content: string) {
|
|
134
|
+
return this.sendRequest<{ success: boolean; message?: string }>({ type: "env:save", filename, content });
|
|
239
135
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// 文件管理
|
|
243
|
-
// ============================================================================
|
|
244
|
-
|
|
245
|
-
async getFileTree(): Promise<{ tree: FileTreeNode[] }> {
|
|
246
|
-
return this.sendRequest({ type: 'files:tree' })
|
|
136
|
+
async getFileTree() {
|
|
137
|
+
return this.sendRequest<{ tree: import("./types").FileTreeNode[] }>({ type: "files:tree" });
|
|
247
138
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return this.sendRequest({ type: 'files:read', filePath })
|
|
139
|
+
async readFile(filePath: string) {
|
|
140
|
+
return this.sendRequest<{ content: string; size: number }>({ type: "files:read", filePath });
|
|
251
141
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return this.sendRequest({ type: 'files:save', filePath, content })
|
|
142
|
+
async saveFile(filePath: string, content: string) {
|
|
143
|
+
return this.sendRequest<{ success: boolean; message?: string }>({ type: "files:save", filePath, content });
|
|
255
144
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// 数据库管理
|
|
259
|
-
// ============================================================================
|
|
260
|
-
|
|
261
|
-
async getDbInfo(): Promise<DatabaseInfo> {
|
|
262
|
-
return this.sendRequest({ type: 'db:info' })
|
|
145
|
+
async getDbInfo() {
|
|
146
|
+
return this.sendRequest<import("./types").DatabaseInfo>({ type: "db:info" });
|
|
263
147
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return this.sendRequest({ type: 'db:tables' })
|
|
148
|
+
async getDbTables() {
|
|
149
|
+
return this.sendRequest<{ tables: import("./types").TableInfo[] }>({ type: "db:tables" });
|
|
267
150
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return this.sendRequest({ type: 'db:select', table, page, pageSize, where })
|
|
151
|
+
async dbSelect(table: string, page?: number, pageSize?: number, where?: unknown) {
|
|
152
|
+
return this.sendRequest<import("./types").SelectResult>({ type: "db:select", table, page, pageSize, where });
|
|
271
153
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return this.sendRequest({ type: 'db:insert', table, row })
|
|
154
|
+
async dbInsert(table: string, row: unknown) {
|
|
155
|
+
return this.sendRequest<{ success: boolean }>({ type: "db:insert", table, row });
|
|
275
156
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return this.sendRequest({ type: 'db:update', table, row, where })
|
|
157
|
+
async dbUpdate(table: string, row: unknown, where: unknown) {
|
|
158
|
+
return this.sendRequest<{ success: boolean; affected: number }>({ type: "db:update", table, row, where });
|
|
279
159
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return this.sendRequest({ type: 'db:delete', table, where })
|
|
160
|
+
async dbDelete(table: string, where: unknown) {
|
|
161
|
+
return this.sendRequest<{ success: boolean; deleted: number }>({ type: "db:delete", table, where });
|
|
283
162
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return this.sendRequest({ type: 'db:drop-table', table })
|
|
163
|
+
async dbDropTable(table: string) {
|
|
164
|
+
return this.sendRequest<{ success: boolean }>({ type: "db:drop-table", table });
|
|
287
165
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return this.sendRequest({ type: 'db:kv:get', table, key })
|
|
166
|
+
async kvGet(table: string, key: string) {
|
|
167
|
+
return this.sendRequest<{ key: string; value: unknown }>({ type: "db:kv:get", table, key });
|
|
291
168
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return this.sendRequest({ type: 'db:kv:set', table, key, value, ttl })
|
|
169
|
+
async kvSet(table: string, key: string, value: unknown, ttl?: number) {
|
|
170
|
+
return this.sendRequest<{ success: boolean }>({ type: "db:kv:set", table, key, value, ttl });
|
|
295
171
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return this.sendRequest({ type: 'db:kv:delete', table, key })
|
|
172
|
+
async kvDelete(table: string, key: string) {
|
|
173
|
+
return this.sendRequest<{ success: boolean }>({ type: "db:kv:delete", table, key });
|
|
299
174
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return this.sendRequest({ type: 'db:kv:entries', table })
|
|
175
|
+
async kvGetEntries(table: string) {
|
|
176
|
+
return this.sendRequest<{ entries: import("./types").KvEntry[] }>({ type: "db:kv:entries", table });
|
|
303
177
|
}
|
|
304
178
|
|
|
305
|
-
// ============================================================================
|
|
306
|
-
// 私有方法
|
|
307
|
-
// ============================================================================
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* 构建 WebSocket URL
|
|
311
|
-
*/
|
|
312
179
|
private buildWebSocketUrl(customUrl?: string): string {
|
|
313
|
-
if (customUrl)
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
318
|
-
const host = window.location.host
|
|
319
|
-
return `${protocol}//${host}/server`
|
|
180
|
+
if (customUrl) return customUrl;
|
|
181
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
182
|
+
return `${protocol}//${window.location.host}/server`;
|
|
320
183
|
}
|
|
321
184
|
|
|
322
|
-
/**
|
|
323
|
-
* 设置连接状态
|
|
324
|
-
*/
|
|
325
185
|
private setState(newState: ConnectionState): void {
|
|
326
|
-
|
|
327
|
-
this.state = newState
|
|
328
|
-
|
|
329
|
-
console.debug(`[WebSocket] State changed: ${oldState} -> ${newState}`)
|
|
186
|
+
this.state = newState;
|
|
330
187
|
}
|
|
331
188
|
|
|
332
|
-
/**
|
|
333
|
-
* 绑定事件处理器
|
|
334
|
-
*/
|
|
335
189
|
private attachEventHandlers(): void {
|
|
336
|
-
if (!this.ws) return
|
|
337
|
-
|
|
338
|
-
this.ws.
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
this.ws.onmessage = (event) => {
|
|
343
|
-
this.handleMessage(event)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
this.ws.onclose = (event) => {
|
|
347
|
-
this.handleConnectionClose(event)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
this.ws.onerror = (event) => {
|
|
351
|
-
this.handleConnectionError(new ConnectionError('WebSocket error', event as any))
|
|
352
|
-
}
|
|
190
|
+
if (!this.ws) return;
|
|
191
|
+
this.ws.onopen = () => this.handleConnectionOpen();
|
|
192
|
+
this.ws.onmessage = (event) => this.handleMessage(event);
|
|
193
|
+
this.ws.onclose = () => this.handleConnectionClose();
|
|
194
|
+
this.ws.onerror = (event) => this.handleConnectionError(new ConnectionError("WebSocket error", event as unknown as Error));
|
|
353
195
|
}
|
|
354
196
|
|
|
355
|
-
/**
|
|
356
|
-
* 处理连接打开
|
|
357
|
-
*/
|
|
358
197
|
private handleConnectionOpen(): void {
|
|
359
|
-
this.setState(ConnectionState.CONNECTED)
|
|
360
|
-
this.reconnectAttempts = 0
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
store.dispatch(setConnected(true))
|
|
364
|
-
|
|
365
|
-
// 初始化数据
|
|
366
|
-
this.initializeData()
|
|
367
|
-
|
|
368
|
-
// 触发回调
|
|
369
|
-
this.callbacks.onConnect?.()
|
|
198
|
+
this.setState(ConnectionState.CONNECTED);
|
|
199
|
+
this.reconnectAttempts = 0;
|
|
200
|
+
this.notifyConnection(true);
|
|
201
|
+
this.callbacks.onConnect?.();
|
|
370
202
|
}
|
|
371
203
|
|
|
372
|
-
/**
|
|
373
|
-
* 处理消息接收
|
|
374
|
-
*/
|
|
375
204
|
private handleMessage(event: MessageEvent): void {
|
|
376
205
|
try {
|
|
377
|
-
const message
|
|
378
|
-
|
|
379
|
-
// 处理请求响应
|
|
206
|
+
const message = JSON.parse(event.data) as WebSocketMessage;
|
|
380
207
|
if (message.requestId && this.pendingRequests.has(message.requestId)) {
|
|
381
|
-
this.handleRequestResponse(message)
|
|
382
|
-
return
|
|
208
|
+
this.handleRequestResponse(message);
|
|
209
|
+
return;
|
|
383
210
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
MessageHandler.handle(message)
|
|
387
|
-
this.callbacks.onMessage?.(message)
|
|
388
|
-
|
|
211
|
+
this.handleBroadcast(message);
|
|
212
|
+
this.callbacks.onMessage?.(message);
|
|
389
213
|
} catch (error) {
|
|
390
|
-
console.error(
|
|
214
|
+
console.error("[WebSocket] Message parsing error:", error);
|
|
391
215
|
}
|
|
392
216
|
}
|
|
393
217
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (!pendingRequest) {
|
|
402
|
-
return
|
|
218
|
+
private handleBroadcast(message: WebSocketMessage): void {
|
|
219
|
+
const t = message.type;
|
|
220
|
+
if (t === "bot:request" || t === "bot:notice" || t === "bot:message") {
|
|
221
|
+
if (typeof window !== "undefined") {
|
|
222
|
+
window.dispatchEvent(new CustomEvent("zhin-console-bot-push", { detail: message }));
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
403
225
|
}
|
|
226
|
+
if (t === "hmr:reload") {
|
|
227
|
+
console.info(`[HMR] File changed, reloading...`);
|
|
228
|
+
window.location.reload();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (t === "system:restarting") {
|
|
232
|
+
console.info("[System] Server restarting, will reload shortly...");
|
|
233
|
+
setTimeout(() => window.location.reload(), 3000);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
404
237
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
238
|
+
private handleRequestResponse(message: WebSocketMessage): void {
|
|
239
|
+
const { requestId } = message;
|
|
240
|
+
const pending = this.pendingRequests.get(requestId!);
|
|
241
|
+
if (!pending) return;
|
|
242
|
+
this.pendingRequests.delete(requestId!);
|
|
243
|
+
clearTimeout(pending.timer);
|
|
408
244
|
if (message.error) {
|
|
409
|
-
|
|
245
|
+
pending.reject(new WebSocketError(message.error, "SERVER_ERROR"));
|
|
410
246
|
} else {
|
|
411
|
-
|
|
247
|
+
pending.resolve(message.data);
|
|
412
248
|
}
|
|
413
249
|
}
|
|
414
250
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (this.state === ConnectionState.DISCONNECTED) {
|
|
423
|
-
// 主动断开,不重连
|
|
424
|
-
return
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
this.setState(ConnectionState.RECONNECTING)
|
|
428
|
-
this.callbacks.onDisconnect?.()
|
|
429
|
-
this.scheduleReconnect()
|
|
251
|
+
private handleConnectionClose(): void {
|
|
252
|
+
this.ws = null;
|
|
253
|
+
this.notifyConnection(false);
|
|
254
|
+
if (this.state === ConnectionState.DISCONNECTED) return;
|
|
255
|
+
this.setState(ConnectionState.RECONNECTING);
|
|
256
|
+
this.callbacks.onDisconnect?.();
|
|
257
|
+
this.scheduleReconnect();
|
|
430
258
|
}
|
|
431
259
|
|
|
432
|
-
/**
|
|
433
|
-
* 处理连接错误
|
|
434
|
-
*/
|
|
435
260
|
private handleConnectionError(error: Error): void {
|
|
436
|
-
this.clearReconnectTimer()
|
|
437
|
-
this.setState(ConnectionState.ERROR)
|
|
438
|
-
console.error(
|
|
439
|
-
this.callbacks.onError?.(error as
|
|
261
|
+
this.clearReconnectTimer();
|
|
262
|
+
this.setState(ConnectionState.ERROR);
|
|
263
|
+
console.error("[WebSocket] Connection error:", error);
|
|
264
|
+
this.callbacks.onError?.(error as unknown as Event);
|
|
440
265
|
}
|
|
441
266
|
|
|
442
|
-
/**
|
|
443
|
-
* 安排重连
|
|
444
|
-
*/
|
|
445
267
|
private scheduleReconnect(): void {
|
|
446
268
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return
|
|
269
|
+
this.setState(ConnectionState.ERROR);
|
|
270
|
+
return;
|
|
450
271
|
}
|
|
451
|
-
|
|
452
|
-
this.reconnectAttempts
|
|
453
|
-
const delay = this.config.reconnectInterval * this.reconnectAttempts
|
|
454
|
-
|
|
455
|
-
console.debug(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`)
|
|
456
|
-
|
|
272
|
+
this.reconnectAttempts++;
|
|
273
|
+
const delay = this.config.reconnectInterval * this.reconnectAttempts;
|
|
457
274
|
this.reconnectTimer = setTimeout(() => {
|
|
458
|
-
if (this.state === ConnectionState.RECONNECTING)
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
}, delay)
|
|
275
|
+
if (this.state === ConnectionState.RECONNECTING) this.connect();
|
|
276
|
+
}, delay);
|
|
462
277
|
}
|
|
463
278
|
|
|
464
|
-
/**
|
|
465
|
-
* 清除重连计时器
|
|
466
|
-
*/
|
|
467
279
|
private clearReconnectTimer(): void {
|
|
468
280
|
if (this.reconnectTimer) {
|
|
469
|
-
clearTimeout(this.reconnectTimer)
|
|
470
|
-
this.reconnectTimer = null
|
|
281
|
+
clearTimeout(this.reconnectTimer);
|
|
282
|
+
this.reconnectTimer = null;
|
|
471
283
|
}
|
|
472
284
|
}
|
|
473
285
|
|
|
474
|
-
/**
|
|
475
|
-
* 清除所有待处理请求
|
|
476
|
-
*/
|
|
477
286
|
private clearPendingRequests(): void {
|
|
478
|
-
for (const [
|
|
479
|
-
clearTimeout(timer)
|
|
480
|
-
reject(new WebSocketError(
|
|
481
|
-
}
|
|
482
|
-
this.pendingRequests.clear()
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* 初始化数据
|
|
487
|
-
*/
|
|
488
|
-
private async initializeData(): Promise<void> {
|
|
489
|
-
try {
|
|
490
|
-
// 并行获取所有配置和 Schema
|
|
491
|
-
const [configs, schemas] = await Promise.all([
|
|
492
|
-
this.getAllConfigs().catch(error => {
|
|
493
|
-
console.warn('[WebSocket] Failed to load configs:', error)
|
|
494
|
-
return {}
|
|
495
|
-
}),
|
|
496
|
-
this.getAllSchemas().catch(error => {
|
|
497
|
-
console.warn('[WebSocket] Failed to load schemas:', error)
|
|
498
|
-
return {}
|
|
499
|
-
})
|
|
500
|
-
])
|
|
501
|
-
|
|
502
|
-
// 更新 Redux 状态
|
|
503
|
-
store.dispatch(updateConfigs(configs))
|
|
504
|
-
store.dispatch(updateSchemas(schemas))
|
|
505
|
-
|
|
506
|
-
} catch (error) {
|
|
507
|
-
console.error('[WebSocket] Data initialization failed:', error)
|
|
287
|
+
for (const [, { reject, timer }] of this.pendingRequests) {
|
|
288
|
+
clearTimeout(timer);
|
|
289
|
+
reject(new WebSocketError("Connection closed", "CONNECTION_CLOSED"));
|
|
508
290
|
}
|
|
291
|
+
this.pendingRequests.clear();
|
|
509
292
|
}
|
|
510
|
-
}
|
|
293
|
+
}
|