@radatek/microserver 2.3.1 → 2.3.3
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/microserver.d.ts +52 -17
- package/microserver.js +150 -109
- package/package.json +1 -1
package/microserver.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MicroServer
|
|
3
|
-
* @version 2.3.
|
|
3
|
+
* @version 2.3.3
|
|
4
4
|
* @package @radatek/microserver
|
|
5
5
|
* @copyright Darius Kisonas 2022
|
|
6
6
|
* @license MIT
|
|
@@ -128,6 +128,7 @@ export interface WebSocketOptions {
|
|
|
128
128
|
autoPong?: boolean;
|
|
129
129
|
permessageDeflate?: boolean;
|
|
130
130
|
maxWindowBits?: number;
|
|
131
|
+
timeout?: number;
|
|
131
132
|
deflate?: boolean;
|
|
132
133
|
}
|
|
133
134
|
/** WebSocket class */
|
|
@@ -198,8 +199,8 @@ export declare class WebSocket extends EventEmitter {
|
|
|
198
199
|
* ```
|
|
199
200
|
*/
|
|
200
201
|
export declare class Controller<T extends Model<any> = any> {
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
req: ServerRequest<T>;
|
|
203
|
+
res: ServerResponse<T>;
|
|
203
204
|
get model(): T | undefined;
|
|
204
205
|
constructor(req: ServerRequest<T>, res: ServerResponse<T>);
|
|
205
206
|
/** Generate routes for this controller */
|
|
@@ -213,12 +214,11 @@ export interface Middleware {
|
|
|
213
214
|
plugin?: Plugin;
|
|
214
215
|
}
|
|
215
216
|
declare class Waiter {
|
|
216
|
-
|
|
217
|
-
startJob(): void;
|
|
217
|
+
isBusy(id?: string): boolean;
|
|
218
|
+
startJob(id?: string): void;
|
|
218
219
|
endJob(id?: string): void;
|
|
219
220
|
get nextId(): string;
|
|
220
|
-
wait(id
|
|
221
|
-
resolve(id: string): void;
|
|
221
|
+
wait(id?: string): Promise<void>;
|
|
222
222
|
}
|
|
223
223
|
/** Router */
|
|
224
224
|
export declare class Router extends EventEmitter {
|
|
@@ -373,7 +373,7 @@ export declare class MicroServer extends EventEmitter {
|
|
|
373
373
|
once(name: string, cb: Function): this;
|
|
374
374
|
/** Add listener and call immediatelly for 'ready' */
|
|
375
375
|
on(name: string, cb: Function): this;
|
|
376
|
-
isReady(): boolean;
|
|
376
|
+
get isReady(): boolean;
|
|
377
377
|
waitReady(): Promise<void>;
|
|
378
378
|
waitPlugin(id: string): Promise<void>;
|
|
379
379
|
/** Listen server, should be used only if config.listen is not set */
|
|
@@ -538,6 +538,33 @@ export interface AuthOptions {
|
|
|
538
538
|
/** Interal next cache cleanup time */
|
|
539
539
|
cacheCleanup?: number;
|
|
540
540
|
}
|
|
541
|
+
export interface AuthOptionsInternal extends AuthOptions {
|
|
542
|
+
/** Authentication token */
|
|
543
|
+
token: Buffer;
|
|
544
|
+
/** Users */
|
|
545
|
+
users: (usr: string, psw?: string) => Promise<UserInfo | undefined>;
|
|
546
|
+
/** Default ACL */
|
|
547
|
+
defaultAcl: {
|
|
548
|
+
[key: string]: boolean;
|
|
549
|
+
};
|
|
550
|
+
/** Expire time in seconds */
|
|
551
|
+
expire: number;
|
|
552
|
+
/** Authentication mode */
|
|
553
|
+
mode: 'cookie' | 'token';
|
|
554
|
+
/** Authentication realm for basic authentication */
|
|
555
|
+
realm: string;
|
|
556
|
+
/** Redirect URL */
|
|
557
|
+
redirect: string;
|
|
558
|
+
/** Authentication cache */
|
|
559
|
+
cache: {
|
|
560
|
+
[key: string]: {
|
|
561
|
+
data: UserInfo;
|
|
562
|
+
time: number;
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
/** Interal next cache cleanup time */
|
|
566
|
+
cacheCleanup: number;
|
|
567
|
+
}
|
|
541
568
|
/** Authentication class */
|
|
542
569
|
export declare class Auth {
|
|
543
570
|
/** Server request */
|
|
@@ -545,10 +572,8 @@ export declare class Auth {
|
|
|
545
572
|
/** Server response */
|
|
546
573
|
res: ServerResponse | undefined;
|
|
547
574
|
/** Authentication options */
|
|
548
|
-
options:
|
|
549
|
-
|
|
550
|
-
users: ((usr: string, psw?: string, salt?: string) => Promise<UserInfo | undefined>);
|
|
551
|
-
constructor(options?: AuthOptions);
|
|
575
|
+
options: AuthOptionsInternal;
|
|
576
|
+
constructor(options: AuthOptionsInternal, req?: ServerRequest, res?: ServerResponse);
|
|
552
577
|
/** Decode token */
|
|
553
578
|
decode(data: string): {
|
|
554
579
|
data: string;
|
|
@@ -708,12 +733,22 @@ export type ModelDocument<T extends ModelSchema> = {
|
|
|
708
733
|
export declare interface ModelCollections {
|
|
709
734
|
collection(name: string): Promise<MicroCollection>;
|
|
710
735
|
}
|
|
736
|
+
export declare class Models {
|
|
737
|
+
}
|
|
711
738
|
export declare class Model<TSchema extends ModelSchema> {
|
|
712
|
-
static collections
|
|
713
|
-
static models:
|
|
714
|
-
static
|
|
739
|
+
static collections: ModelCollections;
|
|
740
|
+
static models: Models;
|
|
741
|
+
static set db(db: any);
|
|
742
|
+
static get db(): any;
|
|
743
|
+
/** Dynamic model extension */
|
|
744
|
+
static dynamic<T extends Model<any>>(model: T, options: {
|
|
745
|
+
controller?: Controller;
|
|
746
|
+
collection?: MicroCollection<any>;
|
|
747
|
+
req?: ServerRequest;
|
|
748
|
+
} | Controller): T;
|
|
749
|
+
static register<K extends string, T extends Model<any>>(name: K, model: T): typeof Model;
|
|
715
750
|
/** Define model */
|
|
716
|
-
static define<T extends ModelSchema>(name:
|
|
751
|
+
static define<K extends string, T extends ModelSchema>(name: K, schema: T, options?: {
|
|
717
752
|
collection?: MicroCollection | Promise<MicroCollection>;
|
|
718
753
|
class?: typeof Model;
|
|
719
754
|
}): Model<T>;
|
|
@@ -778,7 +813,7 @@ export declare interface FindOptions {
|
|
|
778
813
|
}
|
|
779
814
|
/** Collection factory */
|
|
780
815
|
export declare class MicroCollectionStore {
|
|
781
|
-
constructor(dataPath?: string);
|
|
816
|
+
constructor(dataPath?: string, storeTimeDelay?: number);
|
|
782
817
|
/** Get collection */
|
|
783
818
|
collection(name: string): Promise<MicroCollection>;
|
|
784
819
|
}
|
package/microserver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MicroServer
|
|
3
|
-
* @version 2.3.
|
|
3
|
+
* @version 2.3.3
|
|
4
4
|
* @package @radatek/microserver
|
|
5
5
|
* @copyright Darius Kisonas 2022
|
|
6
6
|
* @license MIT
|
|
@@ -457,8 +457,10 @@ export class WebSocket extends EventEmitter {
|
|
|
457
457
|
maxPayload: 1024 * 1024,
|
|
458
458
|
permessageDeflate: false,
|
|
459
459
|
maxWindowBits: 15,
|
|
460
|
+
timeout: 120000,
|
|
460
461
|
...options
|
|
461
462
|
};
|
|
463
|
+
this._socket.setTimeout(this._options.timeout || 120000);
|
|
462
464
|
const key = req.headers['sec-websocket-key'];
|
|
463
465
|
const upgrade = req.headers.upgrade;
|
|
464
466
|
const version = +(req.headers['sec-websocket-version'] || 0);
|
|
@@ -836,10 +838,6 @@ export class Controller {
|
|
|
836
838
|
method = keyMatch[1];
|
|
837
839
|
url = keyMatch[2].startsWith('/') ? keyMatch[2] : ('/' + prefix + keyMatch[2]);
|
|
838
840
|
}
|
|
839
|
-
if (!url && !method) {
|
|
840
|
-
method = 'GET';
|
|
841
|
-
url = '/' + prefix + key;
|
|
842
|
-
}
|
|
843
841
|
if (!method)
|
|
844
842
|
return;
|
|
845
843
|
let autoAcl = method.toLowerCase();
|
|
@@ -906,14 +904,7 @@ export class Controller {
|
|
|
906
904
|
req.model = modelName instanceof Model ? modelName : Model.models[modelName];
|
|
907
905
|
if (!obj.model)
|
|
908
906
|
throw new InvalidData(modelName, 'model');
|
|
909
|
-
|
|
910
|
-
req.model = new Proxy(req.model, {
|
|
911
|
-
get: (target, prop) => {
|
|
912
|
-
if (prop === 'options')
|
|
913
|
-
return modelOptions;
|
|
914
|
-
return target[prop];
|
|
915
|
-
}
|
|
916
|
-
});
|
|
907
|
+
req.model = Model.dynamic(req.model, { controller: obj });
|
|
917
908
|
}
|
|
918
909
|
return func.apply(obj, req.paramsList);
|
|
919
910
|
});
|
|
@@ -922,46 +913,53 @@ export class Controller {
|
|
|
922
913
|
return routes;
|
|
923
914
|
}
|
|
924
915
|
}
|
|
925
|
-
class
|
|
916
|
+
class WaiterJob {
|
|
926
917
|
constructor() {
|
|
927
|
-
this._waiters =
|
|
928
|
-
this._id = 0;
|
|
918
|
+
this._waiters = [];
|
|
929
919
|
this._busy = 0;
|
|
930
920
|
}
|
|
931
|
-
|
|
932
|
-
return this._busy > 0;
|
|
933
|
-
}
|
|
934
|
-
startJob() {
|
|
921
|
+
start() {
|
|
935
922
|
this._busy++;
|
|
936
923
|
}
|
|
937
|
-
|
|
924
|
+
end() {
|
|
938
925
|
this._busy--;
|
|
926
|
+
for (const resolve of this._waiters.splice(0))
|
|
927
|
+
resolve();
|
|
928
|
+
}
|
|
929
|
+
async wait() {
|
|
939
930
|
if (!this._busy)
|
|
940
|
-
|
|
931
|
+
return;
|
|
932
|
+
return new Promise(resolve => this._waiters.push(resolve));
|
|
941
933
|
}
|
|
942
|
-
|
|
943
|
-
|
|
934
|
+
}
|
|
935
|
+
class Waiter {
|
|
936
|
+
constructor() {
|
|
937
|
+
this._id = 0;
|
|
938
|
+
this._waiters = {};
|
|
944
939
|
}
|
|
945
|
-
|
|
946
|
-
|
|
940
|
+
isBusy(id) {
|
|
941
|
+
const waiter = this._waiters[id || 'ready'];
|
|
942
|
+
return !!waiter?._busy;
|
|
947
943
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
delete this._waiters[id];
|
|
944
|
+
startJob(id) {
|
|
945
|
+
let waiter = this._waiters[id || 'ready'];
|
|
946
|
+
if (!waiter)
|
|
947
|
+
waiter = this._waiters[id || 'ready'] = new WaiterJob();
|
|
948
|
+
waiter._busy++;
|
|
954
949
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
this._emitter = emitter;
|
|
950
|
+
endJob(id) {
|
|
951
|
+
const waiter = this._waiters[id || 'ready'];
|
|
952
|
+
if (waiter)
|
|
953
|
+
waiter.end();
|
|
960
954
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
955
|
+
get nextId() {
|
|
956
|
+
return (++this._id).toString();
|
|
957
|
+
}
|
|
958
|
+
async wait(id) {
|
|
959
|
+
const waiter = this._waiters[id || 'ready'];
|
|
960
|
+
if (!waiter)
|
|
961
|
+
return;
|
|
962
|
+
return waiter.wait();
|
|
965
963
|
}
|
|
966
964
|
}
|
|
967
965
|
/** Router */
|
|
@@ -1361,7 +1359,7 @@ export class Router extends EventEmitter {
|
|
|
1361
1359
|
if (plugin.routes)
|
|
1362
1360
|
await this.use(isFunction(plugin.routes) ? await plugin.routes() : plugin.routes);
|
|
1363
1361
|
if (added)
|
|
1364
|
-
this.
|
|
1362
|
+
this.emit(added);
|
|
1365
1363
|
}
|
|
1366
1364
|
async waitPlugin(id) {
|
|
1367
1365
|
if (!this.plugins[id])
|
|
@@ -1384,12 +1382,9 @@ export class Router extends EventEmitter {
|
|
|
1384
1382
|
export class MicroServer extends EventEmitter {
|
|
1385
1383
|
constructor(config) {
|
|
1386
1384
|
super();
|
|
1387
|
-
this._waiter = new
|
|
1385
|
+
this._waiter = new Waiter();
|
|
1388
1386
|
this._methods = {};
|
|
1389
1387
|
let promise = Promise.resolve();
|
|
1390
|
-
this._init = (f, ...args) => {
|
|
1391
|
-
promise = promise.then(() => f.apply(this, args)).catch(e => this.emit('error', e));
|
|
1392
|
-
};
|
|
1393
1388
|
this.config = {
|
|
1394
1389
|
maxBodySize: defaultMaxBodySize,
|
|
1395
1390
|
methods: defaultMethods,
|
|
@@ -1406,17 +1401,20 @@ export class MicroServer extends EventEmitter {
|
|
|
1406
1401
|
if (config[key])
|
|
1407
1402
|
this.router.use(MicroServer.plugins[key], config[key]);
|
|
1408
1403
|
}
|
|
1409
|
-
if (config.listen)
|
|
1410
|
-
this.
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1404
|
+
if (config.listen) {
|
|
1405
|
+
this._waiter.startJob();
|
|
1406
|
+
this.listen({
|
|
1407
|
+
tls: config.tls,
|
|
1408
|
+
listen: config.listen || 8080
|
|
1409
|
+
}).then(() => {
|
|
1410
|
+
this._waiter.endJob();
|
|
1415
1411
|
});
|
|
1412
|
+
}
|
|
1413
|
+
this._waiter.wait().then(() => this.emit('ready'));
|
|
1416
1414
|
}
|
|
1417
1415
|
/** Add one time listener or call immediatelly for 'ready' */
|
|
1418
1416
|
once(name, cb) {
|
|
1419
|
-
if (name === 'ready' && this.isReady
|
|
1417
|
+
if (name === 'ready' && this.isReady)
|
|
1420
1418
|
cb();
|
|
1421
1419
|
else
|
|
1422
1420
|
super.once(name, cb);
|
|
@@ -1424,18 +1422,18 @@ export class MicroServer extends EventEmitter {
|
|
|
1424
1422
|
}
|
|
1425
1423
|
/** Add listener and call immediatelly for 'ready' */
|
|
1426
1424
|
on(name, cb) {
|
|
1427
|
-
if (name === 'ready' && this.isReady
|
|
1425
|
+
if (name === 'ready' && this.isReady)
|
|
1428
1426
|
cb();
|
|
1429
1427
|
super.on(name, cb);
|
|
1430
1428
|
return this;
|
|
1431
1429
|
}
|
|
1432
|
-
isReady() {
|
|
1433
|
-
return !this._waiter.
|
|
1430
|
+
get isReady() {
|
|
1431
|
+
return !this._waiter.isBusy();
|
|
1434
1432
|
}
|
|
1435
1433
|
async waitReady() {
|
|
1436
|
-
if (this.isReady
|
|
1434
|
+
if (this.isReady)
|
|
1437
1435
|
return;
|
|
1438
|
-
return this._waiter.wait(
|
|
1436
|
+
return this._waiter.wait();
|
|
1439
1437
|
}
|
|
1440
1438
|
async waitPlugin(id) {
|
|
1441
1439
|
await this.router.waitPlugin(id);
|
|
@@ -1467,7 +1465,7 @@ export class MicroServer extends EventEmitter {
|
|
|
1467
1465
|
}
|
|
1468
1466
|
const reg = /^((?<proto>\w+):\/\/)?(?<host>(\[[^\]]+\]|[a-z][^:,]+|\d+\.\d+\.\d+\.\d+))?:?(?<port>\d+)?/;
|
|
1469
1467
|
listen.split(',').forEach(listen => {
|
|
1470
|
-
this._waiter.startJob();
|
|
1468
|
+
this._waiter.startJob('listen');
|
|
1471
1469
|
let { proto, host, port } = reg.exec(listen)?.groups || {};
|
|
1472
1470
|
let srv;
|
|
1473
1471
|
switch (proto) {
|
|
@@ -1494,20 +1492,20 @@ export class MicroServer extends EventEmitter {
|
|
|
1494
1492
|
}
|
|
1495
1493
|
this.servers.add(srv);
|
|
1496
1494
|
if (port === '0') // skip listening
|
|
1497
|
-
this._waiter.endJob();
|
|
1495
|
+
this._waiter.endJob('listen');
|
|
1498
1496
|
else {
|
|
1499
1497
|
srv.listen(parseInt(port), host?.replace(/[\[\]]/g, '') || '0.0.0.0', () => {
|
|
1500
1498
|
const addr = srv.address();
|
|
1501
1499
|
this.emit('listen', addr.port, addr.address, srv);
|
|
1502
1500
|
srv._ready = true;
|
|
1503
|
-
this._waiter.endJob();
|
|
1501
|
+
this._waiter.endJob('listen');
|
|
1504
1502
|
});
|
|
1505
1503
|
}
|
|
1506
1504
|
srv.on('error', err => {
|
|
1507
1505
|
srv.close();
|
|
1508
1506
|
this.servers.delete(srv);
|
|
1509
1507
|
if (!srv._ready)
|
|
1510
|
-
this._waiter.endJob();
|
|
1508
|
+
this._waiter.endJob('listen');
|
|
1511
1509
|
this.emit('error', err);
|
|
1512
1510
|
});
|
|
1513
1511
|
srv.on('connection', s => {
|
|
@@ -1516,7 +1514,7 @@ export class MicroServer extends EventEmitter {
|
|
|
1516
1514
|
});
|
|
1517
1515
|
srv.on('upgrade', this.handlerUpgrade.bind(this));
|
|
1518
1516
|
});
|
|
1519
|
-
return this._waiter.wait('
|
|
1517
|
+
return this._waiter.wait('listen');
|
|
1520
1518
|
}
|
|
1521
1519
|
/** Add middleware, routes, etc.. see {router.use} */
|
|
1522
1520
|
use(...args) {
|
|
@@ -1663,7 +1661,6 @@ export class MicroServer extends EventEmitter {
|
|
|
1663
1661
|
this.sockets.clear();
|
|
1664
1662
|
}).then(() => {
|
|
1665
1663
|
this.emit('close');
|
|
1666
|
-
this._waiter.resolve("close");
|
|
1667
1664
|
});
|
|
1668
1665
|
}
|
|
1669
1666
|
/** Add route, alias to `server.router.use(url, ...args)` */
|
|
@@ -2006,31 +2003,12 @@ ProxyPlugin.validHeaders = {
|
|
|
2006
2003
|
MicroServer.plugins.proxy = ProxyPlugin;
|
|
2007
2004
|
/** Authentication class */
|
|
2008
2005
|
export class Auth {
|
|
2009
|
-
constructor(options) {
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
if (
|
|
2014
|
-
|
|
2015
|
-
if (!(token instanceof Buffer))
|
|
2016
|
-
token = Buffer.from(token);
|
|
2017
|
-
this.options = {
|
|
2018
|
-
token,
|
|
2019
|
-
users: options?.users,
|
|
2020
|
-
mode: options?.mode || 'cookie',
|
|
2021
|
-
defaultAcl: options?.defaultAcl || { '*': false },
|
|
2022
|
-
expire: options?.expire || defaultExpire,
|
|
2023
|
-
cache: options?.cache || {}
|
|
2024
|
-
};
|
|
2025
|
-
if (typeof options?.users === 'function')
|
|
2026
|
-
this.users = options.users;
|
|
2027
|
-
else
|
|
2028
|
-
this.users = async (usrid, psw) => {
|
|
2029
|
-
const users = this.options.users;
|
|
2030
|
-
const usr = users?.[usrid];
|
|
2031
|
-
if (usr && (psw === undefined || this.checkPassword(usrid, psw, usr.password || '')))
|
|
2032
|
-
return usr;
|
|
2033
|
-
};
|
|
2006
|
+
constructor(options, req, res) {
|
|
2007
|
+
this.options = options;
|
|
2008
|
+
this.req = req;
|
|
2009
|
+
this.res = res;
|
|
2010
|
+
if (req)
|
|
2011
|
+
req.auth = this;
|
|
2034
2012
|
}
|
|
2035
2013
|
/** Decode token */
|
|
2036
2014
|
decode(data) {
|
|
@@ -2106,7 +2084,7 @@ export class Auth {
|
|
|
2106
2084
|
data = JSON.stringify(usr);
|
|
2107
2085
|
else if (typeof usr === 'string') {
|
|
2108
2086
|
if (psw !== undefined) {
|
|
2109
|
-
const userInfo = await this.users(usr, psw);
|
|
2087
|
+
const userInfo = await this.options.users(usr, psw);
|
|
2110
2088
|
data = userInfo?.id || userInfo?._id;
|
|
2111
2089
|
}
|
|
2112
2090
|
else
|
|
@@ -2123,7 +2101,7 @@ export class Auth {
|
|
|
2123
2101
|
if (typeof usr === 'object')
|
|
2124
2102
|
usrInfo = usr;
|
|
2125
2103
|
if (typeof usr === 'string')
|
|
2126
|
-
usrInfo = await this.users(usr, psw
|
|
2104
|
+
usrInfo = await this.options.users(usr, psw);
|
|
2127
2105
|
if (usrInfo?.id || usrInfo?._id) {
|
|
2128
2106
|
const expire = Math.min(34560000, options?.expire || this.options.expire || defaultExpire);
|
|
2129
2107
|
const expireTime = new Date().getTime() + expire * 1000;
|
|
@@ -2289,17 +2267,32 @@ class AuthPlugin extends Plugin {
|
|
|
2289
2267
|
...options,
|
|
2290
2268
|
cacheCleanup: new Date().getTime()
|
|
2291
2269
|
};
|
|
2292
|
-
if (
|
|
2270
|
+
if (options?.token === defaultToken)
|
|
2293
2271
|
console.warn('Default token in auth plugin');
|
|
2272
|
+
let token = options?.token || defaultToken;
|
|
2273
|
+
if (!token || token.length !== 32)
|
|
2274
|
+
token = defaultToken;
|
|
2275
|
+
if (token.length !== 32)
|
|
2276
|
+
token = crypto.createHash('sha256').update(token).digest();
|
|
2277
|
+
if (!(token instanceof Buffer))
|
|
2278
|
+
token = Buffer.from(token);
|
|
2279
|
+
this.options.token = token;
|
|
2280
|
+
if (typeof options?.users === 'function')
|
|
2281
|
+
this.options.users = (usr, psw) => options.users(usr, psw);
|
|
2282
|
+
else {
|
|
2283
|
+
this.options.users = async (usrid, psw) => {
|
|
2284
|
+
const users = this.options.users;
|
|
2285
|
+
const usr = users?.[usrid];
|
|
2286
|
+
if (usr && (psw === undefined || router.auth?.checkPassword(usrid, psw, usr.password || '')))
|
|
2287
|
+
return usr;
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2294
2290
|
router.auth = new Auth(this.options);
|
|
2295
2291
|
}
|
|
2296
2292
|
/** Authentication middleware */
|
|
2297
2293
|
async handler(req, res, next) {
|
|
2298
2294
|
const options = this.options, cache = options.cache;
|
|
2299
|
-
const auth = new Auth(options);
|
|
2300
|
-
req.auth = auth;
|
|
2301
|
-
auth.req = req;
|
|
2302
|
-
auth.res = res;
|
|
2295
|
+
const auth = new Auth(this.options, req, res);
|
|
2303
2296
|
const authorization = req.headers.authorization || '';
|
|
2304
2297
|
if (authorization.startsWith('Basic ')) {
|
|
2305
2298
|
let usr = cache?.[authorization];
|
|
@@ -2308,7 +2301,7 @@ class AuthPlugin extends Plugin {
|
|
|
2308
2301
|
else {
|
|
2309
2302
|
const usrpsw = Buffer.from(authorization.slice(6), 'base64').toString('utf-8'), pos = usrpsw.indexOf(':'), username = usrpsw.slice(0, pos), psw = usrpsw.slice(pos + 1);
|
|
2310
2303
|
if (username && psw)
|
|
2311
|
-
req.user = await auth.users(username, psw);
|
|
2304
|
+
req.user = await auth.options.users(username, psw);
|
|
2312
2305
|
if (!req.user)
|
|
2313
2306
|
return res.error(401);
|
|
2314
2307
|
if (cache) // 1 hour to expire in cache
|
|
@@ -2354,7 +2347,7 @@ class AuthPlugin extends Plugin {
|
|
|
2354
2347
|
catch (e) { }
|
|
2355
2348
|
}
|
|
2356
2349
|
else {
|
|
2357
|
-
req.user = await auth.users(usrData.data);
|
|
2350
|
+
req.user = await auth.options.users(usrData.data);
|
|
2358
2351
|
if (!req.user) {
|
|
2359
2352
|
req.auth.logout();
|
|
2360
2353
|
return new AccessDenied();
|
|
@@ -2383,7 +2376,7 @@ export class FileStore {
|
|
|
2383
2376
|
this._dir = options?.dir || 'data';
|
|
2384
2377
|
this._cacheTimeout = options?.cacheTimeout || 2000;
|
|
2385
2378
|
this._cacheItems = options?.cacheItems || 10;
|
|
2386
|
-
this._debounceTimeout = options?.debounceTimeout
|
|
2379
|
+
this._debounceTimeout = options?.debounceTimeout ?? 1000;
|
|
2387
2380
|
this._iter = 0;
|
|
2388
2381
|
}
|
|
2389
2382
|
/** cleanup cache */
|
|
@@ -2435,16 +2428,16 @@ export class FileStore {
|
|
|
2435
2428
|
if (item && new Date().getTime() - item.atime < this._cacheTimeout)
|
|
2436
2429
|
return item.data;
|
|
2437
2430
|
try {
|
|
2438
|
-
const stat = await fs.promises.lstat(path.join(this._dir, name));
|
|
2439
|
-
if (item?.mtime !== stat.mtime.getTime()) {
|
|
2440
|
-
let data = JSON.parse(await fs.promises.readFile(path.join(this._dir, name), 'utf8') || '{}');
|
|
2431
|
+
const stat = await fs.promises.lstat(path.join(this._dir, name)).catch(() => null);
|
|
2432
|
+
if (!stat || item?.mtime !== stat.mtime.getTime()) {
|
|
2433
|
+
let data = stat ? JSON.parse(await fs.promises.readFile(path.join(this._dir, name), 'utf8').catch(() => '') || '{}') : {};
|
|
2441
2434
|
this._iter++;
|
|
2442
2435
|
this.cleanup();
|
|
2443
2436
|
if (autosave)
|
|
2444
2437
|
data = this.observe(data, () => this.save(name, data));
|
|
2445
2438
|
this._cache[name] = {
|
|
2446
2439
|
atime: new Date().getTime(),
|
|
2447
|
-
mtime: stat
|
|
2440
|
+
mtime: stat?.mtime.getTime() || new Date().getTime(),
|
|
2448
2441
|
data: data
|
|
2449
2442
|
};
|
|
2450
2443
|
return data;
|
|
@@ -2573,19 +2566,64 @@ function newObjectId() {
|
|
|
2573
2566
|
break;
|
|
2574
2567
|
return (new Date().getTime() / 1000 | 0).toString(16) + globalObjectId.toString('hex');
|
|
2575
2568
|
}
|
|
2569
|
+
class ModelCollectionsInternal {
|
|
2570
|
+
constructor() {
|
|
2571
|
+
this._wait = new Promise(resolve => this._ready = resolve);
|
|
2572
|
+
}
|
|
2573
|
+
set db(db) {
|
|
2574
|
+
Promise.resolve(db).then(db => this._ready(this._db = db));
|
|
2575
|
+
}
|
|
2576
|
+
get db() {
|
|
2577
|
+
return this._db;
|
|
2578
|
+
}
|
|
2579
|
+
async collection(name) {
|
|
2580
|
+
const db = await this._wait;
|
|
2581
|
+
return db.collection(name);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
export class Models {
|
|
2585
|
+
}
|
|
2576
2586
|
export class Model {
|
|
2577
|
-
static
|
|
2578
|
-
|
|
2587
|
+
static set db(db) {
|
|
2588
|
+
this.collections.db = db;
|
|
2589
|
+
}
|
|
2590
|
+
static get db() {
|
|
2591
|
+
return this.collections.db;
|
|
2592
|
+
}
|
|
2593
|
+
/** Dynamic model extension */
|
|
2594
|
+
static dynamic(model, options) {
|
|
2595
|
+
if (options instanceof Controller)
|
|
2596
|
+
options = { controller: options };
|
|
2597
|
+
const collection = options?.collection || model.collection;
|
|
2598
|
+
const modelOptions = { ...model.options };
|
|
2599
|
+
const req = options.req || options.controller?.req;
|
|
2600
|
+
if (req) {
|
|
2601
|
+
modelOptions.user = req.user;
|
|
2602
|
+
modelOptions.params = req.params;
|
|
2603
|
+
}
|
|
2604
|
+
return new Proxy(model, {
|
|
2605
|
+
get: (target, prop) => {
|
|
2606
|
+
if (prop === 'collection')
|
|
2607
|
+
return collection;
|
|
2608
|
+
if (prop === 'options')
|
|
2609
|
+
return modelOptions;
|
|
2610
|
+
return target[prop];
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
static register(name, model) {
|
|
2615
|
+
Model.models[name] = model;
|
|
2616
|
+
return this;
|
|
2579
2617
|
}
|
|
2580
2618
|
/** Define model */
|
|
2581
2619
|
static define(name, schema, options) {
|
|
2582
2620
|
options = options || {};
|
|
2583
|
-
if (!options.collection
|
|
2621
|
+
if (!options.collection)
|
|
2584
2622
|
options.collection = this.collections.collection(name);
|
|
2585
2623
|
const inst = options?.class
|
|
2586
2624
|
? new options.class(schema, { name, ...options })
|
|
2587
2625
|
: new Model(schema, { name, ...options });
|
|
2588
|
-
|
|
2626
|
+
this.register(name, inst);
|
|
2589
2627
|
return inst;
|
|
2590
2628
|
}
|
|
2591
2629
|
/** Create model acording to description */
|
|
@@ -2963,13 +3001,16 @@ export class Model {
|
|
|
2963
3001
|
}
|
|
2964
3002
|
}
|
|
2965
3003
|
}
|
|
3004
|
+
Model.collections = new ModelCollectionsInternal();
|
|
2966
3005
|
Model.models = {};
|
|
2967
3006
|
/** Collection factory */
|
|
2968
3007
|
export class MicroCollectionStore {
|
|
2969
|
-
constructor(dataPath) {
|
|
3008
|
+
constructor(dataPath, storeTimeDelay) {
|
|
2970
3009
|
this._collections = new Map();
|
|
2971
3010
|
if (dataPath)
|
|
2972
|
-
this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, '') });
|
|
3011
|
+
this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, ''), debounceTimeout: storeTimeDelay ?? 1000 });
|
|
3012
|
+
if (!Model.db)
|
|
3013
|
+
Model.db = this;
|
|
2973
3014
|
}
|
|
2974
3015
|
/** Get collection */
|
|
2975
3016
|
async collection(name) {
|