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 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 { USER_AGENT_HEADER, DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, platformClient, childrenClient, fullClient, childrenBind } from './config.js';
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 app = new Koa();
16
- app.use(router.routes());
17
- app.use(router.allowedMethods());
18
- // 读取静态文件夹 gui
19
- app.use(koaStatic('/gui'));
20
- // 允许跨域
21
- app.use(koaCors({
22
- origin: '*', // 允许所有来源
23
- allowMethods: ['GET', 'POST', 'PUT', 'DELETE'] // 允许的 HTTP 方法
24
- }));
25
- const server = app.listen(port, listeningListener);
26
- // 创建 WebSocketServer 并监听同一个端口
27
- global.chatbotServer = new WebSocketServer({ server });
28
- /**
29
- *
30
- * @param originId
31
- * @param ws
32
- */
33
- const setPlatformClient = (originId, ws) => {
34
- // 仅允许有一个平台连接
35
- if (platformClient.size > 0) {
36
- logger.error({
37
- code: ResultCode.Fail,
38
- message: `平台连接已存在: ${originId}`,
39
- data: null
40
- });
41
- ws.close(); // 关闭新连接
42
- return;
43
- }
44
- // 设置平台客户端
45
- platformClient.set(originId, ws);
46
- // 得到平台客户端的消息
47
- ws.on('message', (message) => {
48
- try {
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
- else if (parsedMessage?.actionId) {
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
- else if (parsedMessage?.name) {
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
- catch (error) {
205
- logger.error({
206
- code: ResultCode.Fail,
207
- message: '服务端解析平台消息失败',
208
- data: error
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
- return;
211
- }
212
- });
213
- // 处理关闭事件
214
- ws.on('close', () => {
215
- delete platformClient[originId];
216
- logger.debug({
217
- code: ResultCode.Fail,
218
- message: `Client ${originId} disconnected`,
219
- data: null
220
- });
221
- });
222
- ws.on('error', err => {
223
- logger.error({
224
- code: ResultCode.Fail,
225
- message: `Client ${originId} error`,
226
- data: err
227
- });
228
- });
229
- };
230
- // 设置子客户端
231
- const setChildrenClient = (originId, ws) => {
232
- childrenClient.set(originId, ws);
233
- // 得到子客户端的消息。只会是actions请求。
234
- ws.on('message', (message) => {
235
- // tudo
236
- // 为什么 子客户端的行为,不携带目标平台的 DeviceId?
237
- // 导致无法进行多个平台连接。
238
- if (platformClient.size > 0) {
239
- platformClient.forEach(platformWs => {
240
- // 检查平台客户端状态
241
- if (platformWs.readyState === WebSocket.OPEN) {
242
- platformWs.send(message);
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
- else {
245
- // 如果连接已关闭,删除该平台客户端
246
- platformClient.delete(originId);
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
- ws.on('close', () => {
253
- delete childrenClient[originId];
254
- logger.debug({
255
- code: ResultCode.Fail,
256
- message: `Client ${originId} disconnected`,
257
- data: null
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
- ws.on('error', err => {
261
- logger.error({
262
- code: ResultCode.Fail,
263
- message: `Client ${originId} error`,
264
- data: err
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
- const setFullClient = (originId, ws) => {
270
- fullClient.set(originId, ws);
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
- ws.on('close', () => {
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
- logger.debug({
312
- code: ResultCode.Ok,
313
- message: `Client ${originId} connected`,
314
- data: null
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
- setChildrenClient(originId, ws);
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.get('app/{*path}', async (ctx) => {
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
- if (ctx.path.startsWith(`/app/api`)) {
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: null
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
- if (!existsSync(dir)) {
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 'route/${ctx.path}' 未找到。`,
61
- data: null
90
+ message: `API '${ctx.path}' 未找到。`,
91
+ data: 'existsSync route filename'
62
92
  };
63
93
  return;
64
94
  }
65
- const apiModule = await import(dir);
66
- await apiModule.default(ctx);
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}:`, err);
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
- const resourcePath = formatPath(ctx.params?.path || 'index.html');
80
- const fullPath = path.join(rootPath, resourcePath);
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.status === 404) {
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.get('apps/:app/{*path}', async (ctx) => {
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 rootPath = process.cwd();
111
- const apiDir = `/apps/${appName}/api`;
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(apiDir, '/api') || '');
130
- const apiModule = await import(dir);
131
- await apiModule.default(ctx);
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 handling API request ${ctx.path}:`, err?.message || '');
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
- const resourcePath = formatPath(ctx.params?.path || 'index.html');
145
- const fullPath = path.join(rootPath, 'packages', appName, resourcePath);
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.status === 404) {
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alemonjs",
3
- "version": "2.1.0-alpha.32",
3
+ "version": "2.1.0-alpha.34",
4
4
  "description": "bot script",
5
5
  "author": "lemonade",
6
6
  "license": "MIT",