@radatek/microserver 2.3.0 → 2.3.2
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 +47 -25
- package/microserver.js +193 -176
- 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.2
|
|
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 */
|
|
@@ -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;
|
|
@@ -709,9 +734,10 @@ export declare interface ModelCollections {
|
|
|
709
734
|
collection(name: string): Promise<MicroCollection>;
|
|
710
735
|
}
|
|
711
736
|
export declare class Model<TSchema extends ModelSchema> {
|
|
712
|
-
static collections
|
|
737
|
+
static collections: ModelCollections;
|
|
713
738
|
static models: Record<string, Model<any>>;
|
|
714
|
-
static
|
|
739
|
+
static set db(db: any);
|
|
740
|
+
static get db(): any;
|
|
715
741
|
/** Define model */
|
|
716
742
|
static define<T extends ModelSchema>(name: string, schema: T, options?: {
|
|
717
743
|
collection?: MicroCollection | Promise<MicroCollection>;
|
|
@@ -739,9 +765,9 @@ export declare class Model<TSchema extends ModelSchema> {
|
|
|
739
765
|
/** Find many documents */
|
|
740
766
|
findMany(query: Query, options?: ModelContextOptions): Promise<ModelDocument<TSchema>[]>;
|
|
741
767
|
/** Insert a new document */
|
|
742
|
-
insert(data: Record<string, any>, options?: ModelContextOptions): Promise<
|
|
768
|
+
insert(data: Record<string, any>, options?: ModelContextOptions): Promise<ModelDocument<TSchema>>;
|
|
743
769
|
/** Update one matching document */
|
|
744
|
-
update(query: Record<string, any>, options?: ModelContextOptions): Promise<
|
|
770
|
+
update(query: Record<string, any>, options?: ModelContextOptions): Promise<ModelDocument<TSchema>>;
|
|
745
771
|
/** Delete one matching document */
|
|
746
772
|
delete(query: Query, options?: ModelContextOptions): Promise<void>;
|
|
747
773
|
/** Microserver middleware */
|
|
@@ -750,10 +776,6 @@ export declare class Model<TSchema extends ModelSchema> {
|
|
|
750
776
|
export declare interface MicroCollectionOptions<T extends ModelSchema = any> {
|
|
751
777
|
/** Collection name */
|
|
752
778
|
name?: string;
|
|
753
|
-
/** Collection persistent store */
|
|
754
|
-
store?: FileStore;
|
|
755
|
-
/** Custom data loader */
|
|
756
|
-
load?: (col: MicroCollection) => Promise<object>;
|
|
757
779
|
/** Custom data saver */
|
|
758
780
|
save?: (id: string, doc: ModelDocument<T> | undefined, col: MicroCollection) => Promise<ModelDocument<T>>;
|
|
759
781
|
/** Preloaded data object */
|
|
@@ -773,8 +795,8 @@ export declare interface FindOptions {
|
|
|
773
795
|
query?: Query;
|
|
774
796
|
/** is upsert */
|
|
775
797
|
upsert?: boolean;
|
|
776
|
-
/** is
|
|
777
|
-
|
|
798
|
+
/** is upsert */
|
|
799
|
+
delete?: boolean;
|
|
778
800
|
/** update object */
|
|
779
801
|
update?: Query;
|
|
780
802
|
/** maximum number of hits */
|
|
@@ -782,7 +804,7 @@ export declare interface FindOptions {
|
|
|
782
804
|
}
|
|
783
805
|
/** Collection factory */
|
|
784
806
|
export declare class MicroCollectionStore {
|
|
785
|
-
constructor(dataPath?: string);
|
|
807
|
+
constructor(dataPath?: string, storeTimeDelay?: number);
|
|
786
808
|
/** Get collection */
|
|
787
809
|
collection(name: string): Promise<MicroCollection>;
|
|
788
810
|
}
|
|
@@ -802,20 +824,20 @@ export declare class MicroCollection<TSchema extends ModelSchema = any> {
|
|
|
802
824
|
/** Find all matching documents */
|
|
803
825
|
find(query: Query): Cursor<TSchema>;
|
|
804
826
|
/** Find and modify one matching document */
|
|
805
|
-
findAndModify(options: FindOptions): Promise<
|
|
827
|
+
findAndModify(options: FindOptions): Promise<ModelDocument<TSchema> | undefined>;
|
|
806
828
|
/** Insert one document */
|
|
807
829
|
insertOne(doc: ModelDocument<TSchema>): Promise<ModelDocument<TSchema>>;
|
|
808
830
|
/** Insert multiple documents */
|
|
809
831
|
insertMany(docs: ModelDocument<TSchema>[]): Promise<ModelDocument<TSchema>[]>;
|
|
810
832
|
/** Delete one matching document */
|
|
811
|
-
deleteOne(query: Query): Promise<
|
|
833
|
+
deleteOne(query: Query): Promise<number>;
|
|
812
834
|
/** Delete all matching documents */
|
|
813
835
|
deleteMany(query: Query): Promise<number>;
|
|
814
|
-
updateOne(query: Query,
|
|
836
|
+
updateOne(query: Query, update: Record<string, any>, options?: FindOptions): Promise<{
|
|
815
837
|
upsertedId: any;
|
|
816
838
|
modifiedCount: number;
|
|
817
839
|
}>;
|
|
818
|
-
updateMany(query: Query, update:
|
|
840
|
+
updateMany(query: Query, update: Record<string, any>, options?: FindOptions): Promise<{
|
|
819
841
|
upsertedId: any;
|
|
820
842
|
modifiedCount: number;
|
|
821
843
|
}>;
|
package/microserver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MicroServer
|
|
3
|
-
* @version 2.3.
|
|
3
|
+
* @version 2.3.2
|
|
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);
|
|
@@ -922,46 +924,53 @@ export class Controller {
|
|
|
922
924
|
return routes;
|
|
923
925
|
}
|
|
924
926
|
}
|
|
925
|
-
class
|
|
927
|
+
class WaiterJob {
|
|
926
928
|
constructor() {
|
|
927
|
-
this._waiters =
|
|
928
|
-
this._id = 0;
|
|
929
|
+
this._waiters = [];
|
|
929
930
|
this._busy = 0;
|
|
930
931
|
}
|
|
931
|
-
|
|
932
|
-
return this._busy > 0;
|
|
933
|
-
}
|
|
934
|
-
startJob() {
|
|
932
|
+
start() {
|
|
935
933
|
this._busy++;
|
|
936
934
|
}
|
|
937
|
-
|
|
935
|
+
end() {
|
|
938
936
|
this._busy--;
|
|
937
|
+
for (const resolve of this._waiters.splice(0))
|
|
938
|
+
resolve();
|
|
939
|
+
}
|
|
940
|
+
async wait() {
|
|
939
941
|
if (!this._busy)
|
|
940
|
-
|
|
942
|
+
return;
|
|
943
|
+
return new Promise(resolve => this._waiters.push(resolve));
|
|
941
944
|
}
|
|
942
|
-
|
|
943
|
-
|
|
945
|
+
}
|
|
946
|
+
class Waiter {
|
|
947
|
+
constructor() {
|
|
948
|
+
this._id = 0;
|
|
949
|
+
this._waiters = {};
|
|
944
950
|
}
|
|
945
|
-
|
|
946
|
-
|
|
951
|
+
isBusy(id) {
|
|
952
|
+
const waiter = this._waiters[id || 'ready'];
|
|
953
|
+
return !!waiter?._busy;
|
|
947
954
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
delete this._waiters[id];
|
|
955
|
+
startJob(id) {
|
|
956
|
+
let waiter = this._waiters[id || 'ready'];
|
|
957
|
+
if (!waiter)
|
|
958
|
+
waiter = this._waiters[id || 'ready'] = new WaiterJob();
|
|
959
|
+
waiter._busy++;
|
|
954
960
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
961
|
+
endJob(id) {
|
|
962
|
+
const waiter = this._waiters[id || 'ready'];
|
|
963
|
+
if (waiter)
|
|
964
|
+
waiter.end();
|
|
965
|
+
}
|
|
966
|
+
get nextId() {
|
|
967
|
+
return (++this._id).toString();
|
|
960
968
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
969
|
+
async wait(id) {
|
|
970
|
+
const waiter = this._waiters[id || 'ready'];
|
|
971
|
+
if (!waiter)
|
|
972
|
+
return;
|
|
973
|
+
return waiter.wait();
|
|
965
974
|
}
|
|
966
975
|
}
|
|
967
976
|
/** Router */
|
|
@@ -1361,7 +1370,7 @@ export class Router extends EventEmitter {
|
|
|
1361
1370
|
if (plugin.routes)
|
|
1362
1371
|
await this.use(isFunction(plugin.routes) ? await plugin.routes() : plugin.routes);
|
|
1363
1372
|
if (added)
|
|
1364
|
-
this.
|
|
1373
|
+
this.emit(added);
|
|
1365
1374
|
}
|
|
1366
1375
|
async waitPlugin(id) {
|
|
1367
1376
|
if (!this.plugins[id])
|
|
@@ -1384,12 +1393,9 @@ export class Router extends EventEmitter {
|
|
|
1384
1393
|
export class MicroServer extends EventEmitter {
|
|
1385
1394
|
constructor(config) {
|
|
1386
1395
|
super();
|
|
1387
|
-
this._waiter = new
|
|
1396
|
+
this._waiter = new Waiter();
|
|
1388
1397
|
this._methods = {};
|
|
1389
1398
|
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
1399
|
this.config = {
|
|
1394
1400
|
maxBodySize: defaultMaxBodySize,
|
|
1395
1401
|
methods: defaultMethods,
|
|
@@ -1406,17 +1412,20 @@ export class MicroServer extends EventEmitter {
|
|
|
1406
1412
|
if (config[key])
|
|
1407
1413
|
this.router.use(MicroServer.plugins[key], config[key]);
|
|
1408
1414
|
}
|
|
1409
|
-
if (config.listen)
|
|
1410
|
-
this.
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
+
if (config.listen) {
|
|
1416
|
+
this._waiter.startJob();
|
|
1417
|
+
this.listen({
|
|
1418
|
+
tls: config.tls,
|
|
1419
|
+
listen: config.listen || 8080
|
|
1420
|
+
}).then(() => {
|
|
1421
|
+
this._waiter.endJob();
|
|
1415
1422
|
});
|
|
1423
|
+
}
|
|
1424
|
+
this._waiter.wait().then(() => this.emit('ready'));
|
|
1416
1425
|
}
|
|
1417
1426
|
/** Add one time listener or call immediatelly for 'ready' */
|
|
1418
1427
|
once(name, cb) {
|
|
1419
|
-
if (name === 'ready' && this.isReady
|
|
1428
|
+
if (name === 'ready' && this.isReady)
|
|
1420
1429
|
cb();
|
|
1421
1430
|
else
|
|
1422
1431
|
super.once(name, cb);
|
|
@@ -1424,18 +1433,18 @@ export class MicroServer extends EventEmitter {
|
|
|
1424
1433
|
}
|
|
1425
1434
|
/** Add listener and call immediatelly for 'ready' */
|
|
1426
1435
|
on(name, cb) {
|
|
1427
|
-
if (name === 'ready' && this.isReady
|
|
1436
|
+
if (name === 'ready' && this.isReady)
|
|
1428
1437
|
cb();
|
|
1429
1438
|
super.on(name, cb);
|
|
1430
1439
|
return this;
|
|
1431
1440
|
}
|
|
1432
|
-
isReady() {
|
|
1433
|
-
return !this._waiter.
|
|
1441
|
+
get isReady() {
|
|
1442
|
+
return !this._waiter.isBusy();
|
|
1434
1443
|
}
|
|
1435
1444
|
async waitReady() {
|
|
1436
|
-
if (this.isReady
|
|
1445
|
+
if (this.isReady)
|
|
1437
1446
|
return;
|
|
1438
|
-
return this._waiter.wait(
|
|
1447
|
+
return this._waiter.wait();
|
|
1439
1448
|
}
|
|
1440
1449
|
async waitPlugin(id) {
|
|
1441
1450
|
await this.router.waitPlugin(id);
|
|
@@ -1467,7 +1476,7 @@ export class MicroServer extends EventEmitter {
|
|
|
1467
1476
|
}
|
|
1468
1477
|
const reg = /^((?<proto>\w+):\/\/)?(?<host>(\[[^\]]+\]|[a-z][^:,]+|\d+\.\d+\.\d+\.\d+))?:?(?<port>\d+)?/;
|
|
1469
1478
|
listen.split(',').forEach(listen => {
|
|
1470
|
-
this._waiter.startJob();
|
|
1479
|
+
this._waiter.startJob('listen');
|
|
1471
1480
|
let { proto, host, port } = reg.exec(listen)?.groups || {};
|
|
1472
1481
|
let srv;
|
|
1473
1482
|
switch (proto) {
|
|
@@ -1494,20 +1503,20 @@ export class MicroServer extends EventEmitter {
|
|
|
1494
1503
|
}
|
|
1495
1504
|
this.servers.add(srv);
|
|
1496
1505
|
if (port === '0') // skip listening
|
|
1497
|
-
this._waiter.endJob();
|
|
1506
|
+
this._waiter.endJob('listen');
|
|
1498
1507
|
else {
|
|
1499
1508
|
srv.listen(parseInt(port), host?.replace(/[\[\]]/g, '') || '0.0.0.0', () => {
|
|
1500
1509
|
const addr = srv.address();
|
|
1501
1510
|
this.emit('listen', addr.port, addr.address, srv);
|
|
1502
1511
|
srv._ready = true;
|
|
1503
|
-
this._waiter.endJob();
|
|
1512
|
+
this._waiter.endJob('listen');
|
|
1504
1513
|
});
|
|
1505
1514
|
}
|
|
1506
1515
|
srv.on('error', err => {
|
|
1507
1516
|
srv.close();
|
|
1508
1517
|
this.servers.delete(srv);
|
|
1509
1518
|
if (!srv._ready)
|
|
1510
|
-
this._waiter.endJob();
|
|
1519
|
+
this._waiter.endJob('listen');
|
|
1511
1520
|
this.emit('error', err);
|
|
1512
1521
|
});
|
|
1513
1522
|
srv.on('connection', s => {
|
|
@@ -1516,7 +1525,7 @@ export class MicroServer extends EventEmitter {
|
|
|
1516
1525
|
});
|
|
1517
1526
|
srv.on('upgrade', this.handlerUpgrade.bind(this));
|
|
1518
1527
|
});
|
|
1519
|
-
return this._waiter.wait('
|
|
1528
|
+
return this._waiter.wait('listen');
|
|
1520
1529
|
}
|
|
1521
1530
|
/** Add middleware, routes, etc.. see {router.use} */
|
|
1522
1531
|
use(...args) {
|
|
@@ -1663,7 +1672,6 @@ export class MicroServer extends EventEmitter {
|
|
|
1663
1672
|
this.sockets.clear();
|
|
1664
1673
|
}).then(() => {
|
|
1665
1674
|
this.emit('close');
|
|
1666
|
-
this._waiter.resolve("close");
|
|
1667
1675
|
});
|
|
1668
1676
|
}
|
|
1669
1677
|
/** Add route, alias to `server.router.use(url, ...args)` */
|
|
@@ -2006,31 +2014,12 @@ ProxyPlugin.validHeaders = {
|
|
|
2006
2014
|
MicroServer.plugins.proxy = ProxyPlugin;
|
|
2007
2015
|
/** Authentication class */
|
|
2008
2016
|
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
|
-
};
|
|
2017
|
+
constructor(options, req, res) {
|
|
2018
|
+
this.options = options;
|
|
2019
|
+
this.req = req;
|
|
2020
|
+
this.res = res;
|
|
2021
|
+
if (req)
|
|
2022
|
+
req.auth = this;
|
|
2034
2023
|
}
|
|
2035
2024
|
/** Decode token */
|
|
2036
2025
|
decode(data) {
|
|
@@ -2106,7 +2095,7 @@ export class Auth {
|
|
|
2106
2095
|
data = JSON.stringify(usr);
|
|
2107
2096
|
else if (typeof usr === 'string') {
|
|
2108
2097
|
if (psw !== undefined) {
|
|
2109
|
-
const userInfo = await this.users(usr, psw);
|
|
2098
|
+
const userInfo = await this.options.users(usr, psw);
|
|
2110
2099
|
data = userInfo?.id || userInfo?._id;
|
|
2111
2100
|
}
|
|
2112
2101
|
else
|
|
@@ -2123,7 +2112,7 @@ export class Auth {
|
|
|
2123
2112
|
if (typeof usr === 'object')
|
|
2124
2113
|
usrInfo = usr;
|
|
2125
2114
|
if (typeof usr === 'string')
|
|
2126
|
-
usrInfo = await this.users(usr, psw
|
|
2115
|
+
usrInfo = await this.options.users(usr, psw);
|
|
2127
2116
|
if (usrInfo?.id || usrInfo?._id) {
|
|
2128
2117
|
const expire = Math.min(34560000, options?.expire || this.options.expire || defaultExpire);
|
|
2129
2118
|
const expireTime = new Date().getTime() + expire * 1000;
|
|
@@ -2289,17 +2278,32 @@ class AuthPlugin extends Plugin {
|
|
|
2289
2278
|
...options,
|
|
2290
2279
|
cacheCleanup: new Date().getTime()
|
|
2291
2280
|
};
|
|
2292
|
-
if (
|
|
2281
|
+
if (options?.token === defaultToken)
|
|
2293
2282
|
console.warn('Default token in auth plugin');
|
|
2283
|
+
let token = options?.token || defaultToken;
|
|
2284
|
+
if (!token || token.length !== 32)
|
|
2285
|
+
token = defaultToken;
|
|
2286
|
+
if (token.length !== 32)
|
|
2287
|
+
token = crypto.createHash('sha256').update(token).digest();
|
|
2288
|
+
if (!(token instanceof Buffer))
|
|
2289
|
+
token = Buffer.from(token);
|
|
2290
|
+
this.options.token = token;
|
|
2291
|
+
if (typeof options?.users === 'function')
|
|
2292
|
+
this.options.users = (usr, psw) => options.users(usr, psw);
|
|
2293
|
+
else {
|
|
2294
|
+
this.options.users = async (usrid, psw) => {
|
|
2295
|
+
const users = this.options.users;
|
|
2296
|
+
const usr = users?.[usrid];
|
|
2297
|
+
if (usr && (psw === undefined || router.auth?.checkPassword(usrid, psw, usr.password || '')))
|
|
2298
|
+
return usr;
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2294
2301
|
router.auth = new Auth(this.options);
|
|
2295
2302
|
}
|
|
2296
2303
|
/** Authentication middleware */
|
|
2297
2304
|
async handler(req, res, next) {
|
|
2298
2305
|
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;
|
|
2306
|
+
const auth = new Auth(this.options, req, res);
|
|
2303
2307
|
const authorization = req.headers.authorization || '';
|
|
2304
2308
|
if (authorization.startsWith('Basic ')) {
|
|
2305
2309
|
let usr = cache?.[authorization];
|
|
@@ -2308,7 +2312,7 @@ class AuthPlugin extends Plugin {
|
|
|
2308
2312
|
else {
|
|
2309
2313
|
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
2314
|
if (username && psw)
|
|
2311
|
-
req.user = await auth.users(username, psw);
|
|
2315
|
+
req.user = await auth.options.users(username, psw);
|
|
2312
2316
|
if (!req.user)
|
|
2313
2317
|
return res.error(401);
|
|
2314
2318
|
if (cache) // 1 hour to expire in cache
|
|
@@ -2354,7 +2358,7 @@ class AuthPlugin extends Plugin {
|
|
|
2354
2358
|
catch (e) { }
|
|
2355
2359
|
}
|
|
2356
2360
|
else {
|
|
2357
|
-
req.user = await auth.users(usrData.data);
|
|
2361
|
+
req.user = await auth.options.users(usrData.data);
|
|
2358
2362
|
if (!req.user) {
|
|
2359
2363
|
req.auth.logout();
|
|
2360
2364
|
return new AccessDenied();
|
|
@@ -2383,7 +2387,7 @@ export class FileStore {
|
|
|
2383
2387
|
this._dir = options?.dir || 'data';
|
|
2384
2388
|
this._cacheTimeout = options?.cacheTimeout || 2000;
|
|
2385
2389
|
this._cacheItems = options?.cacheItems || 10;
|
|
2386
|
-
this._debounceTimeout = options?.debounceTimeout
|
|
2390
|
+
this._debounceTimeout = options?.debounceTimeout ?? 1000;
|
|
2387
2391
|
this._iter = 0;
|
|
2388
2392
|
}
|
|
2389
2393
|
/** cleanup cache */
|
|
@@ -2435,16 +2439,16 @@ export class FileStore {
|
|
|
2435
2439
|
if (item && new Date().getTime() - item.atime < this._cacheTimeout)
|
|
2436
2440
|
return item.data;
|
|
2437
2441
|
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') || '{}');
|
|
2442
|
+
const stat = await fs.promises.lstat(path.join(this._dir, name)).catch(() => null);
|
|
2443
|
+
if (!stat || item?.mtime !== stat.mtime.getTime()) {
|
|
2444
|
+
let data = stat ? JSON.parse(await fs.promises.readFile(path.join(this._dir, name), 'utf8').catch(() => '') || '{}') : {};
|
|
2441
2445
|
this._iter++;
|
|
2442
2446
|
this.cleanup();
|
|
2443
2447
|
if (autosave)
|
|
2444
2448
|
data = this.observe(data, () => this.save(name, data));
|
|
2445
2449
|
this._cache[name] = {
|
|
2446
2450
|
atime: new Date().getTime(),
|
|
2447
|
-
mtime: stat
|
|
2451
|
+
mtime: stat?.mtime.getTime() || new Date().getTime(),
|
|
2448
2452
|
data: data
|
|
2449
2453
|
};
|
|
2450
2454
|
return data;
|
|
@@ -2573,14 +2577,32 @@ function newObjectId() {
|
|
|
2573
2577
|
break;
|
|
2574
2578
|
return (new Date().getTime() / 1000 | 0).toString(16) + globalObjectId.toString('hex');
|
|
2575
2579
|
}
|
|
2580
|
+
class ModelCollectionsInternal {
|
|
2581
|
+
constructor() {
|
|
2582
|
+
this._wait = new Promise(resolve => this._ready = resolve);
|
|
2583
|
+
}
|
|
2584
|
+
set db(db) {
|
|
2585
|
+
Promise.resolve(db).then(db => this._ready(this._db = db));
|
|
2586
|
+
}
|
|
2587
|
+
get db() {
|
|
2588
|
+
return this._db;
|
|
2589
|
+
}
|
|
2590
|
+
async collection(name) {
|
|
2591
|
+
const db = await this._wait;
|
|
2592
|
+
return db.collection(name);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2576
2595
|
export class Model {
|
|
2577
|
-
static
|
|
2578
|
-
|
|
2596
|
+
static set db(db) {
|
|
2597
|
+
this.collections.db = db;
|
|
2598
|
+
}
|
|
2599
|
+
static get db() {
|
|
2600
|
+
return this.collections.db;
|
|
2579
2601
|
}
|
|
2580
2602
|
/** Define model */
|
|
2581
2603
|
static define(name, schema, options) {
|
|
2582
2604
|
options = options || {};
|
|
2583
|
-
if (!options.collection
|
|
2605
|
+
if (!options.collection)
|
|
2584
2606
|
options.collection = this.collections.collection(name);
|
|
2585
2607
|
const inst = options?.class
|
|
2586
2608
|
? new options.class(schema, { name, ...options })
|
|
@@ -2916,6 +2938,9 @@ export class Model {
|
|
|
2916
2938
|
}
|
|
2917
2939
|
}
|
|
2918
2940
|
const res = await this.collection.findAndModify({ query: this.getFilter(query, { required: true, validate: false, default: false }), update: query, upsert: options?.insert });
|
|
2941
|
+
if (!res)
|
|
2942
|
+
throw new NotFound('Document not found');
|
|
2943
|
+
return res;
|
|
2919
2944
|
}
|
|
2920
2945
|
/** Delete one matching document */
|
|
2921
2946
|
async delete(query, options) {
|
|
@@ -2960,13 +2985,16 @@ export class Model {
|
|
|
2960
2985
|
}
|
|
2961
2986
|
}
|
|
2962
2987
|
}
|
|
2988
|
+
Model.collections = new ModelCollectionsInternal();
|
|
2963
2989
|
Model.models = {};
|
|
2964
2990
|
/** Collection factory */
|
|
2965
2991
|
export class MicroCollectionStore {
|
|
2966
|
-
constructor(dataPath) {
|
|
2992
|
+
constructor(dataPath, storeTimeDelay) {
|
|
2967
2993
|
this._collections = new Map();
|
|
2968
2994
|
if (dataPath)
|
|
2969
|
-
this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, '') });
|
|
2995
|
+
this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, ''), debounceTimeout: storeTimeDelay ?? 1000 });
|
|
2996
|
+
if (!Model.db)
|
|
2997
|
+
Model.db = this;
|
|
2970
2998
|
}
|
|
2971
2999
|
/** Get collection */
|
|
2972
3000
|
async collection(name) {
|
|
@@ -3035,54 +3063,20 @@ export class MicroCollection {
|
|
|
3035
3063
|
}
|
|
3036
3064
|
/** Find and modify one matching document */
|
|
3037
3065
|
async findAndModify(options) {
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
Object.assign(doc, options.update);
|
|
3047
|
-
if (this._save) {
|
|
3048
|
-
promise = promise.then(async () => {
|
|
3049
|
-
if (!doc._id)
|
|
3050
|
-
throw new Error('Internal error: missing _id in document');
|
|
3051
|
-
this.data[doc._id] = await this._save?.(doc._id, doc, this) || this.data[doc._id];
|
|
3052
|
-
});
|
|
3053
|
-
}
|
|
3054
|
-
count++;
|
|
3055
|
-
if (options.limit && count >= options.limit)
|
|
3056
|
-
return false;
|
|
3057
|
-
}
|
|
3058
|
-
});
|
|
3059
|
-
await promise;
|
|
3060
|
-
return count;
|
|
3061
|
-
}
|
|
3062
|
-
let doc = this.queryDocument(options.query, this.data[id]);
|
|
3063
|
-
if (!doc) {
|
|
3064
|
-
if (!options.upsert && !options.new)
|
|
3065
|
-
throw new InvalidData(`Document not found`);
|
|
3066
|
-
doc = { _id: id };
|
|
3067
|
-
this.data[id] = doc;
|
|
3068
|
-
}
|
|
3069
|
-
else {
|
|
3070
|
-
if (options.new)
|
|
3071
|
-
throw new InvalidData(`Document dupplicate`);
|
|
3072
|
-
}
|
|
3073
|
-
if (options.update) {
|
|
3074
|
-
for (const n in options.update) {
|
|
3066
|
+
const res = await this.findOne(options.query || {});
|
|
3067
|
+
if (res?._id) {
|
|
3068
|
+
await this.updateOne({ _id: res._id }, options.update || {}, options);
|
|
3069
|
+
return this.data[res._id];
|
|
3070
|
+
}
|
|
3071
|
+
if (options.upsert) {
|
|
3072
|
+
const doc = { ...options.query };
|
|
3073
|
+
for (const n in options.update)
|
|
3075
3074
|
if (!n.startsWith('$'))
|
|
3076
3075
|
doc[n] = options.update[n];
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
delete doc[n];
|
|
3081
|
-
}
|
|
3076
|
+
if (options.update?.$set)
|
|
3077
|
+
Object.assign(doc, options.update.$set);
|
|
3078
|
+
return this.insertOne(doc);
|
|
3082
3079
|
}
|
|
3083
|
-
if (this._save)
|
|
3084
|
-
this.data[id] = await this._save(id, doc, this) || doc;
|
|
3085
|
-
return 1;
|
|
3086
3080
|
}
|
|
3087
3081
|
/** Insert one document */
|
|
3088
3082
|
async insertOne(doc) {
|
|
@@ -3108,49 +3102,72 @@ export class MicroCollection {
|
|
|
3108
3102
|
}
|
|
3109
3103
|
/** Delete one matching document */
|
|
3110
3104
|
async deleteOne(query) {
|
|
3111
|
-
|
|
3112
|
-
if (!id)
|
|
3113
|
-
return;
|
|
3114
|
-
delete this.data[id];
|
|
3105
|
+
return (await this.updateMany(query, {}, { delete: true, limit: 1 })).modifiedCount;
|
|
3115
3106
|
}
|
|
3116
3107
|
/** Delete all matching documents */
|
|
3117
3108
|
async deleteMany(query) {
|
|
3118
|
-
|
|
3109
|
+
return (await this.updateMany(query, {}, { delete: true })).modifiedCount;
|
|
3110
|
+
}
|
|
3111
|
+
async updateOne(query, update, options) {
|
|
3112
|
+
const res = await this.updateMany(query, update, { ...options, limit: 1 });
|
|
3113
|
+
return res;
|
|
3114
|
+
}
|
|
3115
|
+
async updateMany(query, update, options) {
|
|
3116
|
+
let res = { upsertedId: undefined, modifiedCount: 0 };
|
|
3117
|
+
if (!query)
|
|
3118
|
+
return res;
|
|
3119
3119
|
let promise = Promise.resolve();
|
|
3120
3120
|
this.find(query).forEach((doc) => {
|
|
3121
3121
|
if (this.queryDocument(query, doc)) {
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3122
|
+
if (options?.delete) {
|
|
3123
|
+
if (!doc._id)
|
|
3124
|
+
return;
|
|
3125
|
+
res.modifiedCount++;
|
|
3126
|
+
if (this._save)
|
|
3127
|
+
promise = promise.then(async () => { doc._id && this._save?.(doc._id, undefined, this); });
|
|
3128
|
+
delete this.data[doc._id];
|
|
3129
|
+
}
|
|
3130
|
+
else {
|
|
3131
|
+
Object.assign(doc, update);
|
|
3132
|
+
res.modifiedCount++;
|
|
3133
|
+
if (this._save) {
|
|
3134
|
+
promise = promise.then(async () => {
|
|
3135
|
+
if (!doc._id)
|
|
3136
|
+
throw new Error('Internal error: missing _id in document');
|
|
3137
|
+
this.data[doc._id] = await this._save?.(doc._id, doc, this) || this.data[doc._id];
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
if (options?.limit && res.modifiedCount >= options?.limit)
|
|
3142
|
+
return false;
|
|
3128
3143
|
}
|
|
3129
3144
|
});
|
|
3130
3145
|
await promise;
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
update
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3146
|
+
if (res.modifiedCount || !options?.upsert || options?.delete)
|
|
3147
|
+
return res;
|
|
3148
|
+
if (!query._id)
|
|
3149
|
+
query._id = res.upsertedId = newObjectId();
|
|
3150
|
+
let doc = this.queryDocument(options.query, this.data[query._id]);
|
|
3151
|
+
if (doc)
|
|
3152
|
+
throw new InvalidData(`Document dupplicate`);
|
|
3153
|
+
doc = { _id: query._id, ...query };
|
|
3154
|
+
this.data[query._id] = doc;
|
|
3155
|
+
if (update) {
|
|
3156
|
+
for (const n in update) {
|
|
3157
|
+
if (!n.startsWith('$'))
|
|
3158
|
+
doc[n] = update[n];
|
|
3159
|
+
}
|
|
3160
|
+
if (update.$set) {
|
|
3161
|
+
for (const n in update.$set)
|
|
3162
|
+
doc[n] = update.$set[n];
|
|
3163
|
+
}
|
|
3164
|
+
if (update.$unset) {
|
|
3165
|
+
for (const n in update.$unset)
|
|
3166
|
+
delete doc[n];
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
if (this._save)
|
|
3170
|
+
this.data[query._id] = await this._save(query._id, doc, this) || doc;
|
|
3171
|
+
return res;
|
|
3155
3172
|
}
|
|
3156
3173
|
}
|