@radatek/microserver 2.0.2 → 2.2.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/dist/microserver.d.ts +72 -35
- package/dist/microserver.js +349 -243
- package/microserver.ts +3803 -0
- package/package.json +9 -4
- package/readme.md +50 -47
package/dist/microserver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MicroServer
|
|
3
|
-
* @version 2.0
|
|
3
|
+
* @version 2.2.0
|
|
4
4
|
* @package @radatek/microserver
|
|
5
5
|
* @copyright Darius Kisonas 2022
|
|
6
6
|
* @license MIT
|
|
@@ -12,7 +12,7 @@ import tls from 'tls';
|
|
|
12
12
|
import querystring from 'querystring';
|
|
13
13
|
import { Readable } from 'stream';
|
|
14
14
|
import fs from 'fs';
|
|
15
|
-
import path from 'path';
|
|
15
|
+
import path, { basename, extname } from 'path';
|
|
16
16
|
import crypto from 'crypto';
|
|
17
17
|
import zlib from 'zlib';
|
|
18
18
|
import { EventEmitter } from 'events';
|
|
@@ -21,6 +21,9 @@ const defaultExpire = 24 * 60 * 60;
|
|
|
21
21
|
const defaultMaxBodySize = 5 * 1024 * 1024;
|
|
22
22
|
const defaultMethods = 'HEAD,GET,POST,PUT,PATCH,DELETE';
|
|
23
23
|
function NOOP(...args) { }
|
|
24
|
+
function isFunction(fn) {
|
|
25
|
+
return typeof fn === 'function' && !fn.prototype?.constructor;
|
|
26
|
+
}
|
|
24
27
|
export class Warning extends Error {
|
|
25
28
|
constructor(text) {
|
|
26
29
|
super(text);
|
|
@@ -71,8 +74,9 @@ export class ServerRequest extends http.IncomingMessage {
|
|
|
71
74
|
_init(router) {
|
|
72
75
|
Object.assign(this, {
|
|
73
76
|
router,
|
|
77
|
+
auth: router.auth,
|
|
74
78
|
protocol: 'encrypted' in this.socket && this.socket.encrypted ? 'https' : 'http',
|
|
75
|
-
|
|
79
|
+
query: {},
|
|
76
80
|
params: {},
|
|
77
81
|
paramsList: [],
|
|
78
82
|
path: '/',
|
|
@@ -92,8 +96,8 @@ export class ServerRequest extends http.IncomingMessage {
|
|
|
92
96
|
this.pathname = pathname;
|
|
93
97
|
this.path = pathname.slice(pathname.lastIndexOf('/'));
|
|
94
98
|
this.baseUrl = pathname.slice(0, pathname.length - this.path.length);
|
|
95
|
-
this.
|
|
96
|
-
parsedUrl.searchParams.forEach((v, k) => this.
|
|
99
|
+
this.query = {};
|
|
100
|
+
parsedUrl.searchParams.forEach((v, k) => this.query[k] = v);
|
|
97
101
|
}
|
|
98
102
|
/** Rewrite request url */
|
|
99
103
|
rewrite(url) {
|
|
@@ -313,8 +317,9 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
313
317
|
this.statusCode = 200;
|
|
314
318
|
}
|
|
315
319
|
/** Send error reponse */
|
|
316
|
-
error(error
|
|
320
|
+
error(error) {
|
|
317
321
|
let code = 0;
|
|
322
|
+
let text;
|
|
318
323
|
if (error instanceof Error) {
|
|
319
324
|
if ('statusCode' in error)
|
|
320
325
|
code = error.statusCode;
|
|
@@ -322,7 +327,7 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
322
327
|
}
|
|
323
328
|
else if (typeof error === 'number') {
|
|
324
329
|
code = error;
|
|
325
|
-
text =
|
|
330
|
+
text = commonCodes[code] || 'Error';
|
|
326
331
|
}
|
|
327
332
|
else
|
|
328
333
|
text = error.toString();
|
|
@@ -399,19 +404,17 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
399
404
|
this.send(data);
|
|
400
405
|
}
|
|
401
406
|
/** Send json response in form { success: false, error: err } */
|
|
402
|
-
jsonError(error
|
|
407
|
+
jsonError(error) {
|
|
403
408
|
this.isJson = true;
|
|
404
|
-
this.statusCode = code || 200;
|
|
405
409
|
if (typeof error === 'number')
|
|
406
|
-
|
|
410
|
+
error = http.STATUS_CODES[error] || 'Error';
|
|
407
411
|
if (error instanceof Error)
|
|
408
412
|
return this.json(error);
|
|
409
413
|
this.json(typeof error === 'string' ? { success: false, error } : { success: false, ...error });
|
|
410
414
|
}
|
|
411
415
|
/** Send json response in form { success: true, ... } */
|
|
412
|
-
jsonSuccess(data
|
|
416
|
+
jsonSuccess(data) {
|
|
413
417
|
this.isJson = true;
|
|
414
|
-
this.statusCode = code || 200;
|
|
415
418
|
if (data instanceof Error)
|
|
416
419
|
return this.json(data);
|
|
417
420
|
this.json(typeof data === 'string' ? { success: true, message: data } : { success: true, ...data });
|
|
@@ -427,6 +430,18 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
427
430
|
this.statusCode = code || 302;
|
|
428
431
|
this.end();
|
|
429
432
|
}
|
|
433
|
+
/** Set status code */
|
|
434
|
+
status(code) {
|
|
435
|
+
this.statusCode = code;
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
download(path, filename) {
|
|
439
|
+
StaticPlugin.serveFile(this.req, this, {
|
|
440
|
+
path: path,
|
|
441
|
+
filename: filename || basename(path),
|
|
442
|
+
mimeType: StaticPlugin.mimeTypes[extname(path)] || 'application/octet-stream'
|
|
443
|
+
});
|
|
444
|
+
}
|
|
430
445
|
}
|
|
431
446
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
432
447
|
const DEFLATE_TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
|
@@ -681,7 +696,7 @@ export class WebSocket extends EventEmitter {
|
|
|
681
696
|
headers = [
|
|
682
697
|
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}`,
|
|
683
698
|
'Connection: close',
|
|
684
|
-
'Content-Type: text/html',
|
|
699
|
+
'Content-Type: ' + message.startsWith('<') ? 'text/html' : 'text/plain',
|
|
685
700
|
`Content-Length: ${Buffer.byteLength(message)}`,
|
|
686
701
|
'',
|
|
687
702
|
message
|
|
@@ -892,6 +907,48 @@ export class Controller {
|
|
|
892
907
|
return routes;
|
|
893
908
|
}
|
|
894
909
|
}
|
|
910
|
+
class Waiter {
|
|
911
|
+
constructor() {
|
|
912
|
+
this._waiters = {};
|
|
913
|
+
this._id = 0;
|
|
914
|
+
this._busy = 0;
|
|
915
|
+
}
|
|
916
|
+
get busy() {
|
|
917
|
+
return this._busy > 0;
|
|
918
|
+
}
|
|
919
|
+
startJob() {
|
|
920
|
+
this._busy++;
|
|
921
|
+
}
|
|
922
|
+
endJob(id) {
|
|
923
|
+
this._busy--;
|
|
924
|
+
if (!this._busy)
|
|
925
|
+
this.resolve(id || 'ready');
|
|
926
|
+
}
|
|
927
|
+
get nextId() {
|
|
928
|
+
return (++this._id).toString();
|
|
929
|
+
}
|
|
930
|
+
async wait(id) {
|
|
931
|
+
return new Promise(resolve => (this._waiters[id] = this._waiters[id] || []).push(resolve));
|
|
932
|
+
}
|
|
933
|
+
resolve(id) {
|
|
934
|
+
const resolvers = this._waiters[id];
|
|
935
|
+
if (resolvers)
|
|
936
|
+
for (const resolve of resolvers)
|
|
937
|
+
resolve(undefined);
|
|
938
|
+
delete this._waiters[id];
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
class EmitterWaiter extends Waiter {
|
|
942
|
+
constructor(emitter) {
|
|
943
|
+
super();
|
|
944
|
+
this._emitter = emitter;
|
|
945
|
+
}
|
|
946
|
+
resolve(id) {
|
|
947
|
+
if (id === 'ready')
|
|
948
|
+
this._emitter.emit(id);
|
|
949
|
+
super.resolve(id);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
895
952
|
/** Router */
|
|
896
953
|
export class Router extends EventEmitter {
|
|
897
954
|
/** @param {MicroServer} server */
|
|
@@ -901,8 +958,95 @@ export class Router extends EventEmitter {
|
|
|
901
958
|
this._stack = [];
|
|
902
959
|
this._stackAfter = [];
|
|
903
960
|
this._tree = {};
|
|
961
|
+
this._waiter = new Waiter();
|
|
904
962
|
this.server = server;
|
|
905
963
|
}
|
|
964
|
+
/** bind middleware or create one from string like: 'redirect:302,https://redirect.to', 'error:422', 'param:name=value', 'acl:users/get', 'model:User', 'group:Users', 'user:admin' */
|
|
965
|
+
bind(fn) {
|
|
966
|
+
if (typeof fn === 'string') {
|
|
967
|
+
let name = fn;
|
|
968
|
+
let idx = name.indexOf(':');
|
|
969
|
+
if (idx < 0 && name.includes('=')) {
|
|
970
|
+
name = 'param:' + name;
|
|
971
|
+
idx = 5;
|
|
972
|
+
}
|
|
973
|
+
if (idx >= 0) {
|
|
974
|
+
const v = name.slice(idx + 1);
|
|
975
|
+
const type = name.slice(0, idx);
|
|
976
|
+
// predefined middlewares
|
|
977
|
+
switch (type) {
|
|
978
|
+
// redirect:302,https://redirect.to
|
|
979
|
+
case 'redirect': {
|
|
980
|
+
let redirect = v.split(','), code = parseInt(v[0]);
|
|
981
|
+
if (!code || code < 301 || code > 399)
|
|
982
|
+
code = 302;
|
|
983
|
+
return (req, res) => res.redirect(code, redirect[1] || v);
|
|
984
|
+
}
|
|
985
|
+
// error:422
|
|
986
|
+
case 'error':
|
|
987
|
+
return (req, res) => res.error(parseInt(v) || 422);
|
|
988
|
+
// param:name=value
|
|
989
|
+
case 'param': {
|
|
990
|
+
idx = v.indexOf('=');
|
|
991
|
+
if (idx > 0) {
|
|
992
|
+
const prm = v.slice(0, idx), val = v.slice(idx + 1);
|
|
993
|
+
return (req, res, next) => { req.params[prm] = val; return next(); };
|
|
994
|
+
}
|
|
995
|
+
break;
|
|
996
|
+
}
|
|
997
|
+
case 'model': {
|
|
998
|
+
const model = v;
|
|
999
|
+
return (req, res) => {
|
|
1000
|
+
res.isJson = true;
|
|
1001
|
+
req.params.model = model;
|
|
1002
|
+
req.model = Model.models[model];
|
|
1003
|
+
if (!req.model) {
|
|
1004
|
+
console.error(`Data model ${model} not defined for request ${req.path}`);
|
|
1005
|
+
return res.error(422);
|
|
1006
|
+
}
|
|
1007
|
+
return req.model.handler(req, res);
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
// user:userid
|
|
1011
|
+
// group:user_groupid
|
|
1012
|
+
// acl:validacl
|
|
1013
|
+
case 'user':
|
|
1014
|
+
case 'group':
|
|
1015
|
+
case 'acl':
|
|
1016
|
+
return (req, res, next) => {
|
|
1017
|
+
if (type === 'user' && v === req.user?.id)
|
|
1018
|
+
return next();
|
|
1019
|
+
if (type === 'acl') {
|
|
1020
|
+
req.params.acl = v;
|
|
1021
|
+
if (req.auth?.acl(v))
|
|
1022
|
+
return next();
|
|
1023
|
+
}
|
|
1024
|
+
if (type === 'group') {
|
|
1025
|
+
req.params.group = v;
|
|
1026
|
+
if (req.user?.group === v)
|
|
1027
|
+
return next();
|
|
1028
|
+
}
|
|
1029
|
+
const accept = req.headers.accept || '';
|
|
1030
|
+
if (!res.isJson && req.auth?.options.redirect && req.method === 'GET' && !accept.includes('json') && (accept.includes('html') || accept.includes('*/*'))) {
|
|
1031
|
+
if (req.auth.options.redirect && req.url !== req.auth.options.redirect)
|
|
1032
|
+
return res.redirect(302, req.auth.options.redirect);
|
|
1033
|
+
else if (req.auth.options.mode !== 'cookie') {
|
|
1034
|
+
res.setHeader('WWW-Authenticate', `Basic realm="${req.auth.options.realm}"`);
|
|
1035
|
+
return res.error(401);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return res.error('Permission denied');
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
throw new Error('Invalid option: ' + name);
|
|
1043
|
+
}
|
|
1044
|
+
if (fn && typeof fn === 'object' && 'handler' in fn && typeof fn.handler === 'function')
|
|
1045
|
+
return fn.handler.bind(fn);
|
|
1046
|
+
if (typeof fn !== 'function')
|
|
1047
|
+
throw new Error('Invalid middleware: ' + String.toString.call(fn));
|
|
1048
|
+
return fn.bind(this);
|
|
1049
|
+
}
|
|
906
1050
|
/** Handler */
|
|
907
1051
|
handler(req, res, next, method) {
|
|
908
1052
|
const nextAfter = next;
|
|
@@ -1002,7 +1146,7 @@ export class Router extends EventEmitter {
|
|
|
1002
1146
|
url,
|
|
1003
1147
|
middlewares
|
|
1004
1148
|
});
|
|
1005
|
-
middlewares = middlewares.map(
|
|
1149
|
+
middlewares = middlewares.map(m => this.bind(m));
|
|
1006
1150
|
let item = this._tree[method];
|
|
1007
1151
|
if (!item)
|
|
1008
1152
|
item = this._tree[method] = { tree: {} };
|
|
@@ -1038,7 +1182,6 @@ export class Router extends EventEmitter {
|
|
|
1038
1182
|
if (!item[key])
|
|
1039
1183
|
item[key] = [];
|
|
1040
1184
|
item[key].push(...middlewares);
|
|
1041
|
-
return this;
|
|
1042
1185
|
}
|
|
1043
1186
|
/** Clear routes and middlewares */
|
|
1044
1187
|
clear() {
|
|
@@ -1052,71 +1195,79 @@ export class Router extends EventEmitter {
|
|
|
1052
1195
|
*
|
|
1053
1196
|
* @signature add(plugin: Plugin)
|
|
1054
1197
|
* @param {Plugin} plugin plugin module instance
|
|
1055
|
-
* @return {
|
|
1198
|
+
* @return {Promise<>}
|
|
1056
1199
|
*
|
|
1057
1200
|
* @signature add(pluginid: string, ...args: any)
|
|
1058
1201
|
* @param {string} pluginid pluginid module
|
|
1059
1202
|
* @param {...any} args arguments passed to constructor
|
|
1060
|
-
* @return {
|
|
1203
|
+
* @return {Promise<>}
|
|
1061
1204
|
*
|
|
1062
1205
|
* @signature add(pluginClass: typeof Plugin, ...args: any)
|
|
1063
1206
|
* @param {typeof Plugin} pluginClass plugin class
|
|
1064
1207
|
* @param {...any} args arguments passed to constructor
|
|
1065
|
-
* @return {
|
|
1208
|
+
* @return {Promise<>}
|
|
1066
1209
|
*
|
|
1067
1210
|
* @signature add(middleware: Middleware)
|
|
1068
1211
|
* @param {Middleware} middleware
|
|
1069
|
-
* @return {
|
|
1212
|
+
* @return {Promise<>}
|
|
1070
1213
|
*
|
|
1071
1214
|
* @signature add(methodUrl: string, ...middlewares: any)
|
|
1072
1215
|
* @param {string} methodUrl 'METHOD /url' or '/url'
|
|
1073
1216
|
* @param {...any} middlewares
|
|
1074
|
-
* @return {
|
|
1217
|
+
* @return {Promise<>}
|
|
1075
1218
|
*
|
|
1076
1219
|
* @signature add(methodUrl: string, controllerClass: typeof Controller)
|
|
1077
1220
|
* @param {string} methodUrl 'METHOD /url' or '/url'
|
|
1078
1221
|
* @param {typeof Controller} controllerClass
|
|
1079
|
-
* @return {
|
|
1222
|
+
* @return {Promise<>}
|
|
1080
1223
|
*
|
|
1081
1224
|
* @signature add(methodUrl: string, routes: Array<Array<any>>)
|
|
1082
1225
|
* @param {string} methodUrl 'METHOD /url' or '/url'
|
|
1083
1226
|
* @param {Array<Array<any>>} routes list with subroutes: ['METHOD /suburl', ...middlewares]
|
|
1084
|
-
* @return {
|
|
1227
|
+
* @return {Promise<>}
|
|
1085
1228
|
*
|
|
1086
1229
|
* @signature add(methodUrl: string, routes: Array<Array<any>>)
|
|
1087
1230
|
* @param {string} methodUrl 'METHOD /url' or '/url'
|
|
1088
1231
|
* @param {Array<Array<any>>} routes list with subroutes: ['METHOD /suburl', ...middlewares]
|
|
1089
|
-
* @return {
|
|
1232
|
+
* @return {Promise<>}
|
|
1090
1233
|
*
|
|
1091
1234
|
* @signature add(routes: { [key: string]: Array<any> })
|
|
1092
1235
|
* @param { {[key: string]: Array<any>} } routes list with subroutes: 'METHOD /suburl': [...middlewares]
|
|
1093
|
-
* @return {
|
|
1236
|
+
* @return {Promise<>}
|
|
1094
1237
|
*
|
|
1095
1238
|
* @signature add(methodUrl: string, routes: { [key: string]: Array<any> })
|
|
1096
1239
|
* @param {string} methodUrl 'METHOD /url' or '/url'
|
|
1097
1240
|
* @param { {[key: string]: Array<any>} } routes list with subroutes: 'METHOD /suburl': [...middlewares]
|
|
1098
|
-
* @return {
|
|
1241
|
+
* @return {Promise<>}
|
|
1099
1242
|
*/
|
|
1100
|
-
use(...args) {
|
|
1243
|
+
async use(...args) {
|
|
1101
1244
|
if (!args[0])
|
|
1102
|
-
return
|
|
1245
|
+
return;
|
|
1246
|
+
this.server._waiter.startJob();
|
|
1247
|
+
for (let i = 0; i < args.length; i++)
|
|
1248
|
+
args[i] = await args[i];
|
|
1103
1249
|
// use(plugin)
|
|
1104
|
-
if (args[0] instanceof Plugin)
|
|
1105
|
-
|
|
1250
|
+
if (args[0] instanceof Plugin) {
|
|
1251
|
+
await this._plugin(args[0]);
|
|
1252
|
+
return this.server._waiter.endJob();
|
|
1253
|
+
}
|
|
1106
1254
|
// use(pluginid, ...args)
|
|
1107
1255
|
if (typeof args[0] === 'string' && MicroServer.plugins[args[0]]) {
|
|
1108
1256
|
const constructor = MicroServer.plugins[args[0]];
|
|
1109
1257
|
const plugin = new constructor(this, ...args.slice(1));
|
|
1110
|
-
|
|
1258
|
+
await this._plugin(plugin);
|
|
1259
|
+
return this.server._waiter.endJob();
|
|
1111
1260
|
}
|
|
1112
1261
|
// use(PluginClass, ...args)
|
|
1113
|
-
if (args[0].prototype instanceof Plugin) {
|
|
1262
|
+
if (typeof args[0] === 'function' && args[0].prototype instanceof Plugin) {
|
|
1114
1263
|
const plugin = new args[0](this, ...args.slice(1));
|
|
1115
|
-
|
|
1264
|
+
await this._plugin(plugin);
|
|
1265
|
+
return this.server._waiter.endJob();
|
|
1116
1266
|
}
|
|
1117
1267
|
// use(middleware)
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
1268
|
+
if (isFunction(args[0])) {
|
|
1269
|
+
this._middleware(args[0]);
|
|
1270
|
+
return this.server._waiter.endJob();
|
|
1120
1271
|
}
|
|
1121
1272
|
let method = '*', url = '/';
|
|
1122
1273
|
if (typeof args[0] === 'string') {
|
|
@@ -1131,7 +1282,7 @@ export class Router extends EventEmitter {
|
|
|
1131
1282
|
}
|
|
1132
1283
|
// use('/url', ControllerClass)
|
|
1133
1284
|
if (typeof args[0] === 'function' && args[0].prototype instanceof Controller) {
|
|
1134
|
-
const routes = args[0].routes();
|
|
1285
|
+
const routes = await args[0].routes();
|
|
1135
1286
|
if (routes)
|
|
1136
1287
|
args[0] = routes;
|
|
1137
1288
|
}
|
|
@@ -1139,17 +1290,17 @@ export class Router extends EventEmitter {
|
|
|
1139
1290
|
if (Array.isArray(args[0])) {
|
|
1140
1291
|
if (method !== '*')
|
|
1141
1292
|
throw new Error('Invalid router usage');
|
|
1142
|
-
args[0]
|
|
1293
|
+
for (const item of args[0]) {
|
|
1143
1294
|
if (Array.isArray(item)) {
|
|
1144
1295
|
// [methodUrl, ...middlewares]
|
|
1145
1296
|
if (typeof item[0] !== 'string' || !item[0].match(/^(\w+ )?\//))
|
|
1146
1297
|
throw new Error('Url expected');
|
|
1147
|
-
|
|
1298
|
+
await this.use(item[0].replace(/\//, (url === '/' ? '' : url) + '/'), ...item.slice(1));
|
|
1148
1299
|
}
|
|
1149
1300
|
else
|
|
1150
1301
|
throw new Error('Invalid param');
|
|
1151
|
-
}
|
|
1152
|
-
return this;
|
|
1302
|
+
}
|
|
1303
|
+
return this.server._waiter.endJob();
|
|
1153
1304
|
}
|
|
1154
1305
|
// use('/url', {'METHOD /url': [...middlewares], ... } ])
|
|
1155
1306
|
if (typeof args[0] === 'object' && args[0].constructor === Object) {
|
|
@@ -1158,44 +1309,47 @@ export class Router extends EventEmitter {
|
|
|
1158
1309
|
for (const [subUrl, subArgs] of Object.entries(args[0])) {
|
|
1159
1310
|
if (!subUrl.match(/^(\w+ )?\//))
|
|
1160
1311
|
throw new Error('Url expected');
|
|
1161
|
-
this.use(subUrl.replace(/\//, (url === '/' ? '' : url) + '/'), ...(Array.isArray(subArgs) ? subArgs : [subArgs]));
|
|
1312
|
+
await this.use(subUrl.replace(/\//, (url === '/' ? '' : url) + '/'), ...(Array.isArray(subArgs) ? subArgs : [subArgs]));
|
|
1162
1313
|
}
|
|
1163
|
-
return this;
|
|
1314
|
+
return this.server._waiter.endJob();
|
|
1164
1315
|
}
|
|
1165
1316
|
// use('/url', ...middleware)
|
|
1166
|
-
|
|
1317
|
+
this._add(method, url, 'next', args.filter((o) => o));
|
|
1318
|
+
return this.server._waiter.endJob();
|
|
1167
1319
|
}
|
|
1168
1320
|
_middleware(middleware) {
|
|
1169
1321
|
if (!middleware)
|
|
1170
|
-
return
|
|
1322
|
+
return;
|
|
1171
1323
|
const priority = (middleware?.priority || 0) - 1;
|
|
1172
1324
|
const stack = priority < -1 ? this._stackAfter : this._stack;
|
|
1173
1325
|
const idx = stack.findIndex(f => 'priority' in f
|
|
1174
1326
|
&& priority >= (f.priority || 0));
|
|
1175
1327
|
stack.splice(idx < 0 ? stack.length : idx, 0, middleware);
|
|
1176
|
-
return this;
|
|
1177
1328
|
}
|
|
1178
|
-
_plugin(plugin) {
|
|
1329
|
+
async _plugin(plugin) {
|
|
1330
|
+
let added;
|
|
1179
1331
|
if (plugin.name) {
|
|
1180
1332
|
if (this.plugins[plugin.name])
|
|
1181
1333
|
throw new Error(`Plugin ${plugin.name} already added`);
|
|
1182
1334
|
this.plugins[plugin.name] = plugin;
|
|
1335
|
+
added = plugin.name;
|
|
1183
1336
|
}
|
|
1337
|
+
await plugin.initialise?.();
|
|
1184
1338
|
if (plugin.handler) {
|
|
1185
1339
|
const middleware = plugin.handler.bind(plugin);
|
|
1186
1340
|
middleware.plugin = plugin;
|
|
1187
1341
|
middleware.priority = plugin.priority;
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
if (plugin.routes) {
|
|
1191
|
-
if (typeof plugin.routes === 'function')
|
|
1192
|
-
this.use(plugin.routes());
|
|
1193
|
-
else
|
|
1194
|
-
this.use(plugin.routes);
|
|
1342
|
+
this._middleware(middleware);
|
|
1195
1343
|
}
|
|
1196
|
-
if (plugin.
|
|
1197
|
-
this.use(plugin.
|
|
1198
|
-
|
|
1344
|
+
if (plugin.routes)
|
|
1345
|
+
await this.use(isFunction(plugin.routes) ? await plugin.routes() : plugin.routes);
|
|
1346
|
+
if (added)
|
|
1347
|
+
this._waiter.resolve(added);
|
|
1348
|
+
}
|
|
1349
|
+
async waitPlugin(id) {
|
|
1350
|
+
if (!this.plugins[id])
|
|
1351
|
+
await this._waiter.wait(id);
|
|
1352
|
+
return this.plugins[id];
|
|
1199
1353
|
}
|
|
1200
1354
|
/** Add hook */
|
|
1201
1355
|
hook(url, ...mid) {
|
|
@@ -1203,7 +1357,7 @@ export class Router extends EventEmitter {
|
|
|
1203
1357
|
let method = '*';
|
|
1204
1358
|
if (m)
|
|
1205
1359
|
[method, url] = [m[1], m[2]];
|
|
1206
|
-
|
|
1360
|
+
this._add(method, url, 'hook', mid);
|
|
1207
1361
|
}
|
|
1208
1362
|
/** Check if middleware allready added */
|
|
1209
1363
|
has(mid) {
|
|
@@ -1211,10 +1365,9 @@ export class Router extends EventEmitter {
|
|
|
1211
1365
|
}
|
|
1212
1366
|
}
|
|
1213
1367
|
export class MicroServer extends EventEmitter {
|
|
1214
|
-
get plugins() { return this.router.plugins; }
|
|
1215
1368
|
constructor(config) {
|
|
1216
1369
|
super();
|
|
1217
|
-
this.
|
|
1370
|
+
this._waiter = new EmitterWaiter(this);
|
|
1218
1371
|
this._methods = {};
|
|
1219
1372
|
let promise = Promise.resolve();
|
|
1220
1373
|
this._init = (f, ...args) => {
|
|
@@ -1246,7 +1399,7 @@ export class MicroServer extends EventEmitter {
|
|
|
1246
1399
|
}
|
|
1247
1400
|
/** Add one time listener or call immediatelly for 'ready' */
|
|
1248
1401
|
once(name, cb) {
|
|
1249
|
-
if (name === 'ready' && this.
|
|
1402
|
+
if (name === 'ready' && this.isReady())
|
|
1250
1403
|
cb();
|
|
1251
1404
|
else
|
|
1252
1405
|
super.once(name, cb);
|
|
@@ -1254,11 +1407,22 @@ export class MicroServer extends EventEmitter {
|
|
|
1254
1407
|
}
|
|
1255
1408
|
/** Add listener and call immediatelly for 'ready' */
|
|
1256
1409
|
on(name, cb) {
|
|
1257
|
-
if (name === 'ready' && this.
|
|
1410
|
+
if (name === 'ready' && this.isReady())
|
|
1258
1411
|
cb();
|
|
1259
1412
|
super.on(name, cb);
|
|
1260
1413
|
return this;
|
|
1261
1414
|
}
|
|
1415
|
+
isReady() {
|
|
1416
|
+
return !this._waiter.busy;
|
|
1417
|
+
}
|
|
1418
|
+
async waitReady() {
|
|
1419
|
+
if (this.isReady())
|
|
1420
|
+
return;
|
|
1421
|
+
return this._waiter.wait("ready");
|
|
1422
|
+
}
|
|
1423
|
+
async waitPlugin(id) {
|
|
1424
|
+
await this.router.waitPlugin(id);
|
|
1425
|
+
}
|
|
1262
1426
|
/** Listen server, should be used only if config.listen is not set */
|
|
1263
1427
|
listen(config) {
|
|
1264
1428
|
const listen = (config?.listen || this.config.listen || 0) + '';
|
|
@@ -1284,164 +1448,62 @@ export class MicroServer extends EventEmitter {
|
|
|
1284
1448
|
});
|
|
1285
1449
|
}
|
|
1286
1450
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
this.
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
if (!
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
tlsOptionsReload(srv);
|
|
1324
|
-
break;
|
|
1325
|
-
default:
|
|
1326
|
-
port = port || '80';
|
|
1327
|
-
srv = http.createServer(handler);
|
|
1328
|
-
break;
|
|
1329
|
-
}
|
|
1330
|
-
this.servers.add(srv);
|
|
1331
|
-
if (port === '0') // skip listening
|
|
1332
|
-
ready(srv);
|
|
1333
|
-
else {
|
|
1334
|
-
srv.listen(parseInt(port), host?.replace(/[\[\]]/g, '') || '0.0.0.0', () => {
|
|
1335
|
-
const addr = srv.address();
|
|
1336
|
-
this.emit('listen', addr.port, addr.address, srv);
|
|
1337
|
-
ready(srv);
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
srv.on('error', err => {
|
|
1341
|
-
this.servers.delete(srv);
|
|
1342
|
-
srv.close();
|
|
1343
|
-
ready();
|
|
1344
|
-
this.emit('error', err);
|
|
1345
|
-
});
|
|
1346
|
-
srv.on('connection', s => {
|
|
1347
|
-
this.sockets.add(s);
|
|
1348
|
-
s.once('close', () => this.sockets.delete(s));
|
|
1451
|
+
const reg = /^((?<proto>\w+):\/\/)?(?<host>(\[[^\]]+\]|[a-z][^:,]+|\d+\.\d+\.\d+\.\d+))?:?(?<port>\d+)?/;
|
|
1452
|
+
listen.split(',').forEach(listen => {
|
|
1453
|
+
this._waiter.startJob();
|
|
1454
|
+
let { proto, host, port } = reg.exec(listen)?.groups || {};
|
|
1455
|
+
let srv;
|
|
1456
|
+
switch (proto) {
|
|
1457
|
+
case 'tcp':
|
|
1458
|
+
if (!config?.handler)
|
|
1459
|
+
throw new Error('Handler is required for tcp');
|
|
1460
|
+
srv = net.createServer(handler);
|
|
1461
|
+
break;
|
|
1462
|
+
case 'tls':
|
|
1463
|
+
if (!config?.handler)
|
|
1464
|
+
throw new Error('Handler is required for tls');
|
|
1465
|
+
srv = tls.createServer(tlsOptions(), handler);
|
|
1466
|
+
tlsOptionsReload(srv);
|
|
1467
|
+
break;
|
|
1468
|
+
case 'https':
|
|
1469
|
+
port = port || '443';
|
|
1470
|
+
srv = https.createServer(tlsOptions(), handler);
|
|
1471
|
+
tlsOptionsReload(srv);
|
|
1472
|
+
break;
|
|
1473
|
+
default:
|
|
1474
|
+
port = port || '80';
|
|
1475
|
+
srv = http.createServer(handler);
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
this.servers.add(srv);
|
|
1479
|
+
if (port === '0') // skip listening
|
|
1480
|
+
this._waiter.endJob();
|
|
1481
|
+
else {
|
|
1482
|
+
srv.listen(parseInt(port), host?.replace(/[\[\]]/g, '') || '0.0.0.0', () => {
|
|
1483
|
+
const addr = srv.address();
|
|
1484
|
+
this.emit('listen', addr.port, addr.address, srv);
|
|
1485
|
+
srv._ready = true;
|
|
1486
|
+
this._waiter.endJob();
|
|
1349
1487
|
});
|
|
1350
|
-
|
|
1351
|
-
|
|
1488
|
+
}
|
|
1489
|
+
srv.on('error', err => {
|
|
1490
|
+
srv.close();
|
|
1491
|
+
this.servers.delete(srv);
|
|
1492
|
+
if (!srv._ready)
|
|
1493
|
+
this._waiter.endJob();
|
|
1494
|
+
this.emit('error', err);
|
|
1495
|
+
});
|
|
1496
|
+
srv.on('connection', s => {
|
|
1497
|
+
this.sockets.add(s);
|
|
1498
|
+
s.once('close', () => this.sockets.delete(s));
|
|
1352
1499
|
});
|
|
1500
|
+
srv.on('upgrade', this.handlerUpgrade.bind(this));
|
|
1353
1501
|
});
|
|
1502
|
+
return this._waiter.wait('ready');
|
|
1354
1503
|
}
|
|
1355
|
-
/**
|
|
1356
|
-
bind(fn) {
|
|
1357
|
-
if (typeof fn === 'string') {
|
|
1358
|
-
let name = fn;
|
|
1359
|
-
let idx = name.indexOf(':');
|
|
1360
|
-
if (idx < 0 && name.includes('=')) {
|
|
1361
|
-
name = 'param:' + name;
|
|
1362
|
-
idx = 5;
|
|
1363
|
-
}
|
|
1364
|
-
if (idx >= 0) {
|
|
1365
|
-
const v = name.slice(idx + 1);
|
|
1366
|
-
const type = name.slice(0, idx);
|
|
1367
|
-
// predefined middlewares
|
|
1368
|
-
switch (type) {
|
|
1369
|
-
// redirect:302,https://redirect.to
|
|
1370
|
-
case 'redirect': {
|
|
1371
|
-
let redirect = v.split(','), code = parseInt(v[0]);
|
|
1372
|
-
if (!code || code < 301 || code > 399)
|
|
1373
|
-
code = 302;
|
|
1374
|
-
return (req, res) => res.redirect(code, redirect[1] || v);
|
|
1375
|
-
}
|
|
1376
|
-
// error:422
|
|
1377
|
-
case 'error':
|
|
1378
|
-
return (req, res) => res.error(parseInt(v) || 422);
|
|
1379
|
-
// param:name=value
|
|
1380
|
-
case 'param': {
|
|
1381
|
-
idx = v.indexOf('=');
|
|
1382
|
-
if (idx > 0) {
|
|
1383
|
-
const prm = v.slice(0, idx), val = v.slice(idx + 1);
|
|
1384
|
-
return (req, res, next) => { req.params[prm] = val; return next(); };
|
|
1385
|
-
}
|
|
1386
|
-
break;
|
|
1387
|
-
}
|
|
1388
|
-
case 'model': {
|
|
1389
|
-
const model = v;
|
|
1390
|
-
return (req, res) => {
|
|
1391
|
-
res.isJson = true;
|
|
1392
|
-
req.params.model = model;
|
|
1393
|
-
req.model = Model.models[model];
|
|
1394
|
-
if (!req.model) {
|
|
1395
|
-
console.error(`Data model ${model} not defined for request ${req.path}`);
|
|
1396
|
-
return res.error(422);
|
|
1397
|
-
}
|
|
1398
|
-
return req.model.handler(req, res);
|
|
1399
|
-
};
|
|
1400
|
-
}
|
|
1401
|
-
// user:userid
|
|
1402
|
-
// group:user_groupid
|
|
1403
|
-
// acl:validacl
|
|
1404
|
-
case 'user':
|
|
1405
|
-
case 'group':
|
|
1406
|
-
case 'acl':
|
|
1407
|
-
return (req, res, next) => {
|
|
1408
|
-
if (type === 'user' && v === req.user?.id)
|
|
1409
|
-
return next();
|
|
1410
|
-
if (type === 'acl') {
|
|
1411
|
-
req.params.acl = v;
|
|
1412
|
-
if (req.auth?.acl(v))
|
|
1413
|
-
return next();
|
|
1414
|
-
}
|
|
1415
|
-
if (type === 'group') {
|
|
1416
|
-
req.params.group = v;
|
|
1417
|
-
if (req.user?.group === v)
|
|
1418
|
-
return next();
|
|
1419
|
-
}
|
|
1420
|
-
const accept = req.headers.accept || '';
|
|
1421
|
-
if (!res.isJson && req.auth?.options.redirect && req.method === 'GET' && !accept.includes('json') && (accept.includes('html') || accept.includes('*/*'))) {
|
|
1422
|
-
if (req.auth.options.redirect && req.url !== req.auth.options.redirect)
|
|
1423
|
-
return res.redirect(302, req.auth.options.redirect);
|
|
1424
|
-
else if (req.auth.options.mode !== 'cookie') {
|
|
1425
|
-
res.setHeader('WWW-Authenticate', `Basic realm="${req.auth.options.realm}"`);
|
|
1426
|
-
return res.error(401);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
return res.error('Permission denied');
|
|
1430
|
-
};
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
throw new Error('Invalid option: ' + name);
|
|
1434
|
-
}
|
|
1435
|
-
if (fn && typeof fn === 'object' && 'handler' in fn && typeof fn.handler === 'function')
|
|
1436
|
-
return fn.handler.bind(fn);
|
|
1437
|
-
if (typeof fn !== 'function')
|
|
1438
|
-
throw new Error('Invalid middleware: ' + String.toString.call(fn));
|
|
1439
|
-
return fn.bind(this);
|
|
1440
|
-
}
|
|
1441
|
-
/** Add middleware, routes, etc.. see {Router.add} */
|
|
1504
|
+
/** Add middleware, routes, etc.. see {router.use} */
|
|
1442
1505
|
use(...args) {
|
|
1443
|
-
this.router.use(...args);
|
|
1444
|
-
return this;
|
|
1506
|
+
return this.router.use(...args);
|
|
1445
1507
|
}
|
|
1446
1508
|
/** Default server handler */
|
|
1447
1509
|
handler(req, res) {
|
|
@@ -1583,36 +1645,41 @@ export class MicroServer extends EventEmitter {
|
|
|
1583
1645
|
}
|
|
1584
1646
|
this.sockets.clear();
|
|
1585
1647
|
}).then(() => {
|
|
1586
|
-
this._ready = false;
|
|
1587
1648
|
this.emit('close');
|
|
1649
|
+
this._waiter.resolve("close");
|
|
1588
1650
|
});
|
|
1589
1651
|
}
|
|
1590
|
-
/** Add route, alias to `server.router.
|
|
1652
|
+
/** Add route, alias to `server.router.use(url, ...args)` */
|
|
1653
|
+
all(url, ...args) {
|
|
1654
|
+
this.router.use(url, ...args);
|
|
1655
|
+
return this;
|
|
1656
|
+
}
|
|
1657
|
+
/** Add route, alias to `server.router.use('GET ' + url, ...args)` */
|
|
1591
1658
|
get(url, ...args) {
|
|
1592
1659
|
this.router.use('GET ' + url, ...args);
|
|
1593
1660
|
return this;
|
|
1594
1661
|
}
|
|
1595
|
-
/** Add route, alias to `server.router.
|
|
1662
|
+
/** Add route, alias to `server.router.use('POST ' + url, ...args)` */
|
|
1596
1663
|
post(url, ...args) {
|
|
1597
1664
|
this.router.use('POST ' + url, ...args);
|
|
1598
1665
|
return this;
|
|
1599
1666
|
}
|
|
1600
|
-
/** Add route, alias to `server.router.
|
|
1667
|
+
/** Add route, alias to `server.router.use('PUT ' + url, ...args)` */
|
|
1601
1668
|
put(url, ...args) {
|
|
1602
1669
|
this.router.use('PUT ' + url, ...args);
|
|
1603
1670
|
return this;
|
|
1604
1671
|
}
|
|
1605
|
-
/** Add route, alias to `server.router.
|
|
1672
|
+
/** Add route, alias to `server.router.use('PATCH ' + url, ...args)` */
|
|
1606
1673
|
patch(url, ...args) {
|
|
1607
1674
|
this.router.use('PATCH ' + url, ...args);
|
|
1608
1675
|
return this;
|
|
1609
1676
|
}
|
|
1610
|
-
/** Add route, alias to `server.router.
|
|
1677
|
+
/** Add route, alias to `server.router.use('DELETE ' + url, ...args)` */
|
|
1611
1678
|
delete(url, ...args) {
|
|
1612
1679
|
this.router.use('DELETE ' + url, ...args);
|
|
1613
1680
|
return this;
|
|
1614
1681
|
}
|
|
1615
|
-
/** Add websocket handler, alias to `server.router.
|
|
1682
|
+
/** Add websocket handler, alias to `server.router.use('WEBSOCKET ' + url, ...args)` */
|
|
1616
1683
|
websocket(url, ...args) {
|
|
1617
1684
|
this.router.use('WEBSOCKET ' + url, ...args);
|
|
1618
1685
|
return this;
|
|
@@ -1697,7 +1764,7 @@ class StaticPlugin extends Plugin {
|
|
|
1697
1764
|
options = {};
|
|
1698
1765
|
if (typeof options === 'string')
|
|
1699
1766
|
options = { path: options };
|
|
1700
|
-
this.mimeTypes = { ...StaticPlugin.mimeTypes, ...options.mimeTypes };
|
|
1767
|
+
this.mimeTypes = options.mimeTypes ? { ...StaticPlugin.mimeTypes, ...options.mimeTypes } : Object.freeze(StaticPlugin.mimeTypes);
|
|
1701
1768
|
this.root = path.resolve((options.root || options?.path || 'public').replace(/^\//, '')) + path.sep;
|
|
1702
1769
|
this.ignore = (options.ignore || []).map((p) => path.normalize(path.join(this.root, p)) + path.sep);
|
|
1703
1770
|
this.index = options.index || 'index.html';
|
|
@@ -1714,7 +1781,7 @@ class StaticPlugin extends Plugin {
|
|
|
1714
1781
|
let filename = path.normalize(path.join(this.root, (req.params && req.params.path) || req.pathname));
|
|
1715
1782
|
if (!filename.startsWith(this.root)) // check root access
|
|
1716
1783
|
return next();
|
|
1717
|
-
const firstch =
|
|
1784
|
+
const firstch = basename(filename)[0];
|
|
1718
1785
|
if (firstch === '.' || firstch === '_') // hidden file
|
|
1719
1786
|
return next();
|
|
1720
1787
|
if (filename.endsWith(path.sep))
|
|
@@ -1736,27 +1803,61 @@ class StaticPlugin extends Plugin {
|
|
|
1736
1803
|
req.filename = filename;
|
|
1737
1804
|
return handler.call(this, req, res, next);
|
|
1738
1805
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1806
|
+
StaticPlugin.serveFile(req, res, {
|
|
1807
|
+
path: filename,
|
|
1808
|
+
mimeType,
|
|
1809
|
+
stats
|
|
1810
|
+
});
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
static serveFile(req, res, options) {
|
|
1814
|
+
const filePath = options.root ? path.join(options.root, options.path) : options.path;
|
|
1815
|
+
const statRes = (err, stats) => {
|
|
1816
|
+
if (err)
|
|
1817
|
+
return res.error(err);
|
|
1818
|
+
if (!stats.isFile())
|
|
1819
|
+
return res.error(404);
|
|
1820
|
+
if (!res.getHeader('Content-Type')) {
|
|
1821
|
+
if (options.mimeType)
|
|
1822
|
+
res.setHeader('Content-Type', options.mimeType);
|
|
1823
|
+
else
|
|
1824
|
+
res.setHeader('Content-Type', this.mimeTypes[path.extname(options.path)] || 'application/octet-stream');
|
|
1825
|
+
}
|
|
1826
|
+
if (options.filename)
|
|
1827
|
+
res.setHeader('Content-Disposition', 'attachment; filename="' + options.filename + '"');
|
|
1828
|
+
if (options.lastModified !== false)
|
|
1744
1829
|
res.setHeader('Last-Modified', stats.mtime.toUTCString());
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1830
|
+
res.setHeader('Content-Length', stats.size);
|
|
1831
|
+
if (options.etag !== false) {
|
|
1832
|
+
const etag = '"' + etagPrefix + stats.mtime.getTime().toString(32) + '"';
|
|
1833
|
+
if (req.headers['if-none-match'] === etag || req.headers['if-modified-since'] === stats.mtime.toUTCString()) {
|
|
1834
|
+
res.statusCode = 304;
|
|
1835
|
+
res.headersOnly = true;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
if (options.maxAge)
|
|
1839
|
+
res.setHeader('Cache-Control', 'max-age=' + options.maxAge);
|
|
1749
1840
|
if (res.headersOnly) {
|
|
1750
|
-
res.
|
|
1751
|
-
return
|
|
1841
|
+
res.end();
|
|
1842
|
+
return;
|
|
1752
1843
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1844
|
+
const streamOptions = { start: 0, end: stats.size - 1 };
|
|
1845
|
+
if (options.range !== false) {
|
|
1846
|
+
const range = req.headers['range'];
|
|
1847
|
+
if (range && range.startsWith('bytes=')) {
|
|
1848
|
+
const parts = range.slice(6).split('-');
|
|
1849
|
+
streamOptions.start = parseInt(parts[0]) || 0;
|
|
1850
|
+
streamOptions.end = parts[1] ? parseInt(parts[1]) : stats.size - 1;
|
|
1851
|
+
res.setHeader('Content-Range', `bytes ${streamOptions.start}-${streamOptions.end}/${stats.size}`);
|
|
1852
|
+
res.setHeader('Content-Length', streamOptions.end - streamOptions.start + 1);
|
|
1853
|
+
}
|
|
1756
1854
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1855
|
+
fs.createReadStream(filePath, streamOptions).pipe(res);
|
|
1856
|
+
};
|
|
1857
|
+
if (!options.stats)
|
|
1858
|
+
fs.stat(filePath, statRes);
|
|
1859
|
+
else
|
|
1860
|
+
statRes(null, options.stats);
|
|
1760
1861
|
}
|
|
1761
1862
|
}
|
|
1762
1863
|
/** Default mime types */
|
|
@@ -1770,12 +1871,17 @@ StaticPlugin.mimeTypes = {
|
|
|
1770
1871
|
'.css': 'text/css',
|
|
1771
1872
|
'.png': 'image/png',
|
|
1772
1873
|
'.jpg': 'image/jpeg',
|
|
1773
|
-
'.mp3': 'audio/mpeg',
|
|
1774
1874
|
'.svg': 'image/svg+xml',
|
|
1875
|
+
'.mp3': 'audio/mpeg',
|
|
1876
|
+
'.ogg': 'audio/ogg',
|
|
1877
|
+
'.mp4': 'video/mp4',
|
|
1775
1878
|
'.pdf': 'application/pdf',
|
|
1776
1879
|
'.woff': 'application/x-font-woff',
|
|
1777
1880
|
'.woff2': 'application/x-font-woff2',
|
|
1778
|
-
'.ttf': 'application/x-font-ttf'
|
|
1881
|
+
'.ttf': 'application/x-font-ttf',
|
|
1882
|
+
'.gz': 'application/gzip',
|
|
1883
|
+
'.zip': 'application/zip',
|
|
1884
|
+
'.tgz': 'application/gzip',
|
|
1779
1885
|
};
|
|
1780
1886
|
MicroServer.plugins.static = StaticPlugin;
|
|
1781
1887
|
export class ProxyPlugin extends Plugin {
|
|
@@ -2201,7 +2307,7 @@ class AuthPlugin extends Plugin {
|
|
|
2201
2307
|
if (sid)
|
|
2202
2308
|
token = sid.slice(sid.indexOf('=') + 1);
|
|
2203
2309
|
if (!token)
|
|
2204
|
-
token = req.
|
|
2310
|
+
token = req.query.token;
|
|
2205
2311
|
if (token) {
|
|
2206
2312
|
const now = new Date().getTime();
|
|
2207
2313
|
let usr, expire;
|
|
@@ -2794,7 +2900,7 @@ export class Model {
|
|
|
2794
2900
|
/** Microserver middleware */
|
|
2795
2901
|
handler(req, res) {
|
|
2796
2902
|
res.isJson = true;
|
|
2797
|
-
let filter, filterStr = req.
|
|
2903
|
+
let filter, filterStr = req.query.filter;
|
|
2798
2904
|
if (filterStr) {
|
|
2799
2905
|
try {
|
|
2800
2906
|
if (!filterStr.startsWith('{'))
|