mm_os 4.2.6 → 4.2.8
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.md +1 -1
- package/README_EN.md +2 -2
- package/adapter/adapter.js +105 -52
- package/adapter/custom_persistence.js +32 -31
- package/adapter/mqtt.js +59 -79
- package/adapter/socket.js +188 -58
- package/adapter/web.js +92 -68
- package/adapter/websocket.js +43 -146
- package/com/api/drive.js +732 -708
- package/com/api/index.js +196 -198
- package/com/cmd/config.tpl.json +48 -122
- package/com/cmd/drive.js +176 -1548
- package/com/cmd/index.js +287 -1066
- package/com/cmd/job/config.json +66 -0
- package/com/cmd/job/index.js +241 -0
- package/com/cmd/job/test.js +188 -0
- package/com/cmd/script.tpl.js +12 -33
- package/com/db/drive.js +2206 -2019
- package/com/db/index.js +241 -242
- package/com/event/config.tpl.json +3 -1
- package/com/event/drive.js +3 -1
- package/com/event/index.js +73 -49
- package/com/event/script.tpl.js +1 -1
- package/com/mqtt/config.tpl.json +16 -8
- package/com/mqtt/drive.js +696 -676
- package/com/mqtt/index.js +1012 -822
- package/com/mqtt/index.test.js +503 -0
- package/com/mqtt/mm_mqtt.js +446 -420
- package/com/mqtt/script.tpl.js +3 -7
- package/com/nav/drive.js +7 -7
- package/com/nav/index.js +228 -231
- package/com/param/index.js +19 -17
- package/com/pendant/index.js +467 -441
- package/com/socket/drive.js +679 -616
- package/com/socket/index.js +94 -48
- package/com/sql/drive.js +1283 -1259
- package/com/sql/index.js +152 -150
- package/com/static/drive.js +1 -1
- package/com/static/index.js +232 -226
- package/com/task/drive.js +428 -405
- package/com/task/index.js +154 -148
- package/com/template/drive.js +1 -1
- package/com/template/index.js +141 -138
- package/com.js +87 -6
- package/common/middleware/web_after/index.js +4 -10
- package/common/middleware/web_base/index.js +1 -1
- package/common/middleware/web_cors/index.js +1 -1
- package/common/middleware/web_error/index.js +131 -119
- package/common/middleware/web_error/middleware.json +2 -1
- package/common/middleware/web_logger/index.js +1 -1
- package/common/middleware/web_main/index.js +2 -0
- package/common/middleware/web_static/index.js +2 -2
- package/common/middleware/web_waf/index.js +1 -1
- package/core/app/config.tpl.json +4 -4
- package/core/app/index.js +710 -534
- package/core/middleware/index.js +1 -9
- package/core/mod/config.tpl.json +1 -1
- package/core/model/config.tpl.json +1 -1
- package/core/plugin/config.tpl.json +15 -3
- package/core/plugin/index.js +31 -0
- package/core/pusher/index.js +158 -161
- package/core/scene/loop.js +1306 -1255
- package/core/store/index.js +1794 -1755
- package/core/store/sql.js +1488 -1464
- package/core/zone/bat/index.js +744 -728
- package/core/zone/config.tpl.json +3 -3
- package/core/zone/index.js +655 -616
- package/os.js +0 -4
- package/package.json +16 -16
- package/server.js +583 -427
- package/com/cmd/nlp.js +0 -525
- package/core/app/index copy.js +0 -611
- package/software.js +0 -194
- package/tcp.js +0 -279
package/adapter/mqtt.js
CHANGED
|
@@ -13,10 +13,8 @@ class Mqtt extends Adapter {
|
|
|
13
13
|
cache: true,
|
|
14
14
|
prefix: 'mqtt_',
|
|
15
15
|
ttl: 3600000,
|
|
16
|
-
push_topic: 'client/iot/${client_id}',
|
|
17
|
-
receive_topic: 'client/iot/${client_id}',
|
|
18
16
|
auth_enabled: false,
|
|
19
|
-
auth_users: [], // 用户名密码列表,格式: {username: 'user', password: 'pass'}
|
|
17
|
+
auth_users: [], // 用户名密码列表,格式: { clientid: 'server', username: 'user', password: 'pass'}
|
|
20
18
|
};
|
|
21
19
|
/**
|
|
22
20
|
* 构造函数
|
|
@@ -34,17 +32,23 @@ class Mqtt extends Adapter {
|
|
|
34
32
|
* @returns {object} 适配器实例
|
|
35
33
|
* @private
|
|
36
34
|
*/
|
|
37
|
-
Mqtt.prototype._init = function (cache) {
|
|
35
|
+
Mqtt.prototype._init = async function (cache) {
|
|
38
36
|
// 创建自定义持久化实例
|
|
39
37
|
let persist = null;
|
|
40
|
-
if (this.
|
|
38
|
+
if (this.web && cache) {
|
|
41
39
|
persist = new Persist(this.config, cache);
|
|
42
40
|
this.log('info', 'MQTT使用自定义持久化');
|
|
43
41
|
}
|
|
44
|
-
// 创建aedes
|
|
42
|
+
// 创建aedes实例
|
|
43
|
+
// 注意: aedes v1构造函数不初始化persistence,需要调用listen()
|
|
45
44
|
this.broker = new Aedes({
|
|
46
|
-
|
|
45
|
+
persistence: persist,
|
|
46
|
+
preConnect: function (client, packet, callback) {
|
|
47
|
+
callback(null, true);
|
|
48
|
+
}
|
|
47
49
|
});
|
|
50
|
+
// 必须调用listen()初始化persistence,否则客户端连接时会崩溃
|
|
51
|
+
await this.broker.listen();
|
|
48
52
|
// 创建TCP服务器
|
|
49
53
|
this.web = net.createServer(this.broker.handle);
|
|
50
54
|
|
|
@@ -64,18 +68,18 @@ Mqtt.prototype._init = function (cache) {
|
|
|
64
68
|
|
|
65
69
|
/**
|
|
66
70
|
* 检查用户是否存在
|
|
67
|
-
* @param {string}
|
|
71
|
+
* @param {string} clientid 客户端ID
|
|
68
72
|
* @param {string} username 用户名
|
|
69
73
|
* @param {string} password 密码
|
|
70
74
|
* @returns {Promise<boolean>} 是否存在
|
|
71
75
|
*/
|
|
72
|
-
Mqtt.prototype.checkUser = async function (
|
|
73
|
-
this.log('debug', '检查用户',
|
|
76
|
+
Mqtt.prototype.checkUser = async function (clientid, username, password) {
|
|
77
|
+
this.log('debug', '检查用户', clientid, username);
|
|
74
78
|
let pass = password ? password.toString() : '';
|
|
75
79
|
// 查找匹配的用户
|
|
76
80
|
// 在实际应用中,这里可以改为异步数据库查询
|
|
77
81
|
// 例如: return await db.query('SELECT * FROM users WHERE username = ? AND password = ?', [username, password_str]);
|
|
78
|
-
return this.config.auth_users.find(u => u.username === username && u.password === pass);
|
|
82
|
+
return this.config.auth_users.find(u => u.clientid === clientid && u.username === username && u.password === pass);
|
|
79
83
|
};
|
|
80
84
|
|
|
81
85
|
/**
|
|
@@ -98,21 +102,21 @@ Mqtt.prototype._setupAuth = function () {
|
|
|
98
102
|
// 如果启用了身份验证
|
|
99
103
|
if (this.config.auth_enabled) {
|
|
100
104
|
// 监听认证事件
|
|
101
|
-
this.broker.
|
|
105
|
+
this.broker.authenticate = async (client, username, password, callback) => {
|
|
102
106
|
try {
|
|
103
107
|
// 转换密码格式为字符串
|
|
104
108
|
let password_str = password ? password.toString() : '';
|
|
105
109
|
|
|
106
|
-
// 记录认证尝试
|
|
107
|
-
this.log('debug', `MQTT认证尝试: clientId=${client.id}, username=${username}, password=${password_str ? '[存在]' : '[不存在]'}`);
|
|
110
|
+
// // 记录认证尝试
|
|
111
|
+
// this.log('debug', `MQTT认证尝试: clientId=${client.id}, username=${username}, password=${password_str ? '[存在]' : '[不存在]'}`);
|
|
108
112
|
|
|
109
113
|
// 如果提供了自定义验证函数,则使用自定义验证
|
|
110
114
|
let user = await this.auth(client, username, password_str);
|
|
111
115
|
if (user) {
|
|
112
|
-
this.log('info', `MQTT认证成功: clientId=${client.id}, username=${username}`);
|
|
116
|
+
// this.log('info', `MQTT认证成功: clientId=${client.id}, username=${username}`);
|
|
113
117
|
return callback(null, true);
|
|
114
118
|
} else {
|
|
115
|
-
this.log('warn', `MQTT认证失败: clientId=${client.id}, username=${username}, 用户名或密码错误`);
|
|
119
|
+
// this.log('warn', `MQTT认证失败: clientId=${client.id}, username=${username}, 用户名或密码错误`);
|
|
116
120
|
return callback(new Error('用户名或密码错误'));
|
|
117
121
|
}
|
|
118
122
|
} catch (error) {
|
|
@@ -120,75 +124,34 @@ Mqtt.prototype._setupAuth = function () {
|
|
|
120
124
|
return callback(new Error('认证服务内部错误'));
|
|
121
125
|
}
|
|
122
126
|
};
|
|
123
|
-
|
|
124
|
-
this.log('info', 'MQTT身份验证已启用');
|
|
125
127
|
}
|
|
126
128
|
};
|
|
127
129
|
|
|
128
130
|
/**
|
|
129
131
|
* 启动MQTT服务器
|
|
132
|
+
* @returns {Promise} 返回Promise,等待服务端启动完成
|
|
130
133
|
*/
|
|
131
134
|
Mqtt.prototype.start = function () {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
var _this = this;
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
let {
|
|
138
|
+
host,
|
|
139
|
+
port
|
|
140
|
+
} = _this.config;
|
|
141
|
+
|
|
142
|
+
_this.web.listen(port, host, () => {
|
|
143
|
+
_this.log('info', `MQTT服务端启动!访问地址:mqtt://${host}:${port}`);
|
|
144
|
+
resolve();
|
|
145
|
+
});
|
|
143
146
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
// 监听服务器错误
|
|
148
|
+
_this.web.on('error', (error) => {
|
|
149
|
+
_this.log('error', 'MQTT服务器启动失败:', error);
|
|
150
|
+
reject(error);
|
|
151
|
+
});
|
|
147
152
|
});
|
|
148
153
|
};
|
|
149
154
|
|
|
150
|
-
/**
|
|
151
|
-
* 发送消息
|
|
152
|
-
* @private
|
|
153
|
-
* @param {string} client_id 客户端ID
|
|
154
|
-
* @param {string} message 消息内容
|
|
155
|
-
* @param {object} [options] 发送选项
|
|
156
|
-
* @param {number} [options.qos=0] QoS级别(0, 1, 2)
|
|
157
|
-
* @param {boolean} [options.retain=false] 是否为retain消息
|
|
158
|
-
* @returns {boolean} 发送是否成功
|
|
159
|
-
*/
|
|
160
|
-
Mqtt.prototype._send = function (client_id, message, options) {
|
|
161
|
-
let client = this.clients.get(client_id);
|
|
162
|
-
if (client) {
|
|
163
|
-
try {
|
|
164
|
-
// 默认选项
|
|
165
|
-
const OPTS = {
|
|
166
|
-
qos: 0,
|
|
167
|
-
retain: false,
|
|
168
|
-
push_topic: null, ...options || {}};
|
|
169
|
-
|
|
170
|
-
// MQTT通常需要发布到特定主题,这里简单处理
|
|
171
|
-
// 实际应用中可能需要根据协议规范处理
|
|
172
|
-
let topic = OPTS.push_topic || this.config.push_topic.replace('${client_id}', client_id);
|
|
173
|
-
this.broker.publish({
|
|
174
|
-
topic: topic,
|
|
175
|
-
payload: message,
|
|
176
|
-
qos: OPTS.qos,
|
|
177
|
-
retain: OPTS.retain
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
this.log('debug', `MQTT服务器向 ${client_id} 发送消息: ${message}, qos: ${OPTS.qos}, retain: ${OPTS.retain}`);
|
|
181
|
-
return true;
|
|
182
|
-
} catch (error) {
|
|
183
|
-
this.log('error', 'MQTT发送消息失败:', error);
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
this.log('warn', `MQTT客户端 ${client_id} 不存在或已关闭`);
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
155
|
/**
|
|
193
156
|
* 发布消息到指定主题
|
|
194
157
|
* @param {string} topic 主题
|
|
@@ -202,8 +165,7 @@ Mqtt.prototype.publish = function (topic, message, options) {
|
|
|
202
165
|
try {
|
|
203
166
|
// 默认选项
|
|
204
167
|
const OPTS = {qos: 0,
|
|
205
|
-
retain: false,
|
|
206
|
-
push_topic: null, ...options || {}};
|
|
168
|
+
retain: false, ...options || {}};
|
|
207
169
|
|
|
208
170
|
this.broker.publish({
|
|
209
171
|
topic: topic,
|
|
@@ -246,7 +208,7 @@ Mqtt.prototype.stop = function () {
|
|
|
246
208
|
this._stop();
|
|
247
209
|
|
|
248
210
|
// 关闭服务器
|
|
249
|
-
this.
|
|
211
|
+
this.broker.close(() => {
|
|
250
212
|
// 清空客户端集合
|
|
251
213
|
this.clients.clear();
|
|
252
214
|
this.log('info', 'MQTT服务器已停止');
|
|
@@ -259,16 +221,34 @@ Mqtt.prototype.stop = function () {
|
|
|
259
221
|
*/
|
|
260
222
|
Mqtt.prototype._stop = function () {
|
|
261
223
|
// 关闭所有客户端连接
|
|
262
|
-
for (let [
|
|
224
|
+
for (let [clientid, client] of this.clients) {
|
|
263
225
|
try {
|
|
264
226
|
// 使用MQTT客户端的断开连接方法
|
|
265
227
|
if (client && client.close) {
|
|
266
228
|
client.close();
|
|
267
229
|
}
|
|
268
230
|
} catch (error) {
|
|
269
|
-
this.log('error', `关闭MQTT客户端 ${
|
|
231
|
+
this.log('error', `关闭MQTT客户端 ${clientid} 失败:`, error);
|
|
270
232
|
}
|
|
271
233
|
}
|
|
272
234
|
};
|
|
273
235
|
|
|
236
|
+
/**
|
|
237
|
+
* 监听MQTT事件
|
|
238
|
+
* @param {string} event 事件名称
|
|
239
|
+
* @param {...*} args 事件参数
|
|
240
|
+
*/
|
|
241
|
+
Mqtt.prototype.on = function (event, ...args) {
|
|
242
|
+
this.broker.on(event, ...args);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 取消监听MQTT事件
|
|
247
|
+
* @param {string} event 事件名称
|
|
248
|
+
* @param {...*} args 事件参数
|
|
249
|
+
*/
|
|
250
|
+
Mqtt.prototype.off = function (event, ...args) {
|
|
251
|
+
this.broker.off(event, ...args);
|
|
252
|
+
};
|
|
253
|
+
|
|
274
254
|
exports.Mqtt = Mqtt;
|
package/adapter/socket.js
CHANGED
|
@@ -9,106 +9,236 @@ class Socket extends Adapter {
|
|
|
9
9
|
host: '0.0.0.0',
|
|
10
10
|
port: 8200
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
* 构造函数
|
|
14
|
-
* @param {object} config 配置对象
|
|
15
|
-
* @param {object} parent 父对象
|
|
16
|
-
*/
|
|
17
12
|
constructor(config, parent) {
|
|
18
13
|
super({ ...Socket.config, ...config }, parent);
|
|
19
14
|
}
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
/**
|
|
23
|
-
*
|
|
18
|
+
* 初始化 - 供外部调用
|
|
19
|
+
* @param {object} server 服务器实例
|
|
24
20
|
* @returns {object} 适配器实例
|
|
25
|
-
* @private
|
|
26
21
|
*/
|
|
27
|
-
Socket.prototype.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
Socket.prototype.init = function (server) {
|
|
23
|
+
if (server) {
|
|
24
|
+
this.getServer = function () { return server; };
|
|
25
|
+
}
|
|
26
|
+
this._middleware = [];
|
|
31
27
|
return this;
|
|
32
28
|
};
|
|
33
29
|
|
|
30
|
+
/**
|
|
31
|
+
* 注册中间件
|
|
32
|
+
* @param {Function} middleware async (ctx, next) => {}
|
|
33
|
+
*/
|
|
34
|
+
Socket.prototype.use = function (middleware) {
|
|
35
|
+
this._middleware.push(middleware);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 启动适配器
|
|
40
|
+
*/
|
|
41
|
+
Socket.prototype.start = function () {
|
|
42
|
+
var self = this;
|
|
43
|
+
var { host, port } = this.config;
|
|
44
|
+
|
|
45
|
+
this.web = net.createServer(function (socket) {
|
|
46
|
+
self._onConn(socket);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.web.listen(port, host, function () {
|
|
50
|
+
self.log('info', 'Socket服务已启动!监听地址:' + host + ':' + port);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.web.on('error', function (error) {
|
|
54
|
+
self.log('error', 'Socket启动失败:', error);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
34
57
|
/**
|
|
35
58
|
* 客户端连接处理
|
|
36
|
-
* @param {Socket} socket
|
|
37
|
-
* @private
|
|
59
|
+
* @param {net.Socket} socket TCP socket
|
|
38
60
|
*/
|
|
39
61
|
Socket.prototype._onConn = function (socket) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
var self = this;
|
|
63
|
+
var client_ip = socket.remoteAddress || '127.0.0.1';
|
|
64
|
+
var clientid = 'tcp_' + this.genClientId();
|
|
65
|
+
|
|
66
|
+
this.clients.set(clientid, socket);
|
|
67
|
+
this.log('info', 'Socket客户端连接成功:', client_ip, clientid);
|
|
68
|
+
|
|
69
|
+
// 缓存未处理完的数据(TCP 粘包处理)
|
|
70
|
+
var buffer = '';
|
|
71
|
+
|
|
72
|
+
socket.on('data', function (data) {
|
|
73
|
+
buffer += data.toString();
|
|
74
|
+
|
|
75
|
+
// 尝试按换行符分割完整消息
|
|
76
|
+
var lines = buffer.split('\n');
|
|
77
|
+
// 最后一段可能是未完成的消息,保留到 buffer
|
|
78
|
+
buffer = lines.pop() || '';
|
|
79
|
+
|
|
80
|
+
for (var i = 0; i < lines.length; i++) {
|
|
81
|
+
var line = lines[i].trim();
|
|
82
|
+
if (!line) continue;
|
|
83
|
+
self._onData(line, socket, clientid);
|
|
84
|
+
}
|
|
52
85
|
});
|
|
53
86
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
socket.on('close', function () {
|
|
88
|
+
self._onClose(clientid);
|
|
89
|
+
self.clients.delete(clientid);
|
|
57
90
|
});
|
|
58
91
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
92
|
+
socket.on('error', function (err) {
|
|
93
|
+
self.log('error', 'Socket客户端错误:', clientid, err);
|
|
94
|
+
self.clients.delete(clientid);
|
|
62
95
|
});
|
|
63
96
|
};
|
|
64
97
|
|
|
65
98
|
/**
|
|
66
|
-
*
|
|
99
|
+
* 收到一行完整数据
|
|
100
|
+
* @param {string} body_str 消息内容
|
|
101
|
+
* @param {net.Socket} socket TCP socket
|
|
102
|
+
* @param {string} clientid 客户端ID
|
|
67
103
|
*/
|
|
68
|
-
Socket.prototype.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
Socket.prototype._onData = function (body_str, socket, clientid) {
|
|
105
|
+
var self = this;
|
|
106
|
+
|
|
107
|
+
// 尝试解析 JSON
|
|
108
|
+
var json;
|
|
109
|
+
if (body_str.indexOf('{') === 0 || body_str.indexOf('[') === 0) {
|
|
110
|
+
try {
|
|
111
|
+
json = JSON.parse(body_str);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// 非 JSON 格式,按原始消息处理
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 创建最小上下文
|
|
118
|
+
var ctx = {
|
|
119
|
+
clientid: clientid,
|
|
120
|
+
socket: socket,
|
|
121
|
+
message: json || body_str,
|
|
122
|
+
ip: socket.remoteAddress || '127.0.0.1',
|
|
123
|
+
send: function (data) {
|
|
124
|
+
var str = typeof data === 'object' ? JSON.stringify(data) : data;
|
|
125
|
+
if (socket.writable) {
|
|
126
|
+
socket.write(str + '\n');
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
close: function () {
|
|
130
|
+
socket.end();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (json && json.method) {
|
|
135
|
+
// 有 method 字段,走 methods 分发
|
|
136
|
+
this._dispatchMethod(json, ctx);
|
|
137
|
+
} else {
|
|
138
|
+
// 走中间件链
|
|
139
|
+
this._runMiddleware(ctx);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 分发方法调用
|
|
145
|
+
* @param {object} json {id, method, params}
|
|
146
|
+
* @param {object} ctx 上下文
|
|
147
|
+
*/
|
|
148
|
+
Socket.prototype._dispatchMethod = function (json, ctx) {
|
|
149
|
+
var self = this;
|
|
150
|
+
var { id, method, params } = json;
|
|
151
|
+
|
|
152
|
+
// 如果有 result 字段且是回复消息,触发回调
|
|
153
|
+
if (json.result && id) {
|
|
154
|
+
// 交由中间件处理(未来可扩展回调)
|
|
155
|
+
this._runMiddleware(ctx);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 查找方法
|
|
160
|
+
// 先从 adapter 自身的 methods 找,再从全局 $.methods 找
|
|
161
|
+
var handler = this.methods && this.methods[method];
|
|
162
|
+
if (!handler) {
|
|
163
|
+
handler = $.methods && $.methods[method];
|
|
164
|
+
}
|
|
73
165
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
166
|
+
if (handler) {
|
|
167
|
+
// 异步执行方法
|
|
168
|
+
Promise.resolve().then(function () {
|
|
169
|
+
return handler(params, ctx);
|
|
170
|
+
}).then(function (result) {
|
|
171
|
+
if (result !== undefined && result !== null && id) {
|
|
172
|
+
var ret = { id: id };
|
|
173
|
+
if (typeof result === 'object' && !Array.isArray(result)) {
|
|
174
|
+
Object.assign(ret, result);
|
|
175
|
+
} else {
|
|
176
|
+
ret.result = result;
|
|
177
|
+
}
|
|
178
|
+
ctx.send(ret);
|
|
179
|
+
}
|
|
180
|
+
}).catch(function (err) {
|
|
181
|
+
self.log('error', 'Socket方法执行错误:', err);
|
|
182
|
+
if (id) {
|
|
183
|
+
var err_ret = { id: id, error: { message: err.message || '执行失败' } };
|
|
184
|
+
ctx.send(err_ret);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
// 未找到方法,走中间件链
|
|
189
|
+
this._runMiddleware(ctx);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 运行中间件链
|
|
195
|
+
* @param {object} ctx 上下文
|
|
196
|
+
*/
|
|
197
|
+
Socket.prototype._runMiddleware = function (ctx) {
|
|
198
|
+
var mw = this._middleware;
|
|
199
|
+
if (mw.length === 0) return;
|
|
200
|
+
|
|
201
|
+
var idx = 0;
|
|
202
|
+
var self = this;
|
|
203
|
+
function next() {
|
|
204
|
+
var fn = mw[idx++];
|
|
205
|
+
if (fn) {
|
|
206
|
+
return Promise.resolve(fn(ctx, next));
|
|
207
|
+
}
|
|
208
|
+
return Promise.resolve();
|
|
209
|
+
}
|
|
210
|
+
next().catch(function (err) {
|
|
211
|
+
self.log('error', 'Socket中间件错误:', err);
|
|
77
212
|
});
|
|
78
213
|
};
|
|
79
214
|
|
|
80
215
|
/**
|
|
81
216
|
* 发送消息
|
|
82
|
-
* @param {string}
|
|
217
|
+
* @param {string} clientid 客户端ID
|
|
83
218
|
* @param {string} message 消息内容
|
|
84
219
|
* @returns {boolean} 发送是否成功
|
|
85
|
-
* @private
|
|
86
220
|
*/
|
|
87
|
-
Socket.prototype._send = function (
|
|
88
|
-
let socket = this.clients.get(
|
|
221
|
+
Socket.prototype._send = function (clientid, message) {
|
|
222
|
+
let socket = this.clients.get(clientid);
|
|
89
223
|
if (socket && !socket.destroyed) {
|
|
90
|
-
|
|
91
|
-
|
|
224
|
+
var str = typeof message === 'object' ? JSON.stringify(message) : message;
|
|
225
|
+
socket.write(str + '\n');
|
|
92
226
|
return true;
|
|
93
|
-
} else {
|
|
94
|
-
this.log('warn', `客户端 ${client_id} 不存在或已关闭`);
|
|
95
|
-
return false;
|
|
96
227
|
}
|
|
228
|
+
return false;
|
|
97
229
|
};
|
|
98
230
|
|
|
99
231
|
/**
|
|
100
232
|
* 停止适配器
|
|
101
233
|
*/
|
|
102
234
|
Socket.prototype.stop = function () {
|
|
103
|
-
// 调用父类的_stop方法
|
|
104
235
|
this._stop();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
236
|
+
if (this.web) {
|
|
237
|
+
this.web.close(function () {
|
|
238
|
+
this.clients.clear();
|
|
239
|
+
this.log('info', 'Socket服务已停止');
|
|
240
|
+
}.bind(this));
|
|
241
|
+
}
|
|
112
242
|
};
|
|
113
243
|
|
|
114
|
-
exports.Socket = Socket;
|
|
244
|
+
exports.Socket = Socket;
|
package/adapter/web.js
CHANGED
|
@@ -1,68 +1,92 @@
|
|
|
1
|
-
const Koa = require('koa');
|
|
2
|
-
const { Adapter } = require('./adapter');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Web适配器类
|
|
6
|
-
* 基于Koa框架的HTTP服务器适配器
|
|
7
|
-
*/
|
|
8
|
-
class Web extends Adapter {
|
|
9
|
-
static config = {
|
|
10
|
-
host: '0.0.0.0',
|
|
11
|
-
port: 8000
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 构造函数
|
|
16
|
-
* @param {object} config 配置参数
|
|
17
|
-
* @param {object} parent 父对象
|
|
18
|
-
*/
|
|
19
|
-
constructor(config, parent) {
|
|
20
|
-
super({ ...Web.config, ...config }, parent);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 预置
|
|
26
|
-
*/
|
|
27
|
-
Web.prototype._preset = function () {
|
|
28
|
-
this.web = new Koa(this.config);
|
|
29
|
-
// 设置cookie签名密钥
|
|
30
|
-
this.web.keys = ['mos-secret-key-2024', 'mos-backup-key-2024'];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
*
|
|
57
|
-
*/
|
|
58
|
-
Web.prototype.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
const Koa = require('koa');
|
|
2
|
+
const { Adapter } = require('./adapter');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Web适配器类
|
|
6
|
+
* 基于Koa框架的HTTP服务器适配器
|
|
7
|
+
*/
|
|
8
|
+
class Web extends Adapter {
|
|
9
|
+
static config = {
|
|
10
|
+
host: '0.0.0.0',
|
|
11
|
+
port: 8000,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 构造函数
|
|
16
|
+
* @param {object} config 配置参数
|
|
17
|
+
* @param {object} parent 父对象
|
|
18
|
+
*/
|
|
19
|
+
constructor(config, parent) {
|
|
20
|
+
super({ ...Web.config, ...config }, parent);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 预置
|
|
26
|
+
*/
|
|
27
|
+
Web.prototype._preset = function () {
|
|
28
|
+
this.web = new Koa(this.config);
|
|
29
|
+
// 设置cookie签名密钥
|
|
30
|
+
this.web.keys = ['mos-secret-key-2024', 'mos-backup-key-2024'];
|
|
31
|
+
|
|
32
|
+
// 添加客户端断开处理中间件(必须在最前面)
|
|
33
|
+
this.web.use(async (ctx, next) => {
|
|
34
|
+
try {
|
|
35
|
+
await next();
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
38
|
+
// 客户端断开连接,无需处理
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 使用中间件
|
|
48
|
+
* @param {Function} middleware 中间件函数
|
|
49
|
+
*/
|
|
50
|
+
Web.prototype.use = function (middleware) {
|
|
51
|
+
this.web.use(middleware);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 获取当前中间件处理函数
|
|
56
|
+
* @returns {Function} 组合后的中间件函数
|
|
57
|
+
*/
|
|
58
|
+
Web.prototype._getFn = function () {
|
|
59
|
+
return this.web.callback();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 初始化
|
|
64
|
+
* @param {object} server 服务器实例
|
|
65
|
+
* @returns {object} 适配器实例
|
|
66
|
+
*/
|
|
67
|
+
Web.prototype.init = function (server) {
|
|
68
|
+
if (server) {
|
|
69
|
+
this.getServer = function () {
|
|
70
|
+
return server;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return this;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 启动
|
|
78
|
+
*/
|
|
79
|
+
Web.prototype.start = function () {
|
|
80
|
+
var { host, port } = this.config;
|
|
81
|
+
this.server = this.web.listen(port, host, () => {
|
|
82
|
+
this.log('info', `web服务端已启动!访问网址:http://${host}:${port}`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 配置服务器超时参数
|
|
86
|
+
this.server.timeout = 60000; // 请求超时 60s(默认120s)
|
|
87
|
+
this.server.headersTimeout = 65000; // 请求头超时(需大于timeout)
|
|
88
|
+
this.server.keepAliveTimeout = 65000; // Keep-Alive 超时
|
|
89
|
+
this.server.requestTimeout = 60000; // Node.js 14+ 请求超时
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
exports.Web = Web;
|