alemonjs 2.1.0-alpha.32 → 2.1.0-alpha.34
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/lib/cbp/index.js +274 -189
- package/lib/cbp/router.js +142 -25
- package/lib/typing/event/map.js +20 -0
- package/package.json +1 -1
package/lib/cbp/index.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
import Koa from 'koa';
|
|
2
2
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
3
|
import router from './router.js';
|
|
4
|
-
import koaStatic from 'koa-static';
|
|
5
4
|
import koaCors from '@koa/cors';
|
|
6
5
|
import { ResultCode } from '../core/code.js';
|
|
7
|
-
import {
|
|
6
|
+
import { platformClient, childrenClient, fullClient, childrenBind, USER_AGENT_HEADER, USER_AGENT_HEADER_VALUE_MAP, DEVICE_ID_HEADER, FULL_RECEIVE_HEADER } from './config.js';
|
|
8
7
|
import { getConfig } from '../core/config.js';
|
|
9
8
|
import * as flattedJSON from 'flatted';
|
|
9
|
+
import { connectionTestOne } from './testone.js';
|
|
10
10
|
|
|
11
|
+
function getReConnectTime() {
|
|
12
|
+
const time = 1000 * 1;
|
|
13
|
+
const curTime = time;
|
|
14
|
+
const mTime = (curTime / 1000 / 60).toFixed(2);
|
|
15
|
+
logger.info({
|
|
16
|
+
code: ResultCode.Fail,
|
|
17
|
+
message: `[ws-discord] 等待 ${mTime} 分钟后重新连接`,
|
|
18
|
+
data: null
|
|
19
|
+
});
|
|
20
|
+
return curTime;
|
|
21
|
+
}
|
|
11
22
|
const cbpServer = (port, listeningListener) => {
|
|
12
23
|
if (global.chatbotServer) {
|
|
13
24
|
delete global.chatbotServer;
|
|
14
25
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const parsedMessage = flattedJSON.parse(message.toString());
|
|
51
|
-
// 1. 解析得到 actionId ,说明是消费行为请求。要广播告诉所有客户端。
|
|
52
|
-
// 2. 解析得到 name ,说明是一个事件请求。
|
|
53
|
-
// 3. 解析得到 apiId ,说明是一个接口请求。
|
|
54
|
-
logger.debug({
|
|
55
|
-
code: ResultCode.Ok,
|
|
56
|
-
message: '服务端接收到消息',
|
|
57
|
-
data: parsedMessage
|
|
58
|
-
});
|
|
59
|
-
if (parsedMessage.apiId) {
|
|
26
|
+
const createServer = () => {
|
|
27
|
+
try {
|
|
28
|
+
// create
|
|
29
|
+
const app = new Koa();
|
|
30
|
+
// MessageRouter
|
|
31
|
+
app.use(router.routes());
|
|
32
|
+
app.use(router.allowedMethods());
|
|
33
|
+
// Cors
|
|
34
|
+
app.use(koaCors({
|
|
35
|
+
origin: '*', // 允许所有来源
|
|
36
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'] // 允许的 HTTP 方法
|
|
37
|
+
}));
|
|
38
|
+
const server = app.listen(port, listeningListener);
|
|
39
|
+
// 创建 WebSocketServer 并监听同一个端口
|
|
40
|
+
global.chatbotServer = new WebSocketServer({ server });
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param originId
|
|
44
|
+
* @param ws
|
|
45
|
+
*/
|
|
46
|
+
const setPlatformClient = (originId, ws) => {
|
|
47
|
+
// 仅允许有一个平台连接
|
|
48
|
+
if (platformClient.size > 0) {
|
|
49
|
+
logger.error({
|
|
50
|
+
code: ResultCode.Fail,
|
|
51
|
+
message: `平台连接已存在: ${originId}`,
|
|
52
|
+
data: null
|
|
53
|
+
});
|
|
54
|
+
ws.close(); // 关闭新连接
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// 设置平台客户端
|
|
58
|
+
platformClient.set(originId, ws);
|
|
59
|
+
// 处理api
|
|
60
|
+
const handleApi = (DeviceId, message) => {
|
|
60
61
|
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
61
|
-
const DeviceId = parsedMessage.DeviceId;
|
|
62
62
|
if (childrenClient.has(DeviceId)) {
|
|
63
63
|
const clientWs = childrenClient.get(DeviceId);
|
|
64
64
|
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
@@ -81,10 +81,9 @@ const cbpServer = (port, listeningListener) => {
|
|
|
81
81
|
fullClient.delete(DeviceId);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const DeviceId = parsedMessage.DeviceId;
|
|
84
|
+
};
|
|
85
|
+
// 处理 action
|
|
86
|
+
const handleAction = (DeviceId, message) => {
|
|
88
87
|
if (childrenClient.has(DeviceId)) {
|
|
89
88
|
const clientWs = childrenClient.get(DeviceId);
|
|
90
89
|
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
@@ -107,8 +106,9 @@ const cbpServer = (port, listeningListener) => {
|
|
|
107
106
|
fullClient.delete(DeviceId);
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
109
|
+
};
|
|
110
|
+
// 处理事件
|
|
111
|
+
const handleEvent = (message, ID) => {
|
|
112
112
|
// 全量客户端
|
|
113
113
|
fullClient.forEach((clientWs, clientId) => {
|
|
114
114
|
// 检查状态 并检查状态
|
|
@@ -122,7 +122,6 @@ const cbpServer = (port, listeningListener) => {
|
|
|
122
122
|
});
|
|
123
123
|
// 根据所在群进行分流。
|
|
124
124
|
// 确保同一个频道的消息。都流向同一个客户端。
|
|
125
|
-
const ID = parsedMessage.ChannelId || parsedMessage.GuildId || parsedMessage.DeviceId;
|
|
126
125
|
if (!ID) {
|
|
127
126
|
logger.error({
|
|
128
127
|
code: ResultCode.Fail,
|
|
@@ -199,151 +198,237 @@ const cbpServer = (port, listeningListener) => {
|
|
|
199
198
|
return;
|
|
200
199
|
}
|
|
201
200
|
clientWs.send(message);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
};
|
|
202
|
+
// 得到平台客户端的消息
|
|
203
|
+
ws.on('message', (message) => {
|
|
204
|
+
try {
|
|
205
|
+
// 解析消息
|
|
206
|
+
const parsedMessage = flattedJSON.parse(message.toString());
|
|
207
|
+
// 1. 解析得到 actionId ,说明是消费行为请求。要广播告诉所有客户端。
|
|
208
|
+
// 2. 解析得到 name ,说明是一个事件请求。
|
|
209
|
+
// 3. 解析得到 apiId ,说明是一个接口请求。
|
|
210
|
+
// 4. 解析得到 testID ,说明是一个测试请求。
|
|
211
|
+
logger.debug({
|
|
212
|
+
code: ResultCode.Ok,
|
|
213
|
+
message: '服务端接收到消息',
|
|
214
|
+
data: parsedMessage
|
|
215
|
+
});
|
|
216
|
+
if (parsedMessage.apiId) {
|
|
217
|
+
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
218
|
+
const DeviceId = parsedMessage.DeviceId;
|
|
219
|
+
handleApi(DeviceId, message);
|
|
220
|
+
}
|
|
221
|
+
else if (parsedMessage?.actionId) {
|
|
222
|
+
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
223
|
+
const DeviceId = parsedMessage.DeviceId;
|
|
224
|
+
handleAction(DeviceId, message);
|
|
225
|
+
}
|
|
226
|
+
else if (parsedMessage?.name) {
|
|
227
|
+
const ID = parsedMessage.ChannelId || parsedMessage.GuildId || parsedMessage.DeviceId;
|
|
228
|
+
handleEvent(message, ID);
|
|
229
|
+
}
|
|
230
|
+
else if (parsedMessage?.testID) {
|
|
231
|
+
// 继续解析数据。测试请求。
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
logger.error({
|
|
236
|
+
code: ResultCode.Fail,
|
|
237
|
+
message: '服务端解析平台消息失败',
|
|
238
|
+
data: error
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
209
242
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
// 处理关闭事件
|
|
244
|
+
ws.on('close', () => {
|
|
245
|
+
platformClient.delete(originId);
|
|
246
|
+
logger.debug({
|
|
247
|
+
code: ResultCode.Fail,
|
|
248
|
+
message: `Client ${originId} disconnected`,
|
|
249
|
+
data: null
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
ws.on('error', err => {
|
|
253
|
+
logger.error({
|
|
254
|
+
code: ResultCode.Fail,
|
|
255
|
+
message: `Client ${originId} error`,
|
|
256
|
+
data: err
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
// 设置子客户端
|
|
261
|
+
const setChildrenClient = (originId, ws) => {
|
|
262
|
+
childrenClient.set(originId, ws);
|
|
263
|
+
// 得到子客户端的消息。只会是actions请求。
|
|
264
|
+
ws.on('message', (message) => {
|
|
265
|
+
// tudo
|
|
266
|
+
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
267
|
+
// 导致无法进行多个平台连接。
|
|
268
|
+
if (platformClient.size > 0) {
|
|
269
|
+
platformClient.forEach((platformWs, platformId) => {
|
|
270
|
+
// 检查平台客户端状态
|
|
271
|
+
if (platformWs.readyState === WebSocket.OPEN) {
|
|
272
|
+
platformWs.send(message);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// 如果连接已关闭,删除该平台客户端
|
|
276
|
+
platformClient.delete(platformId);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
243
279
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
280
|
+
});
|
|
281
|
+
// 处理关闭事件
|
|
282
|
+
ws.on('close', () => {
|
|
283
|
+
childrenClient.delete(originId);
|
|
284
|
+
logger.debug({
|
|
285
|
+
code: ResultCode.Fail,
|
|
286
|
+
message: `Client ${originId} disconnected`,
|
|
287
|
+
data: null
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
ws.on('error', err => {
|
|
291
|
+
logger.error({
|
|
292
|
+
code: ResultCode.Fail,
|
|
293
|
+
message: `Client ${originId} error`,
|
|
294
|
+
data: err
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
// 全量客户端
|
|
299
|
+
const setFullClient = (originId, ws) => {
|
|
300
|
+
fullClient.set(originId, ws);
|
|
301
|
+
// 处理消息事件
|
|
302
|
+
ws.on('message', (message) => {
|
|
303
|
+
// tudo
|
|
304
|
+
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
305
|
+
// 导致无法进行多个平台连接。
|
|
306
|
+
if (platformClient.size > 0) {
|
|
307
|
+
platformClient.forEach((platformWs, platformId) => {
|
|
308
|
+
// 检查平台客户端状态
|
|
309
|
+
if (platformWs.readyState === WebSocket.OPEN) {
|
|
310
|
+
platformWs.send(message);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
// 如果连接已关闭,删除该平台客户端
|
|
314
|
+
platformClient.delete(platformId);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
247
317
|
}
|
|
248
318
|
});
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
319
|
+
// 处理关闭事件
|
|
320
|
+
ws.on('close', () => {
|
|
321
|
+
fullClient.delete(originId);
|
|
322
|
+
logger.debug({
|
|
323
|
+
code: ResultCode.Fail,
|
|
324
|
+
message: `Client ${originId} disconnected`,
|
|
325
|
+
data: null
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
// 处理客户端连接
|
|
330
|
+
global.chatbotServer.on('connection', (ws, request) => {
|
|
331
|
+
// 测试平台的连接
|
|
332
|
+
if (request.url === '/testone') {
|
|
333
|
+
connectionTestOne(ws, request);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// 读取请求头中的 来源
|
|
337
|
+
const headers = request.headers;
|
|
338
|
+
const origin = headers[USER_AGENT_HEADER] || USER_AGENT_HEADER_VALUE_MAP.client;
|
|
339
|
+
// 来源id
|
|
340
|
+
const originId = headers[DEVICE_ID_HEADER];
|
|
341
|
+
if (!originId) {
|
|
342
|
+
// 如果没有来源 ID,拒绝连接
|
|
343
|
+
ws.close(4000, 'Missing Device ID');
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
logger.debug({
|
|
347
|
+
code: ResultCode.Ok,
|
|
348
|
+
message: `Client ${originId} connected`,
|
|
349
|
+
data: null
|
|
350
|
+
});
|
|
351
|
+
// 根据来源进行分类
|
|
352
|
+
if (origin === USER_AGENT_HEADER_VALUE_MAP.platform) {
|
|
353
|
+
setPlatformClient(originId, ws);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
else if (origin === USER_AGENT_HEADER_VALUE_MAP.client) {
|
|
357
|
+
// 连接时,需要给客户端发送主动消息
|
|
358
|
+
ws.send(flattedJSON.stringify({
|
|
359
|
+
active: 'sync',
|
|
360
|
+
payload: {
|
|
361
|
+
value: getConfig().value,
|
|
362
|
+
args: getConfig().argv,
|
|
363
|
+
package: {
|
|
364
|
+
version: getConfig().package?.version
|
|
365
|
+
},
|
|
366
|
+
env: {
|
|
367
|
+
login: process.env.login,
|
|
368
|
+
platform: process.env.platform,
|
|
369
|
+
port: process.env.port
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
// 主动消息
|
|
373
|
+
activeId: originId
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
const isFullReceive = headers[FULL_RECEIVE_HEADER] === '1';
|
|
377
|
+
// 如果是全量接收
|
|
378
|
+
if (isFullReceive) {
|
|
379
|
+
setFullClient(originId, ws);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
setChildrenClient(originId, ws);
|
|
258
383
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
384
|
+
chatbotServer.on('error', (err) => {
|
|
385
|
+
// 清理所有客户端连接
|
|
386
|
+
platformClient.clear();
|
|
387
|
+
childrenClient.clear();
|
|
388
|
+
fullClient.clear();
|
|
389
|
+
// 发现是端口已经被占用
|
|
390
|
+
if (err.code === 'EADDRINUSE') {
|
|
391
|
+
logger.error({
|
|
392
|
+
code: ResultCode.FailInternal,
|
|
393
|
+
message: `端口 ${port} 已被占用,请检查是否有其他服务在运行`,
|
|
394
|
+
data: err.message
|
|
395
|
+
});
|
|
396
|
+
const reCreateTime = getReConnectTime();
|
|
397
|
+
// 清理所有客户端连接,开始重新创建服务器
|
|
398
|
+
setTimeout(() => {
|
|
399
|
+
createServer();
|
|
400
|
+
}, reCreateTime);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
logger.error({
|
|
404
|
+
code: ResultCode.FailInternal,
|
|
405
|
+
message: 'WebSocket server error',
|
|
406
|
+
data: err.message || 'Unknown error'
|
|
407
|
+
});
|
|
408
|
+
}
|
|
265
409
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// 处理消息事件
|
|
272
|
-
ws.on('message', (message) => {
|
|
273
|
-
// tudo
|
|
274
|
-
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
275
|
-
// 导致无法进行多个平台连接。
|
|
276
|
-
if (platformClient.size > 0) {
|
|
277
|
-
platformClient.forEach(platformWs => {
|
|
278
|
-
// 检查平台客户端状态
|
|
279
|
-
if (platformWs.readyState === WebSocket.OPEN) {
|
|
280
|
-
platformWs.send(message);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
// 如果连接已关闭,删除该平台客户端
|
|
284
|
-
platformClient.delete(originId);
|
|
285
|
-
}
|
|
410
|
+
chatbotServer.on('close', () => {
|
|
411
|
+
logger.info({
|
|
412
|
+
code: ResultCode.Ok,
|
|
413
|
+
message: 'WebSocket server closed',
|
|
414
|
+
data: null
|
|
286
415
|
});
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
delete fullClient[originId];
|
|
292
|
-
logger.debug({
|
|
293
|
-
code: ResultCode.Fail,
|
|
294
|
-
message: `Client ${originId} disconnected`,
|
|
295
|
-
data: null
|
|
416
|
+
// 清理所有客户端连接
|
|
417
|
+
platformClient.clear();
|
|
418
|
+
childrenClient.clear();
|
|
419
|
+
fullClient.clear();
|
|
296
420
|
});
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
|
-
// 处理客户端连接
|
|
300
|
-
global.chatbotServer.on('connection', (ws, request) => {
|
|
301
|
-
// 读取请求头中的 来源
|
|
302
|
-
const headers = request.headers;
|
|
303
|
-
const origin = headers[USER_AGENT_HEADER] || 'client';
|
|
304
|
-
// 来源id
|
|
305
|
-
const originId = headers[DEVICE_ID_HEADER];
|
|
306
|
-
if (!originId) {
|
|
307
|
-
// 如果没有来源 ID,拒绝连接
|
|
308
|
-
ws.close(4000, 'Missing Device ID');
|
|
309
|
-
return;
|
|
310
421
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (origin === 'platform') {
|
|
318
|
-
setPlatformClient(originId, ws);
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
// 连接时,需要给客户端发送主动消息
|
|
322
|
-
ws.send(flattedJSON.stringify({
|
|
323
|
-
active: 'sync',
|
|
324
|
-
payload: {
|
|
325
|
-
value: getConfig().value,
|
|
326
|
-
args: getConfig().argv,
|
|
327
|
-
package: {
|
|
328
|
-
version: getConfig().package?.version
|
|
329
|
-
},
|
|
330
|
-
env: {
|
|
331
|
-
login: process.env.login,
|
|
332
|
-
platform: process.env.platform,
|
|
333
|
-
port: process.env.port
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
// 主动消息
|
|
337
|
-
activeId: originId
|
|
338
|
-
}));
|
|
339
|
-
const isFullReceive = headers[FULL_RECEIVE_HEADER] === '1';
|
|
340
|
-
// 如果是全量接收
|
|
341
|
-
if (isFullReceive) {
|
|
342
|
-
setFullClient(originId, ws);
|
|
422
|
+
catch (error) {
|
|
423
|
+
logger.error({
|
|
424
|
+
code: ResultCode.FailInternal,
|
|
425
|
+
message: '创建 CBP 服务器失败',
|
|
426
|
+
data: error
|
|
427
|
+
});
|
|
343
428
|
return;
|
|
344
429
|
}
|
|
345
|
-
|
|
346
|
-
|
|
430
|
+
};
|
|
431
|
+
createServer();
|
|
347
432
|
};
|
|
348
433
|
|
|
349
434
|
export { cbpServer };
|
package/lib/cbp/router.js
CHANGED
|
@@ -7,6 +7,9 @@ import { createRequire } from 'module';
|
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
const mainDirMap = new Map();
|
|
9
9
|
const formatPath = (path) => {
|
|
10
|
+
if (!path || path === '/') {
|
|
11
|
+
return '/index.html';
|
|
12
|
+
}
|
|
10
13
|
const pates = path.split('/');
|
|
11
14
|
const lastPath = pates[pates.length - 1];
|
|
12
15
|
if (lastPath.includes('.')) {
|
|
@@ -15,6 +18,30 @@ const formatPath = (path) => {
|
|
|
15
18
|
path += '.html';
|
|
16
19
|
return path;
|
|
17
20
|
};
|
|
21
|
+
// 输入一个文件路径。
|
|
22
|
+
const getModuelFile = (dir) => {
|
|
23
|
+
const dirMap = {
|
|
24
|
+
'.js': `${dir}.js`,
|
|
25
|
+
'.jsx': `${dir}.jsx`,
|
|
26
|
+
'.mjs': `${dir}.mjs`,
|
|
27
|
+
'.cjs': `${dir}.cjs`,
|
|
28
|
+
'/index.js': `${dir}/index.js`,
|
|
29
|
+
'/index.jsx': `${dir}/index.jsx`,
|
|
30
|
+
'/index.mjs': `${dir}/index.mjs`,
|
|
31
|
+
'/index.cjs': `${dir}/index.cjs`,
|
|
32
|
+
'.ts': `${dir}.ts`,
|
|
33
|
+
'.tsx': `${dir}.tsx`,
|
|
34
|
+
'/index.ts': `${dir}/index.ts`,
|
|
35
|
+
'/index.tsx': `${dir}/index.tsx`
|
|
36
|
+
};
|
|
37
|
+
for (const key in dirMap) {
|
|
38
|
+
const filePath = dirMap[key];
|
|
39
|
+
if (existsSync(filePath) && fs.statSync(filePath)) {
|
|
40
|
+
return filePath;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return '';
|
|
44
|
+
};
|
|
18
45
|
const router = new KoaRouter({
|
|
19
46
|
prefix: '/'
|
|
20
47
|
});
|
|
@@ -27,7 +54,7 @@ router.get('api/online', ctx => {
|
|
|
27
54
|
data: null
|
|
28
55
|
};
|
|
29
56
|
});
|
|
30
|
-
router.
|
|
57
|
+
router.all('app/{*path}', async (ctx) => {
|
|
31
58
|
if (!process.env.input) {
|
|
32
59
|
ctx.status = 400;
|
|
33
60
|
ctx.body = {
|
|
@@ -38,7 +65,8 @@ router.get('app/{*path}', async (ctx) => {
|
|
|
38
65
|
return;
|
|
39
66
|
}
|
|
40
67
|
const rootPath = process.cwd();
|
|
41
|
-
|
|
68
|
+
const apiPath = `/app/api`;
|
|
69
|
+
if (ctx.path.startsWith(apiPath)) {
|
|
42
70
|
let mainPath = join(rootPath, process.env.input);
|
|
43
71
|
// 路径
|
|
44
72
|
if (!existsSync(mainPath)) {
|
|
@@ -46,27 +74,43 @@ router.get('app/{*path}', async (ctx) => {
|
|
|
46
74
|
ctx.body = {
|
|
47
75
|
code: 400,
|
|
48
76
|
message: '未找到主要入口文件',
|
|
49
|
-
data:
|
|
77
|
+
data: 'existsSync input'
|
|
50
78
|
};
|
|
51
79
|
return;
|
|
52
80
|
}
|
|
53
81
|
const mainDir = dirname(mainPath);
|
|
54
82
|
try {
|
|
55
|
-
const dir = join(mainDir, 'route', ctx.path);
|
|
56
|
-
|
|
83
|
+
const dir = join(mainDir, 'route', ctx.path?.replace(apiPath, '/api') || '');
|
|
84
|
+
const dirs = dir.split('/');
|
|
85
|
+
const fileName = dirs[dirs.length - 1];
|
|
86
|
+
if (fileName.includes('.')) {
|
|
57
87
|
ctx.status = 404;
|
|
58
88
|
ctx.body = {
|
|
59
89
|
code: 404,
|
|
60
|
-
message: `API '
|
|
61
|
-
data:
|
|
90
|
+
message: `API '${ctx.path}' 未找到。`,
|
|
91
|
+
data: 'existsSync route filename'
|
|
62
92
|
};
|
|
63
93
|
return;
|
|
64
94
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
95
|
+
const modulePath = getModuelFile(dir);
|
|
96
|
+
if (!modulePath) {
|
|
97
|
+
ctx.status = 404;
|
|
98
|
+
ctx.body = {
|
|
99
|
+
code: 404,
|
|
100
|
+
message: `API '${ctx.path}' 未找到。`,
|
|
101
|
+
data: 'existsSync modulePath'
|
|
102
|
+
};
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const apiModule = await import(modulePath);
|
|
106
|
+
if (!apiModule[ctx.method] || typeof apiModule[ctx.method] !== 'function') {
|
|
107
|
+
ctx.status = 405;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await apiModule[ctx.method](ctx);
|
|
67
111
|
}
|
|
68
112
|
catch (err) {
|
|
69
|
-
console.error(`Error handling API request ${ctx.path}
|
|
113
|
+
console.error(`Error handling API request ${ctx.path}`);
|
|
70
114
|
ctx.status = 500;
|
|
71
115
|
ctx.body = {
|
|
72
116
|
code: 500,
|
|
@@ -76,8 +120,27 @@ router.get('app/{*path}', async (ctx) => {
|
|
|
76
120
|
}
|
|
77
121
|
return;
|
|
78
122
|
}
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
// 如果不是 get请求。即不响应
|
|
124
|
+
if (ctx.method !== 'GET') {
|
|
125
|
+
ctx.status = 405;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let root = '';
|
|
129
|
+
const resourcePath = formatPath(ctx.params?.path);
|
|
130
|
+
try {
|
|
131
|
+
const pkg = require(path.join(rootPath, 'package.json')) || {};
|
|
132
|
+
root = pkg.alemonjs?.web?.root || '';
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
ctx.status = 500;
|
|
136
|
+
ctx.body = {
|
|
137
|
+
code: 500,
|
|
138
|
+
message: `加载 package.json 时发生错误。`,
|
|
139
|
+
error: err.message
|
|
140
|
+
};
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const fullPath = root ? path.join(rootPath, root, resourcePath) : path.join(rootPath, resourcePath);
|
|
81
144
|
try {
|
|
82
145
|
// 返回文件
|
|
83
146
|
const file = await fs.promises.readFile(fullPath);
|
|
@@ -87,7 +150,7 @@ router.get('app/{*path}', async (ctx) => {
|
|
|
87
150
|
ctx.status = 200;
|
|
88
151
|
}
|
|
89
152
|
catch (err) {
|
|
90
|
-
if (err
|
|
153
|
+
if (err?.status === 404) {
|
|
91
154
|
ctx.status = 404;
|
|
92
155
|
ctx.body = {
|
|
93
156
|
code: 404,
|
|
@@ -105,11 +168,14 @@ router.get('app/{*path}', async (ctx) => {
|
|
|
105
168
|
}
|
|
106
169
|
}
|
|
107
170
|
});
|
|
108
|
-
router.
|
|
171
|
+
router.all('app', async (ctx) => {
|
|
172
|
+
ctx.redirect('/app/');
|
|
173
|
+
return;
|
|
174
|
+
});
|
|
175
|
+
router.all('apps/:app/{*path}', async (ctx) => {
|
|
109
176
|
const appName = ctx.params.app;
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
if (ctx.path.startsWith(apiDir)) {
|
|
177
|
+
const apiPath = `/apps/${appName}/api`;
|
|
178
|
+
if (ctx.path.startsWith(apiPath)) {
|
|
113
179
|
try {
|
|
114
180
|
if (!mainDirMap.has(appName)) {
|
|
115
181
|
const mainPath = require.resolve(appName);
|
|
@@ -126,12 +192,37 @@ router.get('apps/:app/{*path}', async (ctx) => {
|
|
|
126
192
|
const mainDir = dirname(mainPath);
|
|
127
193
|
mainDirMap.set(appName, mainDir);
|
|
128
194
|
}
|
|
129
|
-
const dir = join(mainDirMap.get(appName), 'route', ctx.path?.replace(
|
|
130
|
-
const
|
|
131
|
-
|
|
195
|
+
const dir = join(mainDirMap.get(appName), 'route', ctx.path?.replace(apiPath, '/api') || '');
|
|
196
|
+
const dirs = dir.split('/');
|
|
197
|
+
const fileName = dirs[dirs.length - 1];
|
|
198
|
+
if (fileName.includes('.')) {
|
|
199
|
+
ctx.status = 404;
|
|
200
|
+
ctx.body = {
|
|
201
|
+
code: 404,
|
|
202
|
+
message: `API 'route/${ctx.path}' 未找到。`,
|
|
203
|
+
data: null
|
|
204
|
+
};
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const modulePath = getModuelFile(dir);
|
|
208
|
+
if (!modulePath) {
|
|
209
|
+
ctx.status = 404;
|
|
210
|
+
ctx.body = {
|
|
211
|
+
code: 404,
|
|
212
|
+
message: `API 'route/${ctx.path}' 未找到。`,
|
|
213
|
+
data: null
|
|
214
|
+
};
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const apiModule = await import(modulePath);
|
|
218
|
+
if (!apiModule[ctx.method] || typeof apiModule[ctx.method] !== 'function') {
|
|
219
|
+
ctx.status = 405;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
await apiModule[ctx.method](ctx);
|
|
132
223
|
}
|
|
133
224
|
catch (err) {
|
|
134
|
-
logger.warn(`Error
|
|
225
|
+
logger.warn(`Error request ${ctx.path}:`, err?.message || '');
|
|
135
226
|
ctx.status = 500;
|
|
136
227
|
ctx.body = {
|
|
137
228
|
code: 500,
|
|
@@ -141,8 +232,28 @@ router.get('apps/:app/{*path}', async (ctx) => {
|
|
|
141
232
|
}
|
|
142
233
|
return;
|
|
143
234
|
}
|
|
144
|
-
|
|
145
|
-
|
|
235
|
+
// 如果不是 get请求。即不响应
|
|
236
|
+
if (ctx.method !== 'GET') {
|
|
237
|
+
ctx.status = 405;
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const rootPath = path.join(process.cwd(), 'packages', appName);
|
|
241
|
+
const resourcePath = formatPath(ctx.params?.path);
|
|
242
|
+
let root = '';
|
|
243
|
+
try {
|
|
244
|
+
const pkg = require(`${appName}/package`) || {};
|
|
245
|
+
root = pkg?.alemonjs?.web?.root || '';
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
ctx.status = 500;
|
|
249
|
+
ctx.body = {
|
|
250
|
+
code: 500,
|
|
251
|
+
message: `加载 package.json 时发生错误。`,
|
|
252
|
+
error: err.message
|
|
253
|
+
};
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const fullPath = root ? path.join(rootPath, root, resourcePath) : path.join(rootPath, resourcePath);
|
|
146
257
|
try {
|
|
147
258
|
// 返回文件
|
|
148
259
|
const file = await fs.promises.readFile(fullPath);
|
|
@@ -152,7 +263,7 @@ router.get('apps/:app/{*path}', async (ctx) => {
|
|
|
152
263
|
ctx.status = 200;
|
|
153
264
|
}
|
|
154
265
|
catch (err) {
|
|
155
|
-
if (err
|
|
266
|
+
if (err?.status === 404) {
|
|
156
267
|
ctx.status = 404;
|
|
157
268
|
ctx.body = {
|
|
158
269
|
code: 404,
|
|
@@ -161,15 +272,21 @@ router.get('apps/:app/{*path}', async (ctx) => {
|
|
|
161
272
|
};
|
|
162
273
|
}
|
|
163
274
|
else {
|
|
275
|
+
logger.warn(`Error request ${ctx.path}:`, err?.message || '');
|
|
164
276
|
ctx.status = 500;
|
|
165
277
|
ctx.body = {
|
|
166
278
|
code: 500,
|
|
167
279
|
message: `加载子应用 '${appName}' 资源时发生服务器错误。`,
|
|
168
280
|
error: err.message
|
|
169
281
|
};
|
|
170
|
-
logger.warn(`Error handling API request ${ctx.path}:`, err?.message || '');
|
|
171
282
|
}
|
|
172
283
|
}
|
|
173
284
|
});
|
|
285
|
+
router.all('apps/:name', async (ctx) => {
|
|
286
|
+
if (ctx.path === `/apps/${ctx.params.name}`) {
|
|
287
|
+
ctx.redirect(`/apps/${ctx.params.name}/`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
174
291
|
|
|
175
292
|
export { router as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const EventsKeyEnum = [
|
|
2
|
+
'message.create',
|
|
3
|
+
'message.update',
|
|
4
|
+
'message.delete',
|
|
5
|
+
'message.reaction.add',
|
|
6
|
+
'message.reaction.remove',
|
|
7
|
+
'private.message.create',
|
|
8
|
+
'private.message.update',
|
|
9
|
+
'private.message.delete',
|
|
10
|
+
'private.friend.add',
|
|
11
|
+
'private.guild.add',
|
|
12
|
+
'channal.create',
|
|
13
|
+
'channal.delete',
|
|
14
|
+
'guild.join',
|
|
15
|
+
'guild.exit',
|
|
16
|
+
'member.add',
|
|
17
|
+
'member.remove'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export { EventsKeyEnum };
|