@adaptivestone/framework 4.6.0 → 4.8.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/CHANGELOG.md +14 -1
- package/Cli.js +1 -0
- package/cluster.js +1 -2
- package/commands/GetOpenApiJson.js +1 -2
- package/commands/SyncIndexes.js +1 -1
- package/commands/migration/Migrate.js +2 -2
- package/controllers/Auth.js +3 -5
- package/controllers/index.js +0 -3
- package/coverage/clover.xml +2035 -1926
- package/coverage/coverage-final.json +43 -40
- package/coverage/framework/config/auth.js.html +6 -6
- package/coverage/framework/config/http.js.html +10 -10
- package/coverage/framework/config/i18n.js.html +13 -13
- package/coverage/framework/config/index.html +1 -1
- package/coverage/framework/config/log.js.html +23 -23
- package/coverage/framework/config/mail.js.html +30 -30
- package/coverage/framework/config/mongo.js.html +4 -4
- package/coverage/framework/config/rateLimiter.js.html +17 -17
- package/coverage/framework/config/redis.js.html +5 -5
- package/coverage/framework/config/validate.js.html +4 -4
- package/coverage/framework/controllers/Auth.js.html +97 -103
- package/coverage/framework/controllers/Home.js.html +27 -27
- package/coverage/framework/controllers/index.html +15 -15
- package/coverage/framework/controllers/index.js.html +58 -67
- package/coverage/framework/controllers/test/SomeController.js.html +44 -89
- package/coverage/framework/controllers/test/index.html +17 -17
- package/coverage/framework/helpers/files.js.html +310 -0
- package/coverage/framework/helpers/index.html +131 -0
- package/coverage/framework/helpers/logger.js.html +142 -0
- package/coverage/framework/helpers/redis/clearNamespace.js.html +10 -10
- package/coverage/framework/helpers/redis/index.html +1 -1
- package/coverage/framework/index.html +19 -19
- package/coverage/framework/models/Migration.js.html +130 -0
- package/coverage/framework/models/Sequence.js.html +11 -11
- package/coverage/framework/models/User.js.html +130 -130
- package/coverage/framework/models/index.html +20 -5
- package/coverage/framework/modules/AbstractController.js.html +100 -106
- package/coverage/framework/modules/AbstractModel.js.html +58 -58
- package/coverage/framework/modules/Base.js.html +70 -403
- package/coverage/framework/modules/index.html +24 -24
- package/coverage/framework/server.js.html +704 -338
- package/coverage/framework/services/cache/Cache.js.html +96 -81
- package/coverage/framework/services/cache/index.html +19 -19
- package/coverage/framework/services/documentation/DocumentationGenerator.js.html +39 -45
- package/coverage/framework/services/documentation/index.html +11 -11
- package/coverage/framework/services/http/HttpServer.js.html +90 -90
- package/coverage/framework/services/http/index.html +1 -1
- package/coverage/framework/services/http/middleware/AbstractMiddleware.js.html +45 -45
- package/coverage/framework/services/http/middleware/Auth.js.html +19 -19
- package/coverage/framework/services/http/middleware/GetUserByToken.js.html +36 -36
- package/coverage/framework/services/http/middleware/I18n.js.html +67 -67
- package/coverage/framework/services/http/middleware/Pagination.js.html +21 -21
- package/coverage/framework/services/http/middleware/PrepareAppInfo.js.html +5 -5
- package/coverage/framework/services/http/middleware/RateLimiter.js.html +19 -19
- package/coverage/framework/services/http/middleware/RequestLogger.js.html +21 -21
- package/coverage/framework/services/http/middleware/RequestParser.js.html +7 -7
- package/coverage/framework/services/http/middleware/Role.js.html +43 -43
- package/coverage/framework/services/http/middleware/index.html +33 -33
- package/coverage/framework/services/http/middleware/test/CheckFlag.js.html +9 -9
- package/coverage/framework/services/http/middleware/test/index.html +1 -1
- package/coverage/framework/services/messaging/email/index.html +1 -1
- package/coverage/framework/services/messaging/email/index.js.html +67 -67
- package/coverage/framework/services/messaging/index.html +1 -1
- package/coverage/framework/services/messaging/index.js.html +6 -6
- package/coverage/framework/services/validate/ValidateService.js.html +155 -167
- package/coverage/framework/services/validate/drivers/AbstractValidator.js.html +24 -24
- package/coverage/framework/services/validate/drivers/CustomValidator.js.html +14 -14
- package/coverage/framework/services/validate/drivers/YupValidator.js.html +41 -41
- package/coverage/framework/services/validate/drivers/index.html +1 -1
- package/coverage/framework/services/validate/index.html +5 -5
- package/coverage/index.html +91 -76
- package/helpers/files.js +75 -0
- package/helpers/logger.js +19 -0
- package/models/Migration.test.js +19 -0
- package/modules/AbstractController.js +2 -4
- package/modules/Base.js +8 -119
- package/modules/BaseCli.js +0 -4
- package/package.json +2 -2
- package/server.d.ts +14 -1
- package/server.js +208 -86
- package/services/cache/Cache.js +10 -5
- package/services/cache/Cache.test.js +81 -0
- package/services/documentation/DocumentationGenerator.js +18 -20
- package/services/http/middleware/Auth.test.js +57 -0
- package/services/http/middleware/I18n.test.js +15 -3
- package/services/http/middleware/PrepareAppInfo.test.js +1 -1
- package/services/http/middleware/Role.test.js +93 -0
- package/services/validate/ValidateService.js +3 -7
- package/tests/setup.js +1 -0
- package/tests/setupVitest.js +1 -0
package/modules/Base.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { join, normalize } = require('node:path');
|
|
1
|
+
const { getFilesPathWithInheritance } = require('../helpers/files');
|
|
3
2
|
|
|
4
3
|
class Base {
|
|
5
4
|
#realLogger = null;
|
|
@@ -29,128 +28,18 @@ class Base {
|
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
30
|
* Get winston loger for given label
|
|
32
|
-
* @param {
|
|
31
|
+
* @param {string} label name of logger
|
|
33
32
|
*/
|
|
34
33
|
getLogger(label) {
|
|
35
|
-
|
|
36
|
-
const winston = require('winston'); // speed up optimisation
|
|
37
|
-
const alignColorsAndTime = winston.format.combine(
|
|
38
|
-
winston.format.colorize({
|
|
39
|
-
all: true,
|
|
40
|
-
}),
|
|
41
|
-
winston.format.label({
|
|
42
|
-
label: ` \x1B[32m[${label}]\x1B[39m`,
|
|
43
|
-
}),
|
|
44
|
-
winston.format.timestamp(),
|
|
45
|
-
winston.format.printf(
|
|
46
|
-
(info) =>
|
|
47
|
-
`(${process.pid}) ${info.label} ${info.timestamp} ${info.level} : ${info.message}`,
|
|
48
|
-
),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const logConfig = this.app.getConfig('log').transports;
|
|
52
|
-
|
|
53
|
-
function IsConstructor(f) {
|
|
54
|
-
try {
|
|
55
|
-
Reflect.construct(String, [], f);
|
|
56
|
-
} catch (e) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
const transports = [];
|
|
62
|
-
for (const log of logConfig) {
|
|
63
|
-
if (log.enable) {
|
|
64
|
-
if (log.transport === 'console') {
|
|
65
|
-
transports.push(
|
|
66
|
-
new winston.transports.Console({
|
|
67
|
-
level: log.transportOptions.level,
|
|
68
|
-
format: winston.format.combine(
|
|
69
|
-
winston.format.colorize(),
|
|
70
|
-
alignColorsAndTime,
|
|
71
|
-
),
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
} else {
|
|
75
|
-
// eslint-disable-next-line global-require, import/no-dynamic-require
|
|
76
|
-
let Tr = require(log.transport);
|
|
77
|
-
if (!IsConstructor(Tr) && Tr.default) {
|
|
78
|
-
Tr = Tr.default;
|
|
79
|
-
} else {
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.error(
|
|
82
|
-
`${log.transport} not a constructor. Please check it`,
|
|
83
|
-
);
|
|
84
|
-
// eslint-disable-next-line no-continue
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
transports.push(new Tr(log.transportOptions));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return winston.createLogger({
|
|
94
|
-
level: 'silly',
|
|
95
|
-
transports,
|
|
96
|
-
});
|
|
34
|
+
return this.app.logger.child({ label });
|
|
97
35
|
}
|
|
98
36
|
|
|
99
37
|
async getFilesPathWithInheritance(internalFolder, externalFolder) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if ((await fs.stat(f)).isDirectory()) {
|
|
106
|
-
allFiles.pop();
|
|
107
|
-
return rreaddir(f, allFiles);
|
|
108
|
-
}
|
|
109
|
-
return null;
|
|
110
|
-
}),
|
|
111
|
-
);
|
|
112
|
-
return allFiles.map((file) => file.replace(`${normalize(dir)}/`, ''));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
let [internalFiles, externalFiles] = await Promise.all([
|
|
116
|
-
rreaddir(internalFolder),
|
|
117
|
-
rreaddir(externalFolder),
|
|
118
|
-
]);
|
|
119
|
-
|
|
120
|
-
const filterIndexFile = (fileName) => {
|
|
121
|
-
const fileArray = fileName.split('/');
|
|
122
|
-
const file = fileArray[fileArray.length - 1];
|
|
123
|
-
return (
|
|
124
|
-
file[0] === file[0].toUpperCase() && // Start with capital
|
|
125
|
-
file[0] !== '.' && // not start with dot
|
|
126
|
-
!file.includes('.test.js') // not test files
|
|
127
|
-
);
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
internalFiles = internalFiles.filter(filterIndexFile);
|
|
131
|
-
externalFiles = externalFiles.filter(filterIndexFile);
|
|
132
|
-
|
|
133
|
-
const filesToLoad = [];
|
|
134
|
-
for (const file of internalFiles) {
|
|
135
|
-
if (externalFiles.includes(file)) {
|
|
136
|
-
this.logger.verbose(
|
|
137
|
-
`Skipping register INTERNAL file ${file} as it override by EXTERNAL ONE`,
|
|
138
|
-
);
|
|
139
|
-
} else {
|
|
140
|
-
filesToLoad.push({
|
|
141
|
-
path: `${internalFolder}/${file}`,
|
|
142
|
-
file,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
for (const file of externalFiles) {
|
|
148
|
-
filesToLoad.push({
|
|
149
|
-
path: `${externalFolder}/${file}`,
|
|
150
|
-
file,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
return filesToLoad;
|
|
38
|
+
return getFilesPathWithInheritance({
|
|
39
|
+
internalFolder,
|
|
40
|
+
externalFolder,
|
|
41
|
+
logger: (text) => this.logger.verbose(text),
|
|
42
|
+
});
|
|
154
43
|
}
|
|
155
44
|
|
|
156
45
|
/**
|
package/modules/BaseCli.js
CHANGED
|
@@ -47,12 +47,8 @@ class Cli extends Base {
|
|
|
47
47
|
);
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
|
-
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
51
50
|
const { default: Command } = await import(this.commands[command]);
|
|
52
51
|
|
|
53
|
-
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
54
|
-
// const Command = require(this.commands[command]);
|
|
55
|
-
|
|
56
52
|
const c = new Command(this.app, this.commands, args);
|
|
57
53
|
let result = false;
|
|
58
54
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=18.
|
|
7
|
+
"node": ">=18.17.0"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
package/server.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Model as MongooseModel, Schema } from 'mongoose';
|
|
|
7
7
|
|
|
8
8
|
import BaseCli from './modules/BaseCli';
|
|
9
9
|
import Cache from './services/cache/Cache';
|
|
10
|
+
import winston from 'winston';
|
|
10
11
|
|
|
11
12
|
type ServerConfig = {
|
|
12
13
|
folders: ExpandDeep<TFolderConfig>;
|
|
@@ -22,6 +23,7 @@ declare class Server {
|
|
|
22
23
|
foldersConfig: Server['config']['folders'];
|
|
23
24
|
events: EventEmitter;
|
|
24
25
|
get cache(): Server['cacheService'];
|
|
26
|
+
get logger(): winston.Logger;
|
|
25
27
|
httpServer: null;
|
|
26
28
|
controllerManager: null;
|
|
27
29
|
};
|
|
@@ -39,10 +41,16 @@ declare class Server {
|
|
|
39
41
|
constructor(config: ExpandDeep<ServerConfig>);
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
|
-
* Start server (http
|
|
44
|
+
* Start server (http + init all http ralated functions)
|
|
43
45
|
*/
|
|
44
46
|
startServer(callbackBefore404?: Promise<null>): Promise<null>;
|
|
45
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Do an initialization (config reading, etc)
|
|
50
|
+
* @returns {Promise}
|
|
51
|
+
*/
|
|
52
|
+
init(): Promise<boolean>;
|
|
53
|
+
|
|
46
54
|
/**
|
|
47
55
|
* Return config from {configName} (file name) on config folder.
|
|
48
56
|
* Support cache and updating confing into cache
|
|
@@ -54,6 +62,11 @@ declare class Server {
|
|
|
54
62
|
*/
|
|
55
63
|
getConfig(configName: string): {};
|
|
56
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Return or create new logger instance. This is a main logger instance
|
|
67
|
+
*/
|
|
68
|
+
getLogger(): winston.Logger;
|
|
69
|
+
|
|
57
70
|
/**
|
|
58
71
|
* Primary designed for tests when we need to update some configs before start testing
|
|
59
72
|
* Should be called before any initialization was done
|
package/server.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
const EventEmitter = require('node:events');
|
|
3
|
+
const { hrtime } = require('node:process');
|
|
3
4
|
|
|
4
5
|
require('dotenv').config();
|
|
5
6
|
const merge = require('deepmerge');
|
|
7
|
+
const winston = require('winston');
|
|
8
|
+
const { getFilesPathWithInheritance } = require('./helpers/files');
|
|
9
|
+
const { consoleLogger } = require('./helpers/logger');
|
|
10
|
+
|
|
11
|
+
const Cache = require('./services/cache/Cache');
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* Main framework class.
|
|
9
15
|
*/
|
|
10
16
|
class Server {
|
|
17
|
+
#realLogger = null;
|
|
18
|
+
|
|
19
|
+
#isInited = false;
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* Construct new server
|
|
13
23
|
* @param {Object} config main config object
|
|
@@ -33,6 +43,9 @@ class Server {
|
|
|
33
43
|
get cache() {
|
|
34
44
|
return that.getCache();
|
|
35
45
|
},
|
|
46
|
+
get logger() {
|
|
47
|
+
return that.getLogger();
|
|
48
|
+
},
|
|
36
49
|
httpServer: null,
|
|
37
50
|
controllerManager: null,
|
|
38
51
|
};
|
|
@@ -40,28 +53,23 @@ class Server {
|
|
|
40
53
|
this.cache = {
|
|
41
54
|
configs: new Map(),
|
|
42
55
|
models: new Map(),
|
|
56
|
+
modelConstructors: new Map(),
|
|
43
57
|
};
|
|
44
58
|
|
|
45
59
|
this.cli = false;
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
/**
|
|
49
|
-
* Start server (http
|
|
63
|
+
* Start server (http + init all http ralated functions)
|
|
50
64
|
* @param <Promise>callbackBefore404 code that should be executed before adding page 404
|
|
51
65
|
* @returns {Promise}
|
|
52
66
|
*/
|
|
53
67
|
async startServer(callbackBefore404 = async () => Promise.resolve()) {
|
|
54
|
-
// eslint-disable-next-line global-require
|
|
55
|
-
// const HttpServer = require('./services/http/HttpServer');
|
|
56
|
-
// eslint-disable-next-line global-require
|
|
57
|
-
// const ControllerManager = require('./controllers/index');
|
|
58
|
-
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
59
68
|
const [{ default: HttpServer }, { default: ControllerManager }] =
|
|
60
69
|
await Promise.all([
|
|
61
|
-
// eslint-disable-next-line import/extensions
|
|
62
70
|
import('./services/http/HttpServer.js'), // Speed optimisation
|
|
63
|
-
// eslint-disable-next-line import/extensions
|
|
64
71
|
import('./controllers/index.js'), // Speed optimisation
|
|
72
|
+
this.init(),
|
|
65
73
|
]);
|
|
66
74
|
|
|
67
75
|
this.addErrorHandling();
|
|
@@ -76,20 +84,115 @@ class Server {
|
|
|
76
84
|
this.app.httpServer.add404Page();
|
|
77
85
|
}
|
|
78
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Do an initialization (config reading, etc)
|
|
89
|
+
* @returns {Promise}
|
|
90
|
+
*/
|
|
91
|
+
async init() {
|
|
92
|
+
if (this.#isInited) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.time('Server init. Done');
|
|
97
|
+
await Promise.all([this.#initConfigFiles(), this.#loadModelFiles()]);
|
|
98
|
+
|
|
99
|
+
this.#isInited = true;
|
|
100
|
+
|
|
101
|
+
console.timeEnd('Server init. Done');
|
|
102
|
+
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async #initConfigFiles() {
|
|
107
|
+
const files = await getFilesPathWithInheritance({
|
|
108
|
+
internalFolder: `${__dirname}/config`,
|
|
109
|
+
externalFolder: this.app.foldersConfig.config,
|
|
110
|
+
loggerFileType: 'CONFIG',
|
|
111
|
+
logger: (m) => consoleLogger('info', m),
|
|
112
|
+
filter: {
|
|
113
|
+
startWithCapital: false,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const configFiles = {};
|
|
118
|
+
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
const config = file.file.split('.');
|
|
121
|
+
if (!configFiles[config[0]]) {
|
|
122
|
+
configFiles[config[0]] = {};
|
|
123
|
+
}
|
|
124
|
+
if (config.length === 2) {
|
|
125
|
+
configFiles[config[0]].default = file.path;
|
|
126
|
+
} else {
|
|
127
|
+
configFiles[config[0]][config[1]] = file.path;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const loadConfig = async (configName, values) => {
|
|
132
|
+
const promises = [import(values.default)];
|
|
133
|
+
if (process.env.NODE_ENV && values[process.env.NODE_ENV]) {
|
|
134
|
+
promises.push(import(values[process.env.NODE_ENV]));
|
|
135
|
+
}
|
|
136
|
+
const result = await Promise.all(promises);
|
|
137
|
+
return {
|
|
138
|
+
name: configName,
|
|
139
|
+
finalValue: merge(result[0].default, result[1]?.default || {}, {
|
|
140
|
+
arrayMerge: (destinationArray, sourceArray) => sourceArray,
|
|
141
|
+
}),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const loadingPromises = [];
|
|
146
|
+
|
|
147
|
+
for (const [configFile, value] of Object.entries(configFiles)) {
|
|
148
|
+
loadingPromises.push(loadConfig(configFile, value));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const configs = await Promise.all(loadingPromises);
|
|
152
|
+
|
|
153
|
+
for (const config of configs) {
|
|
154
|
+
this.cache.configs.set(config.name, config.finalValue);
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async #loadModelFiles() {
|
|
160
|
+
const files = await getFilesPathWithInheritance({
|
|
161
|
+
internalFolder: `${__dirname}/models`,
|
|
162
|
+
externalFolder: this.app.foldersConfig.models,
|
|
163
|
+
loggerFileType: 'MODEL',
|
|
164
|
+
logger: (m) => consoleLogger('info', m),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const promises = [];
|
|
168
|
+
for (const file of files) {
|
|
169
|
+
const t = hrtime.bigint();
|
|
170
|
+
promises.push(
|
|
171
|
+
import(file.path).then((f) => ({
|
|
172
|
+
name: file.file.split('.')[0],
|
|
173
|
+
file: f,
|
|
174
|
+
took: hrtime.bigint() - t,
|
|
175
|
+
})),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const loadedModels = await Promise.all(promises);
|
|
180
|
+
|
|
181
|
+
for (const model of loadedModels) {
|
|
182
|
+
this.cache.modelConstructors.set(model.name, model.file.default);
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
79
187
|
/**
|
|
80
188
|
* Add error logging on promise reject
|
|
81
189
|
*/
|
|
82
|
-
// eslint-disable-next-line class-methods-use-this
|
|
83
190
|
addErrorHandling() {
|
|
84
|
-
process.on('uncaughtException',
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
' reason: ',
|
|
90
|
-
reason,
|
|
91
|
-
);
|
|
92
|
-
console.trace('unhandledRejection');
|
|
191
|
+
process.on('uncaughtException', (e) =>
|
|
192
|
+
this.app.logger.error('uncaughtException', e),
|
|
193
|
+
);
|
|
194
|
+
process.on('unhandledRejection', (e) => {
|
|
195
|
+
this.app.logger.error('unhandledRejection', e);
|
|
93
196
|
});
|
|
94
197
|
}
|
|
95
198
|
|
|
@@ -104,28 +207,93 @@ class Server {
|
|
|
104
207
|
* @returns {Object} config object. Structure depends of config file
|
|
105
208
|
*/
|
|
106
209
|
getConfig(configName) {
|
|
107
|
-
|
|
210
|
+
if (!this.#isInited) {
|
|
211
|
+
throw new Error('You should call Server.init() before using it');
|
|
212
|
+
}
|
|
213
|
+
|
|
108
214
|
if (!this.cache.configs.has(configName)) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
envConfig =
|
|
112
|
-
this.getFileWithExtendingInhirence(
|
|
113
|
-
'config',
|
|
114
|
-
`${configName}.${process.env.NODE_ENV}.js`,
|
|
115
|
-
) || envConfig;
|
|
116
|
-
}
|
|
117
|
-
this.cache.configs.set(
|
|
118
|
-
configName,
|
|
119
|
-
merge(
|
|
120
|
-
this.getFileWithExtendingInhirence('config', configName),
|
|
121
|
-
envConfig,
|
|
122
|
-
{ arrayMerge: (destinationArray, sourceArray) => sourceArray },
|
|
123
|
-
),
|
|
215
|
+
this.logger.warn(
|
|
216
|
+
`You asked for config ${configName} that not exists. Please check you codebase `,
|
|
124
217
|
);
|
|
218
|
+
return {};
|
|
125
219
|
}
|
|
126
220
|
return this.cache.configs.get(configName);
|
|
127
221
|
}
|
|
128
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Return or create new logger instance. This is a main logger instance
|
|
225
|
+
*/
|
|
226
|
+
getLogger() {
|
|
227
|
+
if (!this.#realLogger) {
|
|
228
|
+
this.#realLogger = this.#createLogger();
|
|
229
|
+
}
|
|
230
|
+
return this.#realLogger;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#createLogger() {
|
|
234
|
+
const alignColorsAndTime = winston.format.combine(
|
|
235
|
+
winston.format.colorize({
|
|
236
|
+
all: true,
|
|
237
|
+
}),
|
|
238
|
+
winston.format.timestamp(),
|
|
239
|
+
winston.format.printf(
|
|
240
|
+
(info) =>
|
|
241
|
+
`(${process.pid}) \x1B[32m[${info.label ?? 'SERVER'}]\x1B[39m ${
|
|
242
|
+
info.timestamp
|
|
243
|
+
} ${info.level} : ${info.message} ${info?.stack ?? ''} ${
|
|
244
|
+
info.durationMs ? `Duration: ${info.durationMs}ms` : ''
|
|
245
|
+
}`,
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
const logConfig = this.app.getConfig('log').transports;
|
|
249
|
+
function IsConstructor(f) {
|
|
250
|
+
try {
|
|
251
|
+
Reflect.construct(String, [], f);
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const logger = winston.createLogger({
|
|
259
|
+
format: winston.format.errors({ stack: true }),
|
|
260
|
+
level: 'silly',
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
for (const log of logConfig) {
|
|
264
|
+
if (log.enable) {
|
|
265
|
+
if (log.transport === 'console') {
|
|
266
|
+
logger.add(
|
|
267
|
+
new winston.transports.Console({
|
|
268
|
+
level: log.transportOptions.level,
|
|
269
|
+
format: winston.format.combine(
|
|
270
|
+
winston.format.colorize(),
|
|
271
|
+
alignColorsAndTime,
|
|
272
|
+
),
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
import(log.transport).then((Tr) => {
|
|
277
|
+
let Transport = Tr.default;
|
|
278
|
+
if (!IsConstructor(Transport) && Transport.default) {
|
|
279
|
+
Transport = Transport.default;
|
|
280
|
+
} else {
|
|
281
|
+
console.error(
|
|
282
|
+
`${log.transport} not a constructor. Please check it`,
|
|
283
|
+
);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
logger.profile(`Adding new logger ${log.transport}`);
|
|
287
|
+
logger.add(new Transport(log.transportOptions));
|
|
288
|
+
logger.profile(`Adding new logger ${log.transport}`);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return logger;
|
|
295
|
+
}
|
|
296
|
+
|
|
129
297
|
/**
|
|
130
298
|
* Primary designed for tests when we need to update some configs before start testing
|
|
131
299
|
* Should be called before any initialization was done
|
|
@@ -149,14 +317,14 @@ class Server {
|
|
|
149
317
|
*/
|
|
150
318
|
getModel(modelName) {
|
|
151
319
|
if (modelName.endsWith('s')) {
|
|
152
|
-
|
|
320
|
+
this.app.logger.warn(
|
|
153
321
|
`Probably your model name '${modelName}' in plural from. Try to avoid plural form`,
|
|
154
322
|
);
|
|
155
323
|
}
|
|
156
324
|
if (!this.cache.models.has(modelName)) {
|
|
157
|
-
const Model = this.
|
|
325
|
+
const Model = this.cache.modelConstructors.get(modelName);
|
|
158
326
|
if (!Model) {
|
|
159
|
-
|
|
327
|
+
this.app.logger.error(`Model not found: ${modelName}`);
|
|
160
328
|
return false;
|
|
161
329
|
}
|
|
162
330
|
try {
|
|
@@ -164,8 +332,8 @@ class Server {
|
|
|
164
332
|
|
|
165
333
|
this.cache.models.set(modelName, model.mongooseModel);
|
|
166
334
|
} catch (e) {
|
|
167
|
-
|
|
168
|
-
|
|
335
|
+
this.app.logger.error(`Problem with model ${modelName}, ${e.message}`);
|
|
336
|
+
this.app.logger.error(e);
|
|
169
337
|
}
|
|
170
338
|
}
|
|
171
339
|
return this.cache.models.get(modelName);
|
|
@@ -178,11 +346,7 @@ class Server {
|
|
|
178
346
|
*/
|
|
179
347
|
async runCliCommand(commandName, args) {
|
|
180
348
|
if (!this.cli) {
|
|
181
|
-
|
|
182
|
-
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
183
|
-
// const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
|
|
184
|
-
// eslint-disable-next-line global-require
|
|
185
|
-
const BaseCli = require('./modules/BaseCli');
|
|
349
|
+
const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
|
|
186
350
|
this.cli = new BaseCli(this);
|
|
187
351
|
}
|
|
188
352
|
return this.cli.run(commandName, args);
|
|
@@ -194,52 +358,10 @@ class Server {
|
|
|
194
358
|
*/
|
|
195
359
|
getCache() {
|
|
196
360
|
if (!this.cacheService) {
|
|
197
|
-
// eslint-disable-next-line global-require
|
|
198
|
-
const Cache = require('./services/cache/Cache'); // Speed optimisation
|
|
199
361
|
this.cacheService = new Cache(this.app);
|
|
200
362
|
}
|
|
201
363
|
return this.cacheService;
|
|
202
364
|
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Get file using Inhirence (ability to overrite models, configs, etc)
|
|
206
|
-
* @param {('models'|'config')} fileType type of file to load
|
|
207
|
-
* @param {string} fileName name of file to load
|
|
208
|
-
*/
|
|
209
|
-
getFileWithExtendingInhirence(fileType, fileName) {
|
|
210
|
-
let file;
|
|
211
|
-
try {
|
|
212
|
-
// eslint-disable-next-line global-require, import/no-dynamic-require
|
|
213
|
-
file = require(`${this.config.folders[fileType]}/${fileName}`);
|
|
214
|
-
} catch (e) {
|
|
215
|
-
try {
|
|
216
|
-
// eslint-disable-next-line global-require, import/no-dynamic-require
|
|
217
|
-
file = require(`./${fileType}/${fileName}`);
|
|
218
|
-
} catch (e2) {
|
|
219
|
-
const levels = [
|
|
220
|
-
'error',
|
|
221
|
-
'warn',
|
|
222
|
-
'info',
|
|
223
|
-
'http',
|
|
224
|
-
'verbose',
|
|
225
|
-
'debug',
|
|
226
|
-
'silly',
|
|
227
|
-
];
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
!process.env.LOGGER_CONSOLE_LEVEL ||
|
|
231
|
-
levels.indexOf(process.env.LOGGER_CONSOLE_LEVEL) > 0 // as a warn level
|
|
232
|
-
) {
|
|
233
|
-
console.warn(
|
|
234
|
-
`Config not found '${fileName}'. This can be a normal (in case this an environment config)`,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
file = false;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return file;
|
|
242
|
-
}
|
|
243
365
|
}
|
|
244
366
|
|
|
245
367
|
module.exports = Server;
|
package/services/cache/Cache.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
const redis = require('redis');
|
|
2
1
|
const Base = require('../../modules/Base');
|
|
3
2
|
|
|
4
3
|
class Cache extends Base {
|
|
5
4
|
constructor(app) {
|
|
5
|
+
super(app);
|
|
6
|
+
this.whenReady = this.#init();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async #init() {
|
|
6
10
|
// todo for now only redis. refactor for drives support in future
|
|
7
11
|
// at least memory and redis drivers should be presented
|
|
8
12
|
// memory drives should works on master process level
|
|
9
13
|
// we should support multiple cashe same time
|
|
10
|
-
|
|
14
|
+
const redis = await import('redis');
|
|
11
15
|
const conf = this.app.getConfig('redis');
|
|
12
16
|
this.redisClient = redis.createClient({
|
|
13
17
|
url: conf.url,
|
|
@@ -33,6 +37,7 @@ class Cache extends Base {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
async getSetValue(keyValue, onNotFound, storeTime = 60 * 5) {
|
|
40
|
+
await this.whenReady;
|
|
36
41
|
if (!this.redisClient.isOpen) {
|
|
37
42
|
await this.redisClient.connect();
|
|
38
43
|
}
|
|
@@ -58,9 +63,7 @@ class Cache extends Base {
|
|
|
58
63
|
try {
|
|
59
64
|
result = await onNotFound();
|
|
60
65
|
} catch (e) {
|
|
61
|
-
this.logger.error(
|
|
62
|
-
`Cache onNotFound for key '${key}' error: ${e.message}`,
|
|
63
|
-
);
|
|
66
|
+
this.logger.error(`Cache onNotFound for key '${key}' error: ${e}`);
|
|
64
67
|
this.promiseMapping.delete(key);
|
|
65
68
|
reject(e);
|
|
66
69
|
return Promise.reject(e);
|
|
@@ -100,6 +103,8 @@ class Cache extends Base {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
async removeKey(keyValue) {
|
|
106
|
+
await this.whenReady;
|
|
107
|
+
|
|
103
108
|
if (!this.redisClient.isOpen) {
|
|
104
109
|
await this.redisClient.connect();
|
|
105
110
|
}
|