alemonjs 2.1.28 → 2.1.30-rc.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/README_CONFIG.md +2 -2
- package/lib/app/message-format-old.js +2 -2
- package/lib/cbp/connects/client.js +94 -36
- package/lib/cbp/connects/platform.js +154 -0
- package/lib/cbp/processor/actions.js +27 -12
- package/lib/cbp/processor/api.js +27 -12
- package/lib/cbp/processor/config.js +3 -1
- package/lib/cbp/processor/transport.d.ts +4 -0
- package/lib/cbp/processor/transport.js +7 -0
- package/lib/client.js +3 -0
- package/lib/core/utils.js +4 -4
- package/lib/main.js +20 -10
- package/lib/process/direct-channel.d.ts +7 -0
- package/lib/process/direct-channel.js +110 -0
- package/lib/process/index.d.ts +2 -0
- package/lib/process/index.js +2 -0
- package/lib/process/ipc-bridge.d.ts +7 -0
- package/lib/process/ipc-bridge.js +39 -0
- package/lib/process/module.js +15 -4
- package/lib/process/platform.js +18 -2
- package/lib/server/routers/middleware.js +2 -2
- package/lib/server/routers/router.js +10 -10
- package/lib/server/routers/utils.js +2 -2
- package/lib/types/message/markdown.d.ts +2 -2
- package/package.json +1 -1
package/README_CONFIG.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
```yaml
|
|
6
6
|
# === 服务器配置 ===
|
|
7
|
-
port: 17117 # CBP
|
|
8
|
-
serverPort: 18110 #
|
|
7
|
+
port: 17117 # CBP Websocket 端口 (可选,将允许外部客户端互动)
|
|
8
|
+
serverPort: 18110 # Koa 服务器端口(可选,仅在需要Web服务时设置)
|
|
9
9
|
|
|
10
10
|
# === 应用配置 ===
|
|
11
11
|
input: 'lib/index.js' # 应用入口文件路径,快捷参数 --input
|
|
@@ -18,9 +18,101 @@ import '../../app/message-api.js';
|
|
|
18
18
|
import '../../app/message-format-old.js';
|
|
19
19
|
import { apiResolves, apiTimeouts, actionResolves, actionTimeouts, FULL_RECEIVE_HEADER } from '../processor/config.js';
|
|
20
20
|
import { createWSConnector } from './base.js';
|
|
21
|
+
import { setDirectSend } from '../processor/transport.js';
|
|
22
|
+
import { createDirectServer } from '../../process/direct-channel.js';
|
|
21
23
|
|
|
24
|
+
const handleParsedMessage = (parsedMessage) => {
|
|
25
|
+
if (parsedMessage?.apiId) {
|
|
26
|
+
const resolve = apiResolves.get(parsedMessage.apiId);
|
|
27
|
+
if (resolve) {
|
|
28
|
+
apiResolves.delete(parsedMessage.apiId);
|
|
29
|
+
const timeout = apiTimeouts.get(parsedMessage.apiId);
|
|
30
|
+
if (timeout) {
|
|
31
|
+
apiTimeouts.delete(parsedMessage.apiId);
|
|
32
|
+
clearTimeout(timeout);
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(parsedMessage.payload)) {
|
|
35
|
+
resolve(parsedMessage.payload);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
resolve([createResult(ResultCode.Fail, '接口处理错误', null)]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (parsedMessage?.actionId) {
|
|
43
|
+
const resolve = actionResolves.get(parsedMessage.actionId);
|
|
44
|
+
if (resolve) {
|
|
45
|
+
actionResolves.delete(parsedMessage.actionId);
|
|
46
|
+
const timeout = actionTimeouts.get(parsedMessage.actionId);
|
|
47
|
+
if (timeout) {
|
|
48
|
+
actionTimeouts.delete(parsedMessage.actionId);
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(parsedMessage.payload)) {
|
|
52
|
+
resolve(parsedMessage.payload);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
resolve([createResult(ResultCode.Fail, '消费处理错误', null)]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (parsedMessage.name) {
|
|
60
|
+
onProcessor(parsedMessage.name, parsedMessage, parsedMessage.value);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const cbpClientDirect = (sockPath, open) => {
|
|
64
|
+
createDirectServer(sockPath, (data) => {
|
|
65
|
+
handleParsedMessage(data);
|
|
66
|
+
}).then((channel) => {
|
|
67
|
+
setDirectSend(channel.send);
|
|
68
|
+
open();
|
|
69
|
+
logger.debug({
|
|
70
|
+
code: ResultCode.Ok,
|
|
71
|
+
message: '客户端已启用直连通道模式(Unix Domain Socket)',
|
|
72
|
+
data: null
|
|
73
|
+
});
|
|
74
|
+
}).catch((err) => {
|
|
75
|
+
logger.error({
|
|
76
|
+
code: ResultCode.Fail,
|
|
77
|
+
message: '客户端直连通道建立失败,回退 fork IPC',
|
|
78
|
+
data: err
|
|
79
|
+
});
|
|
80
|
+
cbpClientIPC(open);
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const cbpClientIPC = (open) => {
|
|
84
|
+
process.on('message', (message) => {
|
|
85
|
+
try {
|
|
86
|
+
const msg = typeof message === 'string' ? JSON.parse(message) : message;
|
|
87
|
+
if (msg?.type === 'ipc:data') {
|
|
88
|
+
handleParsedMessage(msg.data);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logger.error({
|
|
93
|
+
code: ResultCode.Fail,
|
|
94
|
+
message: 'IPC 客户端解析消息失败',
|
|
95
|
+
data: error
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
open();
|
|
100
|
+
logger.debug({
|
|
101
|
+
code: ResultCode.Ok,
|
|
102
|
+
message: '客户端已启用 IPC 极速通讯模式',
|
|
103
|
+
data: null
|
|
104
|
+
});
|
|
105
|
+
};
|
|
22
106
|
const cbpClient = (url, options = {}) => {
|
|
23
107
|
const { open = () => { }, isFullReceive = true } = options;
|
|
108
|
+
if (process.env.__ALEMON_DIRECT_SOCK && typeof process.send === 'function') {
|
|
109
|
+
cbpClientDirect(process.env.__ALEMON_DIRECT_SOCK, open);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (process.env.__ALEMON_IPC === '1' && typeof process.send === 'function') {
|
|
113
|
+
cbpClientIPC(open);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
24
116
|
createWSConnector({
|
|
25
117
|
url,
|
|
26
118
|
role: 'client',
|
|
@@ -41,42 +133,8 @@ const cbpClient = (url, options = {}) => {
|
|
|
41
133
|
}
|
|
42
134
|
}
|
|
43
135
|
}
|
|
44
|
-
else
|
|
45
|
-
|
|
46
|
-
if (resolve) {
|
|
47
|
-
apiResolves.delete(parsedMessage.apiId);
|
|
48
|
-
const timeout = apiTimeouts.get(parsedMessage.apiId);
|
|
49
|
-
if (timeout) {
|
|
50
|
-
apiTimeouts.delete(parsedMessage.apiId);
|
|
51
|
-
clearTimeout(timeout);
|
|
52
|
-
}
|
|
53
|
-
if (Array.isArray(parsedMessage.payload)) {
|
|
54
|
-
resolve(parsedMessage.payload);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
resolve([createResult(ResultCode.Fail, '接口处理错误', null)]);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (parsedMessage?.actionId) {
|
|
62
|
-
const resolve = actionResolves.get(parsedMessage.actionId);
|
|
63
|
-
if (resolve) {
|
|
64
|
-
actionResolves.delete(parsedMessage.actionId);
|
|
65
|
-
const timeout = actionTimeouts.get(parsedMessage.actionId);
|
|
66
|
-
if (timeout) {
|
|
67
|
-
actionTimeouts.delete(parsedMessage.actionId);
|
|
68
|
-
clearTimeout(timeout);
|
|
69
|
-
}
|
|
70
|
-
if (Array.isArray(parsedMessage.payload)) {
|
|
71
|
-
resolve(parsedMessage.payload);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
resolve([createResult(ResultCode.Fail, '消费处理错误', null)]);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else if (parsedMessage.name) {
|
|
79
|
-
onProcessor(parsedMessage.name, parsedMessage, parsedMessage.value);
|
|
136
|
+
else {
|
|
137
|
+
handleParsedMessage(parsedMessage);
|
|
80
138
|
}
|
|
81
139
|
}
|
|
82
140
|
catch (error) {
|
|
@@ -7,11 +7,165 @@ import 'path';
|
|
|
7
7
|
import 'yaml';
|
|
8
8
|
import '../../core/utils.js';
|
|
9
9
|
import { createWSConnector } from './base.js';
|
|
10
|
+
import { createDirectClient } from '../../process/direct-channel.js';
|
|
10
11
|
|
|
12
|
+
const cbpPlatformDirect = (sockPath, open) => {
|
|
13
|
+
const actionReplys = [];
|
|
14
|
+
const apiReplys = [];
|
|
15
|
+
let channel = null;
|
|
16
|
+
const pendingQueue = [];
|
|
17
|
+
const send = (data) => {
|
|
18
|
+
data.DeviceId = deviceId;
|
|
19
|
+
if (channel) {
|
|
20
|
+
channel.send(data);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
pendingQueue.push({ ...data });
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const replyAction = (data, payload) => {
|
|
27
|
+
channel?.send({
|
|
28
|
+
action: data.action,
|
|
29
|
+
payload: payload,
|
|
30
|
+
actionId: data.actionId,
|
|
31
|
+
DeviceId: data.DeviceId
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const replyApi = (data, payload) => {
|
|
35
|
+
channel?.send({
|
|
36
|
+
action: data.action,
|
|
37
|
+
apiId: data.apiId,
|
|
38
|
+
DeviceId: data.DeviceId,
|
|
39
|
+
payload: payload
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const onactions = (reply) => {
|
|
43
|
+
actionReplys.push(reply);
|
|
44
|
+
};
|
|
45
|
+
const onapis = (reply) => {
|
|
46
|
+
apiReplys.push(reply);
|
|
47
|
+
};
|
|
48
|
+
createDirectClient(sockPath, (data) => {
|
|
49
|
+
if (data?.apiId) {
|
|
50
|
+
for (const cb of apiReplys) {
|
|
51
|
+
void cb(data, val => replyApi(data, val));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (data?.actionId) {
|
|
55
|
+
for (const cb of actionReplys) {
|
|
56
|
+
void cb(data, val => replyAction(data, val));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}).then((ch) => {
|
|
60
|
+
channel = ch;
|
|
61
|
+
for (const msg of pendingQueue) {
|
|
62
|
+
channel.send(msg);
|
|
63
|
+
}
|
|
64
|
+
pendingQueue.length = 0;
|
|
65
|
+
open();
|
|
66
|
+
logger.debug({
|
|
67
|
+
code: ResultCode.Ok,
|
|
68
|
+
message: '平台端已启用直连通道模式(Unix Domain Socket)',
|
|
69
|
+
data: null
|
|
70
|
+
});
|
|
71
|
+
}).catch((err) => {
|
|
72
|
+
logger.error({
|
|
73
|
+
code: ResultCode.Fail,
|
|
74
|
+
message: '平台端直连通道建立失败,回退 fork IPC',
|
|
75
|
+
data: err
|
|
76
|
+
});
|
|
77
|
+
cbpPlatformIPC(open, actionReplys, apiReplys);
|
|
78
|
+
});
|
|
79
|
+
return { send, onactions, onapis };
|
|
80
|
+
};
|
|
81
|
+
const cbpPlatformIPC = (open, existingActionReplys, existingApiReplys) => {
|
|
82
|
+
const actionReplys = existingActionReplys ?? [];
|
|
83
|
+
const apiReplys = existingApiReplys ?? [];
|
|
84
|
+
const send = (data) => {
|
|
85
|
+
if (typeof process.send === 'function') {
|
|
86
|
+
data.DeviceId = deviceId;
|
|
87
|
+
process.send({ type: 'ipc:data', data });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const replyAction = (data, payload) => {
|
|
91
|
+
if (typeof process.send === 'function') {
|
|
92
|
+
process.send({
|
|
93
|
+
type: 'ipc:data',
|
|
94
|
+
data: {
|
|
95
|
+
action: data.action,
|
|
96
|
+
payload: payload,
|
|
97
|
+
actionId: data.actionId,
|
|
98
|
+
DeviceId: data.DeviceId
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const replyApi = (data, payload) => {
|
|
104
|
+
if (typeof process.send === 'function') {
|
|
105
|
+
process.send({
|
|
106
|
+
type: 'ipc:data',
|
|
107
|
+
data: {
|
|
108
|
+
action: data.action,
|
|
109
|
+
apiId: data.apiId,
|
|
110
|
+
DeviceId: data.DeviceId,
|
|
111
|
+
payload: payload
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onactions = (reply) => {
|
|
117
|
+
actionReplys.push(reply);
|
|
118
|
+
};
|
|
119
|
+
const onapis = (reply) => {
|
|
120
|
+
apiReplys.push(reply);
|
|
121
|
+
};
|
|
122
|
+
process.on('message', (message) => {
|
|
123
|
+
try {
|
|
124
|
+
const msg = typeof message === 'string' ? JSON.parse(message) : message;
|
|
125
|
+
if (msg?.type === 'ipc:data') {
|
|
126
|
+
const data = msg.data;
|
|
127
|
+
if (data?.apiId) {
|
|
128
|
+
for (const cb of apiReplys) {
|
|
129
|
+
void cb(data, val => replyApi(data, val));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (data?.actionId) {
|
|
133
|
+
for (const cb of actionReplys) {
|
|
134
|
+
void cb(data, val => replyAction(data, val));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
logger.error({
|
|
141
|
+
code: ResultCode.Fail,
|
|
142
|
+
message: 'IPC 平台端解析消息失败',
|
|
143
|
+
data: error
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
open();
|
|
148
|
+
logger.debug({
|
|
149
|
+
code: ResultCode.Ok,
|
|
150
|
+
message: '平台端已启用 IPC 极速通讯模式',
|
|
151
|
+
data: null
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
send,
|
|
155
|
+
onactions,
|
|
156
|
+
onapis
|
|
157
|
+
};
|
|
158
|
+
};
|
|
11
159
|
const cbpPlatform = (url, options = {
|
|
12
160
|
open: () => { }
|
|
13
161
|
}) => {
|
|
14
162
|
const { open = () => { } } = options;
|
|
163
|
+
if (process.env.__ALEMON_DIRECT_SOCK && typeof process.send === 'function') {
|
|
164
|
+
return cbpPlatformDirect(process.env.__ALEMON_DIRECT_SOCK, open);
|
|
165
|
+
}
|
|
166
|
+
if (process.env.__ALEMON_IPC === '1' && typeof process.send === 'function') {
|
|
167
|
+
return cbpPlatformIPC(open);
|
|
168
|
+
}
|
|
15
169
|
const send = (data) => {
|
|
16
170
|
if (global.chatbotPlatform && global.chatbotPlatform.readyState === WebSocket.OPEN) {
|
|
17
171
|
data.DeviceId = deviceId;
|
|
@@ -5,27 +5,42 @@ import 'path';
|
|
|
5
5
|
import 'yaml';
|
|
6
6
|
import { createResult } from '../../core/utils.js';
|
|
7
7
|
import { generateUniqueId, deviceId, actionResolves, actionTimeouts, timeoutTime } from './config.js';
|
|
8
|
+
import { getDirectSend } from './transport.js';
|
|
8
9
|
|
|
10
|
+
const setupActionResolve = (actionId, resolve) => {
|
|
11
|
+
actionResolves.set(actionId, resolve);
|
|
12
|
+
const timeout = setTimeout(() => {
|
|
13
|
+
if (!actionResolves.has(actionId) || !actionTimeouts.has(actionId)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
actionResolves.delete(actionId);
|
|
17
|
+
actionTimeouts.delete(actionId);
|
|
18
|
+
resolve([createResult(ResultCode.Fail, '行为超时', null)]);
|
|
19
|
+
}, timeoutTime);
|
|
20
|
+
actionTimeouts.set(actionId, timeout);
|
|
21
|
+
};
|
|
9
22
|
const sendAction = (data) => {
|
|
10
23
|
const actionId = generateUniqueId();
|
|
11
24
|
return new Promise(resolve => {
|
|
25
|
+
data.actionId = actionId;
|
|
26
|
+
data.DeviceId = deviceId;
|
|
27
|
+
const directSend = getDirectSend();
|
|
28
|
+
if (directSend) {
|
|
29
|
+
directSend(data);
|
|
30
|
+
setupActionResolve(actionId, resolve);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (process.env.__ALEMON_IPC === '1' && typeof process.send === 'function') {
|
|
34
|
+
process.send({ type: 'ipc:data', data });
|
|
35
|
+
setupActionResolve(actionId, resolve);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
12
38
|
if (!global.chatbotClient?.send) {
|
|
13
39
|
resolve([createResult(ResultCode.Fail, 'Chatbot client is not available', null)]);
|
|
14
40
|
return;
|
|
15
41
|
}
|
|
16
|
-
data.actionId = actionId;
|
|
17
|
-
data.DeviceId = deviceId;
|
|
18
42
|
global.chatbotClient?.send(flattedJSON.stringify(data));
|
|
19
|
-
|
|
20
|
-
const timeout = setTimeout(() => {
|
|
21
|
-
if (!actionResolves.has(actionId) || !actionTimeouts.has(actionId)) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
actionResolves.delete(actionId);
|
|
25
|
-
actionTimeouts.delete(actionId);
|
|
26
|
-
resolve([createResult(ResultCode.Fail, '行为超时', null)]);
|
|
27
|
-
}, timeoutTime);
|
|
28
|
-
actionTimeouts.set(actionId, timeout);
|
|
43
|
+
setupActionResolve(actionId, resolve);
|
|
29
44
|
});
|
|
30
45
|
};
|
|
31
46
|
|
package/lib/cbp/processor/api.js
CHANGED
|
@@ -5,27 +5,42 @@ import 'yaml';
|
|
|
5
5
|
import { createResult } from '../../core/utils.js';
|
|
6
6
|
import { generateUniqueId, deviceId, apiResolves, apiTimeouts, timeoutTime } from './config.js';
|
|
7
7
|
import * as flattedJSON from 'flatted';
|
|
8
|
+
import { getDirectSend } from './transport.js';
|
|
8
9
|
|
|
10
|
+
const setupApiResolve = (apiId, resolve) => {
|
|
11
|
+
apiResolves.set(apiId, resolve);
|
|
12
|
+
const timeout = setTimeout(() => {
|
|
13
|
+
if (!apiResolves.has(apiId) || !apiTimeouts.has(apiId)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
apiResolves.delete(apiId);
|
|
17
|
+
apiTimeouts.delete(apiId);
|
|
18
|
+
resolve([createResult(ResultCode.Fail, '接口超时', null)]);
|
|
19
|
+
}, timeoutTime);
|
|
20
|
+
apiTimeouts.set(apiId, timeout);
|
|
21
|
+
};
|
|
9
22
|
const sendAPI = (data) => {
|
|
10
23
|
const ApiId = generateUniqueId();
|
|
11
24
|
return new Promise(resolve => {
|
|
25
|
+
data.apiId = ApiId;
|
|
26
|
+
data.DeviceId = deviceId;
|
|
27
|
+
const directSend = getDirectSend();
|
|
28
|
+
if (directSend) {
|
|
29
|
+
directSend(data);
|
|
30
|
+
setupApiResolve(ApiId, resolve);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (process.env.__ALEMON_IPC === '1' && typeof process.send === 'function') {
|
|
34
|
+
process.send({ type: 'ipc:data', data });
|
|
35
|
+
setupApiResolve(ApiId, resolve);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
12
38
|
if (!global.chatbotClient?.send) {
|
|
13
39
|
resolve([createResult(ResultCode.Fail, 'Chatbot client is not available', null)]);
|
|
14
40
|
return;
|
|
15
41
|
}
|
|
16
|
-
data.apiId = ApiId;
|
|
17
|
-
data.DeviceId = deviceId;
|
|
18
42
|
global.chatbotClient?.send(flattedJSON.stringify(data));
|
|
19
|
-
|
|
20
|
-
const timeout = setTimeout(() => {
|
|
21
|
-
if (!apiResolves.has(ApiId) || !apiTimeouts.has(ApiId)) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
apiResolves.delete(ApiId);
|
|
25
|
-
apiTimeouts.delete(ApiId);
|
|
26
|
-
resolve([createResult(ResultCode.Fail, '接口超时', null)]);
|
|
27
|
-
}, timeoutTime);
|
|
28
|
-
apiTimeouts.set(ApiId, timeout);
|
|
43
|
+
setupApiResolve(ApiId, resolve);
|
|
29
44
|
});
|
|
30
45
|
};
|
|
31
46
|
|
|
@@ -17,8 +17,10 @@ const apiResolves = new Map();
|
|
|
17
17
|
const actionTimeouts = new Map();
|
|
18
18
|
const apiTimeouts = new Map();
|
|
19
19
|
const childrenBind = new Map();
|
|
20
|
+
let _idCounter = 0;
|
|
21
|
+
const _idPrefix = process.pid.toString(36) + Date.now().toString(36);
|
|
20
22
|
const generateUniqueId = () => {
|
|
21
|
-
return
|
|
23
|
+
return _idPrefix + (++_idCounter).toString(36);
|
|
22
24
|
};
|
|
23
25
|
const timeoutTime = 1000 * 60 * 3;
|
|
24
26
|
const reconnectInterval = 1000 * 6;
|
package/lib/client.js
CHANGED
package/lib/core/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs__default, { existsSync, readdirSync } from 'fs';
|
|
3
|
+
import path__default, { join } from 'path';
|
|
4
4
|
import { fileSuffixResponse, ResultCode } from './variable.js';
|
|
5
5
|
import module from 'module';
|
|
6
6
|
|
|
@@ -87,8 +87,8 @@ const createExports = (packageJson) => {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
const getInputExportPath = (input) => {
|
|
90
|
-
const packageJsonPath =
|
|
91
|
-
if (
|
|
90
|
+
const packageJsonPath = path__default.join(input ?? process.cwd(), 'package.json');
|
|
91
|
+
if (fs__default.existsSync(packageJsonPath)) {
|
|
92
92
|
const packageJson = require(packageJsonPath);
|
|
93
93
|
const main = packageJson?.main || createExports(packageJson);
|
|
94
94
|
if (main) {
|
package/lib/main.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getConfig } from './core/config.js';
|
|
2
2
|
import { cbpServer } from './cbp/server/main.js';
|
|
3
|
-
import {
|
|
3
|
+
import { filePrefixCommon, defaultPlatformCommonPrefix } from './core/variable.js';
|
|
4
4
|
import { startPlatformAdapterWithFallback } from './process/platform.js';
|
|
5
5
|
import { startModuleAdapter } from './process/module.js';
|
|
6
|
+
import { generateSocketPath } from './process/direct-channel.js';
|
|
6
7
|
|
|
7
8
|
const createOptionsByKey = (options, key, defaultValue) => {
|
|
8
9
|
const cfg = getConfig();
|
|
@@ -41,7 +42,7 @@ const startClient = (options) => {
|
|
|
41
42
|
process.env.input = createOptionsByKey(options, 'input', '');
|
|
42
43
|
process.env.output = createOptionsByKey(options, 'output', '');
|
|
43
44
|
process.env.is_full_receive = String(createOptionsByKey(options, 'is_full_receive', true));
|
|
44
|
-
process.env.port = String(createOptionsByKey(options, 'port',
|
|
45
|
+
process.env.port = String(createOptionsByKey(options, 'port', '') || '');
|
|
45
46
|
process.env.url = createOptionsByKey(options, 'url', '');
|
|
46
47
|
startModuleAdapter();
|
|
47
48
|
};
|
|
@@ -50,18 +51,27 @@ const start = (options = {}) => {
|
|
|
50
51
|
options = { input: options };
|
|
51
52
|
}
|
|
52
53
|
global.__options = options;
|
|
53
|
-
const port = createOptionsByKey(options, 'port',
|
|
54
|
+
const port = createOptionsByKey(options, 'port', '');
|
|
54
55
|
const serverPort = createOptionsByKey(options, 'serverPort', '');
|
|
55
|
-
process.env.port = port;
|
|
56
|
+
process.env.port = port ? String(port) : '';
|
|
56
57
|
process.env.serverPort = serverPort;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (port) {
|
|
59
|
+
cbpServer(port, () => {
|
|
60
|
+
const httpURL = `http://127.0.0.1:${port}`;
|
|
61
|
+
const wsURL = `ws://127.0.0.1:${port}`;
|
|
62
|
+
logger.info(`[CBP server started at ${httpURL}]`);
|
|
63
|
+
logger.info(`[CBP server started at ${wsURL}]`);
|
|
64
|
+
startClient(options);
|
|
65
|
+
startPlatform(options);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const sockPath = generateSocketPath();
|
|
70
|
+
process.env.__ALEMON_DIRECT_SOCK = sockPath;
|
|
71
|
+
logger.info('[Direct-IPC mode] 平台↔客户端直连通道,无主进程桥接');
|
|
62
72
|
startClient(options);
|
|
63
73
|
startPlatform(options);
|
|
64
|
-
}
|
|
74
|
+
}
|
|
65
75
|
};
|
|
66
76
|
|
|
67
77
|
export { start };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const generateSocketPath: () => string;
|
|
2
|
+
export interface DirectChannel {
|
|
3
|
+
send: (data: any) => void;
|
|
4
|
+
close: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare const createDirectServer: (sockPath: string, onMessage: (data: any) => void) => Promise<DirectChannel>;
|
|
7
|
+
export declare const createDirectClient: (sockPath: string, onMessage: (data: any) => void, maxRetries?: number, retryDelay?: number) => Promise<DirectChannel>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as net from 'net';
|
|
2
|
+
import * as v8 from 'v8';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
|
|
7
|
+
const generateSocketPath = () => {
|
|
8
|
+
if (process.platform === 'win32') {
|
|
9
|
+
return `\\\\.\\pipe\\alemon-direct-${process.pid}-${Date.now()}`;
|
|
10
|
+
}
|
|
11
|
+
return path.join(os.tmpdir(), `alemon-direct-${process.pid}-${Date.now()}.sock`);
|
|
12
|
+
};
|
|
13
|
+
const encodeMessage = (data) => {
|
|
14
|
+
const serialized = v8.serialize(data);
|
|
15
|
+
const header = Buffer.alloc(4);
|
|
16
|
+
header.writeUInt32BE(serialized.length, 0);
|
|
17
|
+
return Buffer.concat([header, serialized]);
|
|
18
|
+
};
|
|
19
|
+
const createMessageParser = (onMessage) => {
|
|
20
|
+
let buffer = Buffer.alloc(0);
|
|
21
|
+
return (chunk) => {
|
|
22
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
23
|
+
while (buffer.length >= 4) {
|
|
24
|
+
const msgLen = buffer.readUInt32BE(0);
|
|
25
|
+
if (buffer.length < 4 + msgLen) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
const msgBuf = buffer.subarray(4, 4 + msgLen);
|
|
29
|
+
buffer = buffer.subarray(4 + msgLen);
|
|
30
|
+
try {
|
|
31
|
+
onMessage(v8.deserialize(msgBuf));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const createDirectServer = (sockPath, onMessage) => {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
let connection = null;
|
|
41
|
+
if (process.platform !== 'win32') {
|
|
42
|
+
try {
|
|
43
|
+
fs.unlinkSync(sockPath);
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
}
|
|
47
|
+
const server = net.createServer((socket) => {
|
|
48
|
+
connection = socket;
|
|
49
|
+
const parser = createMessageParser(onMessage);
|
|
50
|
+
socket.on('data', parser);
|
|
51
|
+
socket.on('error', () => { connection = null; });
|
|
52
|
+
socket.on('close', () => { connection = null; });
|
|
53
|
+
});
|
|
54
|
+
const cleanup = () => {
|
|
55
|
+
try {
|
|
56
|
+
server.close();
|
|
57
|
+
connection?.destroy();
|
|
58
|
+
if (process.platform !== 'win32') {
|
|
59
|
+
try {
|
|
60
|
+
fs.unlinkSync(sockPath);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch { }
|
|
66
|
+
};
|
|
67
|
+
process.on('exit', cleanup);
|
|
68
|
+
server.listen(sockPath, () => {
|
|
69
|
+
resolve({
|
|
70
|
+
send: (data) => {
|
|
71
|
+
if (connection && !connection.destroyed) {
|
|
72
|
+
connection.write(encodeMessage(data));
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
close: cleanup
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
server.on('error', reject);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const createDirectClient = (sockPath, onMessage, maxRetries = 30, retryDelay = 150) => {
|
|
82
|
+
let attempts = 0;
|
|
83
|
+
const tryConnect = () => {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const parser = createMessageParser(onMessage);
|
|
86
|
+
const socket = net.createConnection(sockPath, () => {
|
|
87
|
+
resolve({
|
|
88
|
+
send: (data) => {
|
|
89
|
+
if (!socket.destroyed) {
|
|
90
|
+
socket.write(encodeMessage(data));
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
close: () => {
|
|
94
|
+
socket.destroy();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
socket.on('data', parser);
|
|
99
|
+
socket.on('error', reject);
|
|
100
|
+
}).catch((err) => {
|
|
101
|
+
if (++attempts < maxRetries) {
|
|
102
|
+
return new Promise((r) => setTimeout(() => r(tryConnect()), retryDelay));
|
|
103
|
+
}
|
|
104
|
+
throw err;
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
return tryConnect();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export { createDirectClient, createDirectServer, generateSocketPath };
|
package/lib/process/index.d.ts
CHANGED
package/lib/process/index.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { startModuleAdapter } from './module.js';
|
|
2
2
|
export { startPlatformAdapterWithFallback } from './platform.js';
|
|
3
|
+
export { forwardFromClient, forwardFromPlatform, getClientChild, getPlatformChild, setClientChild, setPlatformChild } from './ipc-bridge.js';
|
|
4
|
+
export { createDirectClient, createDirectServer, generateSocketPath } from './direct-channel.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChildProcess } from 'child_process';
|
|
2
|
+
export declare const setPlatformChild: (child: ChildProcess | null) => void;
|
|
3
|
+
export declare const setClientChild: (child: ChildProcess | null) => void;
|
|
4
|
+
export declare const getPlatformChild: () => ChildProcess;
|
|
5
|
+
export declare const getClientChild: () => ChildProcess;
|
|
6
|
+
export declare const forwardFromPlatform: (data: any) => void;
|
|
7
|
+
export declare const forwardFromClient: (data: any) => void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import { fullClient } from '../cbp/processor/config.js';
|
|
3
|
+
import * as flattedJSON from 'flatted';
|
|
4
|
+
|
|
5
|
+
let platformChild = null;
|
|
6
|
+
let clientChild = null;
|
|
7
|
+
const setPlatformChild = (child) => {
|
|
8
|
+
platformChild = child;
|
|
9
|
+
};
|
|
10
|
+
const setClientChild = (child) => {
|
|
11
|
+
clientChild = child;
|
|
12
|
+
};
|
|
13
|
+
const getPlatformChild = () => platformChild;
|
|
14
|
+
const getClientChild = () => clientChild;
|
|
15
|
+
const forwardFromPlatform = (data) => {
|
|
16
|
+
if (clientChild?.connected) {
|
|
17
|
+
clientChild.send({ type: 'ipc:data', data });
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const messageStr = flattedJSON.stringify(data);
|
|
21
|
+
fullClient.forEach((ws, id) => {
|
|
22
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
23
|
+
ws.send(messageStr);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
fullClient.delete(id);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const forwardFromClient = (data) => {
|
|
34
|
+
if (platformChild?.connected) {
|
|
35
|
+
platformChild.send({ type: 'ipc:data', data });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { forwardFromClient, forwardFromPlatform, getClientChild, getPlatformChild, setClientChild, setPlatformChild };
|
package/lib/process/module.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ResultCode } from '../core/variable.js';
|
|
|
3
3
|
import { getConfigValue } from '../core/config.js';
|
|
4
4
|
import '../core/utils.js';
|
|
5
5
|
import module from 'module';
|
|
6
|
+
import { setClientChild, forwardFromClient } from './ipc-bridge.js';
|
|
6
7
|
|
|
7
8
|
const initRequire = () => { };
|
|
8
9
|
initRequire.resolve = () => '';
|
|
@@ -31,14 +32,18 @@ function startModuleAdapter() {
|
|
|
31
32
|
restarted: false,
|
|
32
33
|
ready: false
|
|
33
34
|
};
|
|
34
|
-
const
|
|
35
|
+
const clearTimer = () => {
|
|
35
36
|
if (manager.timer) {
|
|
36
37
|
clearTimeout(manager.timer);
|
|
37
38
|
manager.timer = undefined;
|
|
38
39
|
}
|
|
40
|
+
};
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
clearTimer();
|
|
39
43
|
if (manager.child) {
|
|
40
44
|
manager.child.removeAllListeners();
|
|
41
45
|
}
|
|
46
|
+
setClientChild(null);
|
|
42
47
|
};
|
|
43
48
|
const restart = () => {
|
|
44
49
|
if (manager.restarted) {
|
|
@@ -66,7 +71,9 @@ function startModuleAdapter() {
|
|
|
66
71
|
};
|
|
67
72
|
try {
|
|
68
73
|
manager.child = childProcess.fork(modulePath, [], {
|
|
69
|
-
execArgv: process.execArgv
|
|
74
|
+
execArgv: process.execArgv,
|
|
75
|
+
env: { ...process.env, __ALEMON_IPC: '1' },
|
|
76
|
+
serialization: 'advanced'
|
|
70
77
|
});
|
|
71
78
|
manager.timer = setTimeout(checkTimeout, CONFIG.FORK_TIMEOUT);
|
|
72
79
|
manager.child.on('exit', (code, signal) => {
|
|
@@ -83,14 +90,18 @@ function startModuleAdapter() {
|
|
|
83
90
|
const data = typeof message === 'string' ? JSON.parse(message) : message;
|
|
84
91
|
if (data?.type === 'ready') {
|
|
85
92
|
manager.ready = true;
|
|
86
|
-
|
|
93
|
+
clearTimer();
|
|
94
|
+
setClientChild(manager.child);
|
|
87
95
|
logger?.debug?.({
|
|
88
96
|
code: ResultCode.Ok,
|
|
89
|
-
message: '
|
|
97
|
+
message: '模块加载已就绪(IPC)',
|
|
90
98
|
data: null
|
|
91
99
|
});
|
|
92
100
|
manager.child?.send?.({ type: 'start' });
|
|
93
101
|
}
|
|
102
|
+
else if (data?.type === 'ipc:data') {
|
|
103
|
+
forwardFromClient(data.data);
|
|
104
|
+
}
|
|
94
105
|
}
|
|
95
106
|
catch (error) {
|
|
96
107
|
logger?.error?.({
|
package/lib/process/platform.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ResultCode } from '../core/variable.js';
|
|
|
3
3
|
import { getConfigValue } from '../core/config.js';
|
|
4
4
|
import '../core/utils.js';
|
|
5
5
|
import module from 'module';
|
|
6
|
+
import { setPlatformChild, forwardFromPlatform } from './ipc-bridge.js';
|
|
6
7
|
|
|
7
8
|
const initRequire = () => { };
|
|
8
9
|
initRequire.resolve = () => '';
|
|
@@ -91,6 +92,7 @@ function startPlatformAdapterWithFallback() {
|
|
|
91
92
|
if (manager.child) {
|
|
92
93
|
manager.child.removeAllListeners();
|
|
93
94
|
}
|
|
95
|
+
setPlatformChild(null);
|
|
94
96
|
};
|
|
95
97
|
const restart = () => {
|
|
96
98
|
if (manager.restarted || imported) {
|
|
@@ -110,6 +112,14 @@ function startPlatformAdapterWithFallback() {
|
|
|
110
112
|
}
|
|
111
113
|
isForkFailed = true;
|
|
112
114
|
cleanup();
|
|
115
|
+
if (!process.env.port) {
|
|
116
|
+
logger?.error?.({
|
|
117
|
+
code: ResultCode.Fail,
|
|
118
|
+
message: 'fork 启动平台连接失败,当前为纯 IPC 模式(未配置 port),无法降级到 WebSocket,已终止平台连接',
|
|
119
|
+
data: error
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
113
123
|
logger?.warn?.({
|
|
114
124
|
code: ResultCode.Fail,
|
|
115
125
|
message: 'fork 启动平台连接失败,将尝试 import 加载',
|
|
@@ -135,7 +145,9 @@ function startPlatformAdapterWithFallback() {
|
|
|
135
145
|
};
|
|
136
146
|
try {
|
|
137
147
|
manager.child = childProcess.fork(modulePath, [], {
|
|
138
|
-
execArgv: process.execArgv
|
|
148
|
+
execArgv: process.execArgv,
|
|
149
|
+
env: { ...process.env, __ALEMON_IPC: '1' },
|
|
150
|
+
serialization: 'advanced'
|
|
139
151
|
});
|
|
140
152
|
manager.timer = setTimeout(checkTimeout, CONFIG.FORK_TIMEOUT);
|
|
141
153
|
manager.child.on('exit', (code, signal) => {
|
|
@@ -161,13 +173,17 @@ function startPlatformAdapterWithFallback() {
|
|
|
161
173
|
clearTimeout(manager.timer);
|
|
162
174
|
manager.timer = undefined;
|
|
163
175
|
}
|
|
176
|
+
setPlatformChild(manager.child);
|
|
164
177
|
logger?.debug?.({
|
|
165
178
|
code: ResultCode.Ok,
|
|
166
|
-
message: '
|
|
179
|
+
message: '平台连接已就绪(IPC)',
|
|
167
180
|
data: null
|
|
168
181
|
});
|
|
169
182
|
manager.child?.send?.({ type: 'start' });
|
|
170
183
|
}
|
|
184
|
+
else if (data?.type === 'ipc:data') {
|
|
185
|
+
forwardFromPlatform(data.data);
|
|
186
|
+
}
|
|
171
187
|
}
|
|
172
188
|
catch (error) {
|
|
173
189
|
logger?.error?.({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
|
-
import
|
|
2
|
+
import path__default, { dirname } from 'path';
|
|
3
3
|
|
|
4
4
|
async function collectMiddlewares(routeFile) {
|
|
5
5
|
const middlewares = [];
|
|
@@ -7,7 +7,7 @@ async function collectMiddlewares(routeFile) {
|
|
|
7
7
|
const suffixes = ['.ts', '.js', '.cjs', '.mjs', '.tsx', '.jsx'];
|
|
8
8
|
while (true) {
|
|
9
9
|
for (const ext of suffixes) {
|
|
10
|
-
const mwPath =
|
|
10
|
+
const mwPath = path__default.join(dir, `_middleware${ext}`);
|
|
11
11
|
if (existsSync(mwPath)) {
|
|
12
12
|
const module = await import(`file://${mwPath}`);
|
|
13
13
|
const mw = module?.default ?? {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import KoaRouter from 'koa-router';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs__default, { existsSync } from 'fs';
|
|
3
|
+
import path__default, { join, dirname } from 'path';
|
|
4
4
|
import mime from 'mime-types';
|
|
5
5
|
import hello from './hello.html.js';
|
|
6
6
|
import { getModuelFile, formatPath } from './utils.js';
|
|
@@ -56,7 +56,7 @@ router.all('app/{*path}', async (ctx) => {
|
|
|
56
56
|
const mainDir = dirname(mainPath);
|
|
57
57
|
try {
|
|
58
58
|
const dir = join(mainDir, 'route', ctx.path?.replace(apiPath, '/api') || '');
|
|
59
|
-
if (existsSync(dir) &&
|
|
59
|
+
if (existsSync(dir) && fs__default.statSync(dir).isFile()) {
|
|
60
60
|
ctx.status = 404;
|
|
61
61
|
ctx.body = {
|
|
62
62
|
code: 404,
|
|
@@ -102,7 +102,7 @@ router.all('app/{*path}', async (ctx) => {
|
|
|
102
102
|
let root = '';
|
|
103
103
|
const resourcePath = formatPath(ctx.params?.path);
|
|
104
104
|
try {
|
|
105
|
-
const pkg = require(
|
|
105
|
+
const pkg = require(path__default.join(rootPath, 'package.json')) ?? {};
|
|
106
106
|
root = pkg.alemonjs?.web?.root ?? '';
|
|
107
107
|
}
|
|
108
108
|
catch (err) {
|
|
@@ -114,9 +114,9 @@ router.all('app/{*path}', async (ctx) => {
|
|
|
114
114
|
};
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
const fullPath = root ?
|
|
117
|
+
const fullPath = root ? path__default.join(rootPath, root, resourcePath) : path__default.join(rootPath, resourcePath);
|
|
118
118
|
try {
|
|
119
|
-
const file = await
|
|
119
|
+
const file = await fs__default.promises.readFile(fullPath);
|
|
120
120
|
const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
|
|
121
121
|
ctx.set('Content-Type', mimeType);
|
|
122
122
|
ctx.body = file;
|
|
@@ -164,7 +164,7 @@ router.all('apps/:app/{*path}', async (ctx) => {
|
|
|
164
164
|
mainDirMap.set(appName, mainDir);
|
|
165
165
|
}
|
|
166
166
|
const dir = join(mainDirMap.get(appName), 'route', ctx.path?.replace(apiPath, '/api') || '');
|
|
167
|
-
if (existsSync(dir) &&
|
|
167
|
+
if (existsSync(dir) && fs__default.statSync(dir).isFile()) {
|
|
168
168
|
ctx.status = 404;
|
|
169
169
|
ctx.body = {
|
|
170
170
|
code: 404,
|
|
@@ -211,7 +211,7 @@ router.all('apps/:app/{*path}', async (ctx) => {
|
|
|
211
211
|
ctx.status = 405;
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
214
|
-
const rootPath =
|
|
214
|
+
const rootPath = path__default.join(process.cwd(), 'node_modules', appName);
|
|
215
215
|
const resourcePath = formatPath(ctx.params?.path);
|
|
216
216
|
let root = '';
|
|
217
217
|
try {
|
|
@@ -227,9 +227,9 @@ router.all('apps/:app/{*path}', async (ctx) => {
|
|
|
227
227
|
};
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
|
-
const fullPath = root ?
|
|
230
|
+
const fullPath = root ? path__default.join(rootPath, root, resourcePath) : path__default.join(rootPath, resourcePath);
|
|
231
231
|
try {
|
|
232
|
-
const file = await
|
|
232
|
+
const file = await fs__default.promises.readFile(fullPath);
|
|
233
233
|
const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
|
|
234
234
|
ctx.set('Content-Type', mimeType);
|
|
235
235
|
ctx.body = file;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs__default, { existsSync } from 'fs';
|
|
2
2
|
|
|
3
3
|
const getModuelFile = (dir) => {
|
|
4
4
|
const dirMap = {
|
|
@@ -17,7 +17,7 @@ const getModuelFile = (dir) => {
|
|
|
17
17
|
};
|
|
18
18
|
for (const key in dirMap) {
|
|
19
19
|
const filePath = dirMap[key];
|
|
20
|
-
if (existsSync(filePath) &&
|
|
20
|
+
if (existsSync(filePath) && fs__default.statSync(filePath)) {
|
|
21
21
|
return filePath;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -4,9 +4,9 @@ export type DataMarkdownText = {
|
|
|
4
4
|
};
|
|
5
5
|
export type DataMarkdownMention = {
|
|
6
6
|
type: 'MD.mention';
|
|
7
|
-
value?: string;
|
|
7
|
+
value?: string | 'everyone';
|
|
8
8
|
options?: {
|
|
9
|
-
belong?: 'user' | 'channel'
|
|
9
|
+
belong?: 'user' | 'channel';
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
12
|
export type DataMarkdownContent = {
|