@maiyunnet/kebab 3.1.17 → 3.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/ws.js +8 -0
- package/package.json +1 -1
- package/sys/child.js +54 -41
- package/sys/ctr.d.ts +2 -2
- package/sys/route.js +3 -3
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '3.1.
|
|
9
|
+
export const VER = '3.1.18';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/ws.js
CHANGED
|
@@ -283,6 +283,10 @@ function bindPipe(s1, s2) {
|
|
|
283
283
|
switch (msg.opcode) {
|
|
284
284
|
case EOpcode.TEXT:
|
|
285
285
|
case EOpcode.BINARY: {
|
|
286
|
+
if (typeof msg.data === 'string') {
|
|
287
|
+
s2.writeText(msg.data);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
286
290
|
s2.writeBinary(msg.buffer);
|
|
287
291
|
break;
|
|
288
292
|
}
|
|
@@ -312,6 +316,10 @@ function bindPipe(s1, s2) {
|
|
|
312
316
|
switch (msg.opcode) {
|
|
313
317
|
case EOpcode.TEXT:
|
|
314
318
|
case EOpcode.BINARY: {
|
|
319
|
+
if (typeof msg.data === 'string') {
|
|
320
|
+
s1.writeText(msg.data);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
315
323
|
s1.writeBinary(msg.buffer);
|
|
316
324
|
break;
|
|
317
325
|
}
|
package/package.json
CHANGED
package/sys/child.js
CHANGED
|
@@ -8,7 +8,7 @@ import * as tls from 'tls';
|
|
|
8
8
|
import * as http from 'http';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
10
|
// --- 库 ---
|
|
11
|
-
import * as
|
|
11
|
+
import * as lFs from '#kebab/lib/fs.js';
|
|
12
12
|
import * as lCore from '#kebab/lib/core.js';
|
|
13
13
|
import * as lText from '#kebab/lib/text.js';
|
|
14
14
|
import * as sCtr from '#kebab/sys/ctr.js';
|
|
@@ -214,7 +214,7 @@ async function requestHandler(req, res, https) {
|
|
|
214
214
|
}
|
|
215
215
|
const uri = lText.parseUrl(`http${https ? 's' : ''}://${host}${req.url ?? ''}`);
|
|
216
216
|
/** --- 当前的 vhost 配置文件 --- */
|
|
217
|
-
const vhost = getVhostByHostname(uri.hostname ?? '');
|
|
217
|
+
const vhost = await getVhostByHostname(uri.hostname ?? '');
|
|
218
218
|
if (!vhost) {
|
|
219
219
|
req.socket.destroy();
|
|
220
220
|
return;
|
|
@@ -227,12 +227,6 @@ async function requestHandler(req, res, https) {
|
|
|
227
227
|
return;
|
|
228
228
|
*/
|
|
229
229
|
}
|
|
230
|
-
const vhostRoot = vhost.root.replace(/\${example}/g, kebab.ROOT_PATH + 'www/example/');
|
|
231
|
-
/** --- 网站绝对根目录,末尾带 / --- */
|
|
232
|
-
let rootPath = lText.isRealPath(vhostRoot) ? vhostRoot : kebab.WWW_CWD + vhostRoot;
|
|
233
|
-
if (!rootPath.endsWith('/')) {
|
|
234
|
-
rootPath += '/';
|
|
235
|
-
}
|
|
236
230
|
/** --- 请求的路径部分,前导带 / 末尾不一定,用户怎么请求就是什么 --- */
|
|
237
231
|
let path = uri.pathname ?? '/';
|
|
238
232
|
if (path !== '/') {
|
|
@@ -248,7 +242,7 @@ async function requestHandler(req, res, https) {
|
|
|
248
242
|
if (item !== '') {
|
|
249
243
|
// --- 判断 item 是文件还是文件夹 ---
|
|
250
244
|
/** --- 'abc' / 'def.json' --- */
|
|
251
|
-
let stat = await
|
|
245
|
+
let stat = await lFs.stats(vhost.real + now + item);
|
|
252
246
|
if (!stat) {
|
|
253
247
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
254
248
|
res.setHeader('content-length', 22);
|
|
@@ -259,7 +253,7 @@ async function requestHandler(req, res, https) {
|
|
|
259
253
|
if (stat.isDirectory()) {
|
|
260
254
|
now += item + '/';
|
|
261
255
|
// --- 判断是不是动态层 ---
|
|
262
|
-
stat = await
|
|
256
|
+
stat = await lFs.stats(vhost.real + now + 'kebab.json');
|
|
263
257
|
if (stat) {
|
|
264
258
|
// --- 动态层,交给 Route 处理器 ---
|
|
265
259
|
try {
|
|
@@ -267,7 +261,7 @@ async function requestHandler(req, res, https) {
|
|
|
267
261
|
'req': req,
|
|
268
262
|
'res': res,
|
|
269
263
|
'uri': uri,
|
|
270
|
-
'rootPath':
|
|
264
|
+
'rootPath': vhost.real + now,
|
|
271
265
|
'urlBase': '/' + now,
|
|
272
266
|
'path': path.slice(('/' + pathList.slice(0, i).join('/')).length + 1),
|
|
273
267
|
'timer': timer
|
|
@@ -295,7 +289,7 @@ async function requestHandler(req, res, https) {
|
|
|
295
289
|
}
|
|
296
290
|
else {
|
|
297
291
|
// --- 文件,直接输出并结束 ---
|
|
298
|
-
await
|
|
292
|
+
await lFs.readToResponse(vhost.real + now + item, req, res, stat);
|
|
299
293
|
return;
|
|
300
294
|
}
|
|
301
295
|
}
|
|
@@ -305,7 +299,7 @@ async function requestHandler(req, res, https) {
|
|
|
305
299
|
break;
|
|
306
300
|
}
|
|
307
301
|
// --- 本层是根,判断根是不是动态层 ---
|
|
308
|
-
const stat = await
|
|
302
|
+
const stat = await lFs.stats(vhost.real + now + 'kebab.json');
|
|
309
303
|
if (stat) {
|
|
310
304
|
// --- 动态层,交给 Route 处理器 ---
|
|
311
305
|
try {
|
|
@@ -313,7 +307,7 @@ async function requestHandler(req, res, https) {
|
|
|
313
307
|
'req': req,
|
|
314
308
|
'res': res,
|
|
315
309
|
'uri': uri,
|
|
316
|
-
'rootPath':
|
|
310
|
+
'rootPath': vhost.real + now,
|
|
317
311
|
'urlBase': '/' + now,
|
|
318
312
|
'path': path.slice(1),
|
|
319
313
|
'timer': timer
|
|
@@ -335,9 +329,9 @@ async function requestHandler(req, res, https) {
|
|
|
335
329
|
// --- 最后一层是目录,且不是动态层,判断有没有主页,有则输出,没有则 403 出错 ---
|
|
336
330
|
const indexFiles = ['index.html', 'index.htm'];
|
|
337
331
|
for (const indexFile of indexFiles) {
|
|
338
|
-
const stat = await
|
|
332
|
+
const stat = await lFs.isFile(vhost.real + now + indexFile);
|
|
339
333
|
if (stat) {
|
|
340
|
-
await
|
|
334
|
+
await lFs.readToResponse(vhost.real + now + indexFile, req, res, stat);
|
|
341
335
|
return;
|
|
342
336
|
}
|
|
343
337
|
}
|
|
@@ -357,17 +351,11 @@ async function upgradeHandler(req, socket, https) {
|
|
|
357
351
|
// --- 当前 uri ---
|
|
358
352
|
const uri = lText.parseUrl(`ws${https ? 's' : ''}://${req.headers['host'] ?? ''}${req.url ?? ''}`);
|
|
359
353
|
/** --- 当前的 vhost 配置文件 --- */
|
|
360
|
-
const vhost = getVhostByHostname(uri.hostname ?? '');
|
|
354
|
+
const vhost = await getVhostByHostname(uri.hostname ?? '');
|
|
361
355
|
if (!vhost) {
|
|
362
356
|
socket.destroy();
|
|
363
357
|
return;
|
|
364
358
|
}
|
|
365
|
-
const vhostRoot = vhost.root.replace(/\${example}/g, kebab.ROOT_PATH + 'www/example/');
|
|
366
|
-
/** --- 网站绝对根目录,末尾带 / --- */
|
|
367
|
-
let rootPath = lText.isRealPath(vhostRoot) ? vhostRoot : kebab.WWW_CWD + vhostRoot;
|
|
368
|
-
if (!rootPath.endsWith('/')) {
|
|
369
|
-
rootPath += '/';
|
|
370
|
-
}
|
|
371
359
|
/** --- 请求的路径部分,前导带 / 末尾不一定,用户怎么请求就是什么 --- */
|
|
372
360
|
let path = uri.pathname ?? '/';
|
|
373
361
|
if (path !== '/') {
|
|
@@ -383,7 +371,7 @@ async function upgradeHandler(req, socket, https) {
|
|
|
383
371
|
if (item !== '') {
|
|
384
372
|
// --- 判断 item 是文件还是文件夹 ---
|
|
385
373
|
/** --- 'abc' / 'def.json' --- */
|
|
386
|
-
let stat = await
|
|
374
|
+
let stat = await lFs.stats(vhost.real + now + item);
|
|
387
375
|
if (!stat) {
|
|
388
376
|
socket.destroy();
|
|
389
377
|
return;
|
|
@@ -391,14 +379,14 @@ async function upgradeHandler(req, socket, https) {
|
|
|
391
379
|
if (stat.isDirectory()) {
|
|
392
380
|
now += item + '/';
|
|
393
381
|
// --- 判断是不是动态层 ---
|
|
394
|
-
stat = await
|
|
382
|
+
stat = await lFs.stats(vhost.real + now + 'kebab.json');
|
|
395
383
|
if (stat) {
|
|
396
384
|
// --- 动态层,交给 Route 处理器 ---
|
|
397
385
|
if (await sRoute.run({
|
|
398
386
|
'req': req,
|
|
399
387
|
'socket': socket,
|
|
400
388
|
'uri': uri,
|
|
401
|
-
'rootPath':
|
|
389
|
+
'rootPath': vhost.real + now,
|
|
402
390
|
'urlBase': '/' + now,
|
|
403
391
|
'path': path.slice(('/' + pathList.slice(0, i).join('/')).length + 1)
|
|
404
392
|
})) {
|
|
@@ -418,14 +406,14 @@ async function upgradeHandler(req, socket, https) {
|
|
|
418
406
|
break;
|
|
419
407
|
}
|
|
420
408
|
// --- 判断根是不是动态层 ---
|
|
421
|
-
const stat = await
|
|
409
|
+
const stat = await lFs.stats(vhost.real + now + 'kebab.json');
|
|
422
410
|
if (stat) {
|
|
423
411
|
// --- 动态层,交给 Route 处理器 ---
|
|
424
412
|
if (await sRoute.run({
|
|
425
413
|
'req': req,
|
|
426
414
|
'socket': socket,
|
|
427
415
|
'uri': uri,
|
|
428
|
-
'rootPath':
|
|
416
|
+
'rootPath': vhost.real + now,
|
|
429
417
|
'urlBase': '/' + now,
|
|
430
418
|
'path': path.slice(1)
|
|
431
419
|
})) {
|
|
@@ -442,7 +430,7 @@ async function upgradeHandler(req, socket, https) {
|
|
|
442
430
|
*/
|
|
443
431
|
async function reload() {
|
|
444
432
|
// --- 清除全局 config,并重新加载全局信息 ---
|
|
445
|
-
const configContent = await
|
|
433
|
+
const configContent = await lFs.getContent(kebab.CONF_CWD + 'config.json', 'utf8');
|
|
446
434
|
if (!configContent) {
|
|
447
435
|
throw `File '${kebab.CONF_CWD}config.json' not found.`;
|
|
448
436
|
}
|
|
@@ -454,13 +442,13 @@ async function reload() {
|
|
|
454
442
|
lCore.globalConfig[key] = config[key];
|
|
455
443
|
}
|
|
456
444
|
// --- 重新加载 VHOST 信息(/conf/vhost/) ---
|
|
457
|
-
const files = await
|
|
445
|
+
const files = await lFs.readDir(kebab.VHOST_CWD);
|
|
458
446
|
vhosts = [];
|
|
459
447
|
for (const file of files) {
|
|
460
448
|
if (!file.name.endsWith('.json')) {
|
|
461
449
|
continue;
|
|
462
450
|
}
|
|
463
|
-
const fstr = await
|
|
451
|
+
const fstr = await lFs.getContent(kebab.VHOST_CWD + file.name, 'utf8');
|
|
464
452
|
if (!fstr) {
|
|
465
453
|
continue;
|
|
466
454
|
}
|
|
@@ -485,12 +473,12 @@ async function reloadCert() {
|
|
|
485
473
|
certLastLoad = Date.now();
|
|
486
474
|
const cl = [];
|
|
487
475
|
try {
|
|
488
|
-
const certConfig = await
|
|
476
|
+
const certConfig = await lFs.getContent(kebab.CONF_CWD + 'cert.json', 'utf8');
|
|
489
477
|
if (certConfig) {
|
|
490
478
|
const certs = lText.parseJson(certConfig);
|
|
491
479
|
for (const item of certs) {
|
|
492
|
-
const key = await
|
|
493
|
-
const cert = await
|
|
480
|
+
const key = await lFs.getContent(lText.isRealPath(item.key) ? item.key : kebab.CERT_CWD + item.key, 'utf8');
|
|
481
|
+
const cert = await lFs.getContent(lText.isRealPath(item.cert) ? item.cert : kebab.CERT_CWD + item.cert, 'utf8');
|
|
494
482
|
if (!cert || !key) {
|
|
495
483
|
continue;
|
|
496
484
|
}
|
|
@@ -571,37 +559,62 @@ process.on('message', function (msg) {
|
|
|
571
559
|
lCore.log({}, '[CHILD][process][message] ' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
572
560
|
});
|
|
573
561
|
});
|
|
562
|
+
/**
|
|
563
|
+
* --- 获取 vhost 的真实路径 ---
|
|
564
|
+
* --- 替换 ${example} 为实际路径 ---
|
|
565
|
+
* --- 如果不是真实路径,添加 WWW_CWD 前缀 ---
|
|
566
|
+
* --- 确保路径以 / 结尾 ---
|
|
567
|
+
*/
|
|
568
|
+
function getVhostReal(root) {
|
|
569
|
+
let real = root.replace(/\${example}/g, kebab.ROOT_PATH + 'www/example/');
|
|
570
|
+
real = lText.isRealPath(real) ? real : kebab.WWW_CWD + real;
|
|
571
|
+
return real.endsWith('/') ? real : real + '/';
|
|
572
|
+
}
|
|
574
573
|
/**
|
|
575
574
|
* --- 获取匹配的 vhost 对象 ---
|
|
576
575
|
* --- 如果有精准匹配,以精准匹配为准,否则为通配符匹配(vSub),最后全局泛匹配(vGlobal) ---
|
|
577
576
|
* @param hostname 当前的 hostname,不带端口
|
|
578
577
|
*/
|
|
579
|
-
function getVhostByHostname(hostname) {
|
|
580
|
-
let vGlobal
|
|
578
|
+
async function getVhostByHostname(hostname) {
|
|
579
|
+
let vGlobal;
|
|
580
|
+
let vGlobalReal = '';
|
|
581
|
+
let vSub;
|
|
582
|
+
let vSubReal = '';
|
|
581
583
|
for (const vhost of vhosts) {
|
|
582
584
|
for (let domain of vhost.domains) {
|
|
583
585
|
if (domain === '*') {
|
|
584
586
|
// --- 全局泛匹配 ---
|
|
585
|
-
|
|
587
|
+
const real = getVhostReal(vhost.root);
|
|
588
|
+
if (await lFs.isDir(real)) {
|
|
589
|
+
vGlobal = vhost;
|
|
590
|
+
vGlobalReal = real;
|
|
591
|
+
}
|
|
586
592
|
}
|
|
587
593
|
else if (domain.includes('*')) {
|
|
588
594
|
// --- 通配符匹配 ---
|
|
589
595
|
domain = domain.replace(/\./g, '\\.').replace(/\*/g, '[\\w-]+?');
|
|
590
596
|
if (new RegExp(`^${domain}$`).test(hostname)) {
|
|
591
|
-
|
|
597
|
+
const real = getVhostReal(vhost.root);
|
|
598
|
+
if (await lFs.isDir(real)) {
|
|
599
|
+
vSub = vhost;
|
|
600
|
+
vSubReal = real;
|
|
601
|
+
}
|
|
592
602
|
}
|
|
593
603
|
}
|
|
594
604
|
else if (domain === hostname) {
|
|
595
605
|
// --- 完全匹配 ---
|
|
596
|
-
|
|
606
|
+
const real = getVhostReal(vhost.root);
|
|
607
|
+
if (await lFs.isDir(real)) {
|
|
608
|
+
return { ...vhost, real };
|
|
609
|
+
}
|
|
597
610
|
}
|
|
598
611
|
}
|
|
599
612
|
}
|
|
600
613
|
if (vSub) {
|
|
601
|
-
return vSub;
|
|
614
|
+
return { ...vSub, 'real': vSubReal };
|
|
602
615
|
}
|
|
603
616
|
if (vGlobal) {
|
|
604
|
-
return vGlobal;
|
|
617
|
+
return { ...vGlobal, 'real': vGlobalReal };
|
|
605
618
|
}
|
|
606
619
|
return null;
|
|
607
620
|
}
|
package/sys/ctr.d.ts
CHANGED
|
@@ -115,11 +115,11 @@ export declare class Ctr {
|
|
|
115
115
|
*/
|
|
116
116
|
onData(data: Buffer | string, opcode: lWs.EOpcode): kebab.Json;
|
|
117
117
|
/**
|
|
118
|
-
* --- 包含所有 opcode 的消息,若要发送数据需自行调用 write
|
|
118
|
+
* --- 包含所有 opcode 的消息,若要发送数据需自行调用 write 方法,data 恒定为原始 buffer,返回 false 则不会执行默认方法 ---
|
|
119
119
|
* @param data 数据
|
|
120
120
|
* @param opcode opcode
|
|
121
121
|
*/
|
|
122
|
-
onMessage(data: Buffer
|
|
122
|
+
onMessage(data: Buffer, opcode: lWs.EOpcode): undefined | boolean | Promise<undefined | boolean>;
|
|
123
123
|
/**
|
|
124
124
|
* --- WebSocket 下连接恢复可写入状态后会调用此事件,可重写此方法 ---
|
|
125
125
|
*/
|
package/sys/route.js
CHANGED
|
@@ -274,7 +274,7 @@ export async function run(data) {
|
|
|
274
274
|
wsSocket.on('message', async function (msg) {
|
|
275
275
|
switch (msg.opcode) {
|
|
276
276
|
case ws.EOpcode.CLOSE: {
|
|
277
|
-
const r = await cctr['onMessage'](msg.
|
|
277
|
+
const r = await cctr['onMessage'](msg.buffer, msg.opcode);
|
|
278
278
|
if (r === false) {
|
|
279
279
|
break;
|
|
280
280
|
}
|
|
@@ -282,7 +282,7 @@ export async function run(data) {
|
|
|
282
282
|
break;
|
|
283
283
|
}
|
|
284
284
|
case ws.EOpcode.PING: {
|
|
285
|
-
const r = await cctr['onMessage'](msg.
|
|
285
|
+
const r = await cctr['onMessage'](msg.buffer, msg.opcode);
|
|
286
286
|
if (r === false) {
|
|
287
287
|
break;
|
|
288
288
|
}
|
|
@@ -292,7 +292,7 @@ export async function run(data) {
|
|
|
292
292
|
case ws.EOpcode.BINARY:
|
|
293
293
|
case ws.EOpcode.TEXT: {
|
|
294
294
|
try {
|
|
295
|
-
const r = await cctr['onMessage'](msg.
|
|
295
|
+
const r = await cctr['onMessage'](msg.buffer, msg.opcode);
|
|
296
296
|
if (r === false) {
|
|
297
297
|
break;
|
|
298
298
|
}
|