@adaptivestone/framework 2.17.0 → 3.0.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 +55 -0
- package/README.md +2 -0
- package/commands/Documentation.js +6 -8
- package/commands/DropIndex.js +4 -0
- package/commands/migration/Create.js +4 -0
- package/commands/migration/Migrate.js +4 -0
- package/config/http.js +1 -1
- package/config/i18n.js +1 -1
- package/config/log.js +2 -1
- package/controllers/Auth.js +19 -28
- package/controllers/Auth.test.js +4 -9
- package/controllers/Home.js +1 -2
- package/models/User.js +1 -10
- package/modules/AbstractCommand.js +4 -0
- package/modules/AbstractController.js +236 -267
- package/modules/AbstractModel.js +15 -22
- package/modules/Base.d.ts +0 -5
- package/modules/Base.js +0 -9
- package/package.json +8 -14
- package/server.d.ts +3 -1
- package/server.js +6 -1
- package/services/cache/Cache.d.ts +22 -0
- package/services/cache/Cache.js +4 -0
- package/services/http/HttpServer.js +5 -3
- package/services/http/middleware/AbstractMiddleware.js +14 -0
- package/services/http/middleware/Auth.js +4 -0
- package/services/http/middleware/GetUserByToken.js +4 -0
- package/services/http/middleware/PrepareAppInfo.js +4 -0
- package/services/http/middleware/RateLimiter.js +5 -1
- package/services/http/middleware/Role.js +29 -0
- package/tests/setup.js +1 -1
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://gitlab.com/adaptivestone/framework"
|
|
9
9
|
},
|
|
10
|
+
"homepage": "https://framework.adaptivestone.com/",
|
|
10
11
|
"scripts": {
|
|
11
12
|
"dev": "nodemon ./index.js",
|
|
12
13
|
"prod": "nodemon ./cluster.js",
|
|
@@ -25,7 +26,6 @@
|
|
|
25
26
|
"license": "MIT",
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"bcrypt": "^5.0.1",
|
|
28
|
-
"body-parser": "^1.19.0",
|
|
29
29
|
"cors": "^2.8.5",
|
|
30
30
|
"deepmerge": "^4.2.2",
|
|
31
31
|
"dotenv": "^10.0.0",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"i18next-fs-backend": "^1.1.1",
|
|
37
37
|
"i18next-http-middleware": "^3.1.4",
|
|
38
38
|
"minimist": "^1.2.5",
|
|
39
|
-
"mongoose": "^
|
|
39
|
+
"mongoose": "^6.0.0",
|
|
40
40
|
"nodemailer": "^6.6.3",
|
|
41
41
|
"nodemailer-sendmail-transport": "^1.0.2",
|
|
42
42
|
"nodemailer-stub-transport": "^1.1.0",
|
|
@@ -44,21 +44,20 @@
|
|
|
44
44
|
"pug": "^3.0.2",
|
|
45
45
|
"rate-limiter-flexible": "^2.2.4",
|
|
46
46
|
"redis": "^3.1.2",
|
|
47
|
-
"validator": "^13.6.0",
|
|
48
47
|
"winston": "^3.3.3",
|
|
49
48
|
"winston-transport-sentry-node": "^2.0.0",
|
|
50
49
|
"yup": "^0.32.9"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
|
-
"eslint": "^
|
|
54
|
-
"eslint-config-airbnb-base": "^
|
|
52
|
+
"eslint": "^8.0.0",
|
|
53
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
55
54
|
"eslint-config-prettier": "^8.3.0",
|
|
56
55
|
"eslint-plugin-import": "^2.23.4",
|
|
57
|
-
"eslint-plugin-jest": "^
|
|
56
|
+
"eslint-plugin-jest": "^25.0.0",
|
|
58
57
|
"husky": "^7.0.0",
|
|
59
58
|
"jest": "^27.0.6",
|
|
60
|
-
"lint-staged": "^
|
|
61
|
-
"mongodb-memory-server": "^
|
|
59
|
+
"lint-staged": "^12.0.0",
|
|
60
|
+
"mongodb-memory-server": "^8.0.2",
|
|
62
61
|
"nodemon": "^2.0.12",
|
|
63
62
|
"supertest": "^6.1.4"
|
|
64
63
|
},
|
|
@@ -66,10 +65,5 @@
|
|
|
66
65
|
"**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
|
67
66
|
"prettier --write"
|
|
68
67
|
]
|
|
69
|
-
},
|
|
70
|
-
"config": {
|
|
71
|
-
"mongodbMemoryServer": {
|
|
72
|
-
"version": "4.4.6"
|
|
73
|
-
}
|
|
74
68
|
}
|
|
75
69
|
}
|
package/server.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import EventEmitter from 'events';
|
|
|
6
6
|
import { Model as MongooseModel, Schema } from 'mongoose';
|
|
7
7
|
|
|
8
8
|
import BaseCli from './modules/BaseCli';
|
|
9
|
+
import Cache from './services/cache/Cache';
|
|
9
10
|
|
|
10
11
|
type ServerConfig = {
|
|
11
12
|
folders: ExpandDeep<TFolderConfig>;
|
|
@@ -20,10 +21,11 @@ declare class Server {
|
|
|
20
21
|
updateConfig: Server['updateConfig'];
|
|
21
22
|
foldersConfig: Server['config'];
|
|
22
23
|
events: EventEmitter;
|
|
23
|
-
get cache(): Server['
|
|
24
|
+
get cache(): Server['cacheService'];
|
|
24
25
|
httpServer: null;
|
|
25
26
|
controllerManager: null;
|
|
26
27
|
};
|
|
28
|
+
cacheService: Cache;
|
|
27
29
|
|
|
28
30
|
cache: {
|
|
29
31
|
configs: Map<string, {}>;
|
package/server.js
CHANGED
|
@@ -124,7 +124,7 @@ class Server {
|
|
|
124
124
|
updateConfig(configName, config) {
|
|
125
125
|
// const confName = configName.charAt(0).toUpperCase() + configName.slice(1);
|
|
126
126
|
const conf = this.getConfig(configName);
|
|
127
|
-
const newConf = Object.assign(conf, config);
|
|
127
|
+
const newConf = Object.assign(conf, config); // TODO deep clone
|
|
128
128
|
this.cache.configs.set(configName, newConf);
|
|
129
129
|
return newConf;
|
|
130
130
|
}
|
|
@@ -136,6 +136,11 @@ class Server {
|
|
|
136
136
|
* @returns {import('mongoose').Model}
|
|
137
137
|
*/
|
|
138
138
|
getModel(modelName) {
|
|
139
|
+
if (modelName.endsWith('s')) {
|
|
140
|
+
console.warn(
|
|
141
|
+
`Probably your model name '${modelName}' in plural from. Try to avoid plural form`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
139
144
|
if (!this.cache.models.has(modelName)) {
|
|
140
145
|
const Model = this.getFileWithExtendingInhirence('models', modelName);
|
|
141
146
|
if (!Model) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Base from '../../modules/Base';
|
|
2
|
+
import Server from '../../server';
|
|
3
|
+
|
|
4
|
+
declare class Cache extends Base {
|
|
5
|
+
app: Server['app'];
|
|
6
|
+
|
|
7
|
+
constructor(app: Server['app']);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get value from cache. Set and get if not eists
|
|
11
|
+
* @param key key to check
|
|
12
|
+
* @param onNotFound callback that will be executed if value not found on cahce
|
|
13
|
+
* @param storeTime how long we should store value on cache
|
|
14
|
+
*/
|
|
15
|
+
getSetValue(
|
|
16
|
+
key: String,
|
|
17
|
+
onNotFound: () => Promise<any>,
|
|
18
|
+
storeTime: number,
|
|
19
|
+
): Promise<any>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export = Cache;
|
package/services/cache/Cache.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const bodyParser = require('body-parser');
|
|
5
4
|
const cors = require('cors');
|
|
6
5
|
|
|
7
6
|
const i18next = require('i18next');
|
|
8
7
|
const i18nextMiddleware = require('i18next-http-middleware');
|
|
9
8
|
const BackendFS = require('i18next-fs-backend');
|
|
10
9
|
const Backend = require('i18next-chained-backend');
|
|
10
|
+
const PrepareAppInfoMiddleware = require('./middleware/PrepareAppInfo');
|
|
11
11
|
|
|
12
12
|
const Base = require('../../modules/Base');
|
|
13
13
|
|
|
@@ -41,11 +41,13 @@ class HttpServer extends Base {
|
|
|
41
41
|
origin: httpConfig.corsDomains,
|
|
42
42
|
}),
|
|
43
43
|
); // todo whitelist
|
|
44
|
-
this.express.use(
|
|
45
|
-
this.express.use(
|
|
44
|
+
this.express.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|
45
|
+
this.express.use(express.json({ limit: '50mb' }));
|
|
46
46
|
this.express.use(express.static(folderConfig.folders.public));
|
|
47
47
|
this.express.use(express.static('./public'));
|
|
48
48
|
|
|
49
|
+
this.express.use(new PrepareAppInfoMiddleware(this.app).getMiddleware());
|
|
50
|
+
|
|
49
51
|
// As exprress will check numbersof arguments
|
|
50
52
|
// eslint-disable-next-line no-unused-vars
|
|
51
53
|
this.express.use((err, req, res, next) => {
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
const Base = require('../../../modules/Base');
|
|
2
2
|
|
|
3
3
|
class AbstractMiddleware extends Base {
|
|
4
|
+
constructor(app, params) {
|
|
5
|
+
super(app);
|
|
6
|
+
this.params = params;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static get description() {
|
|
10
|
+
return ' Middleware description. Please provide own';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async middleware(req, res, next) {
|
|
14
|
+
this.logger.warn('Middleware is not implemented');
|
|
15
|
+
next();
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
getMiddleware() {
|
|
5
19
|
return this.middleware.bind(this);
|
|
6
20
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const AbstractMiddleware = require('./AbstractMiddleware');
|
|
2
2
|
|
|
3
3
|
class AuthMiddleware extends AbstractMiddleware {
|
|
4
|
+
static get description() {
|
|
5
|
+
return 'Allow to pass only if the user provided. Please use any middleware that provide user instance before';
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
async middleware(req, res, next) {
|
|
5
9
|
if (!req.appInfo.user) {
|
|
6
10
|
this.logger.info('User try to access resource without credentials');
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const AbstractMiddleware = require('./AbstractMiddleware');
|
|
2
2
|
|
|
3
3
|
class GetUserByToken extends AbstractMiddleware {
|
|
4
|
+
static get description() {
|
|
5
|
+
return 'Grab a token and try to parse the user from it. It user exist will add req.appInfo.user variable';
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
async middleware(req, res, next) {
|
|
5
9
|
let { token } = req.body;
|
|
6
10
|
if (!token) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const AbstractMiddleware = require('./AbstractMiddleware');
|
|
2
2
|
|
|
3
3
|
class PrepareAppInfo extends AbstractMiddleware {
|
|
4
|
+
static get description() {
|
|
5
|
+
return 'Basic middleware that creates "req.appInfo" object';
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
async middleware(req, res, next) {
|
|
5
9
|
if (!req.appInfo) {
|
|
6
10
|
req.appInfo = {
|
|
@@ -10,8 +10,12 @@ const mongoose = require('mongoose');
|
|
|
10
10
|
const AbstractMiddleware = require('./AbstractMiddleware');
|
|
11
11
|
|
|
12
12
|
class RateLimiter extends AbstractMiddleware {
|
|
13
|
+
static get description() {
|
|
14
|
+
return 'Rate limiter middleware. Limit amount of request. Please refer to documentation';
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
constructor(app, params) {
|
|
14
|
-
super(app);
|
|
18
|
+
super(app, params);
|
|
15
19
|
const limiterOptions = this.app.getConfig('rateLimiter');
|
|
16
20
|
this.finalOptions = merge(limiterOptions, params);
|
|
17
21
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const AbstractMiddleware = require('./AbstractMiddleware');
|
|
2
|
+
|
|
3
|
+
class RoleMiddleware extends AbstractMiddleware {
|
|
4
|
+
static get description() {
|
|
5
|
+
return 'Check user role (user.roles property). If the user has no role then stop request and return error. OR logic (any role will pass user)';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async middleware(req, res, next) {
|
|
9
|
+
const { user } = req.appInfo;
|
|
10
|
+
|
|
11
|
+
if (!user) {
|
|
12
|
+
return res.status(401).send();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let hasRole = false;
|
|
16
|
+
user.roles.forEach((role) => {
|
|
17
|
+
if (this.params.roles.includes(role)) {
|
|
18
|
+
hasRole = true;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!hasRole) {
|
|
23
|
+
return res.status(403).json({ message: 'You do not have access' });
|
|
24
|
+
}
|
|
25
|
+
return next();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = RoleMiddleware;
|
package/tests/setup.js
CHANGED
|
@@ -9,7 +9,7 @@ const Server = require('../server');
|
|
|
9
9
|
jest.setTimeout(1000000);
|
|
10
10
|
beforeAll(async () => {
|
|
11
11
|
mongoMemoryServerInstance = await MongoMemoryReplSet.create({
|
|
12
|
-
binary: { version: '4.4.6' },
|
|
12
|
+
// binary: { version: '4.4.6' },
|
|
13
13
|
replSet: { storageEngine: 'wiredTiger' },
|
|
14
14
|
});
|
|
15
15
|
await mongoMemoryServerInstance.waitUntilRunning();
|