@adaptivestone/framework 2.15.4 → 3.0.1

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/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@adaptivestone/framework",
3
- "version": "2.15.4",
3
+ "version": "3.0.1",
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,18 +26,17 @@
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",
32
32
  "email-templates": "^8.0.7",
33
33
  "express": "^4.17.1",
34
- "i18next": "^20.3.5",
34
+ "i18next": "^21.2.4",
35
35
  "i18next-chained-backend": "^3.0.2",
36
36
  "i18next-fs-backend": "^1.1.1",
37
37
  "i18next-http-middleware": "^3.1.4",
38
38
  "minimist": "^1.2.5",
39
- "mongoose": "^5.13.3",
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
- "winston-transport-sentry-node": "^1.0.2",
48
+ "winston-transport-sentry-node": "^2.0.0",
50
49
  "yup": "^0.32.9"
51
50
  },
52
51
  "devDependencies": {
53
- "eslint": "^7.31.0",
54
- "eslint-config-airbnb-base": "^14.2.1",
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": "^24.4.0",
56
+ "eslint-plugin-jest": "^25.0.0",
58
57
  "husky": "^7.0.0",
59
58
  "jest": "^27.0.6",
60
- "lint-staged": "^11.1.1",
61
- "mongodb-memory-server": "^7.3.4",
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 ADDED
@@ -0,0 +1,75 @@
1
+ import TFolderConfig from './types/TFoldersConfig';
2
+ import { ExpandDeep } from './types/Expand';
3
+
4
+ import EventEmitter from 'events';
5
+
6
+ import { Model as MongooseModel, Schema } from 'mongoose';
7
+
8
+ import BaseCli from './modules/BaseCli';
9
+ import Cache from './services/cache/Cache';
10
+
11
+ type ServerConfig = {
12
+ folders: ExpandDeep<TFolderConfig>;
13
+ };
14
+
15
+ declare class Server {
16
+ config: ServerConfig;
17
+ app: {
18
+ getConfig: Server['getConfig'];
19
+ getModel: Server['getModel'];
20
+ runCliCommand: Server['runCliCommand'];
21
+ updateConfig: Server['updateConfig'];
22
+ foldersConfig: Server['config'];
23
+ events: EventEmitter;
24
+ get cache(): Server['cacheService'];
25
+ httpServer: null;
26
+ controllerManager: null;
27
+ };
28
+ cacheService: Cache;
29
+
30
+ cache: {
31
+ configs: Map<string, {}>;
32
+ models: Map<string, MongooseModel<any>>;
33
+ };
34
+ cli: boolean;
35
+
36
+ /**
37
+ * Construct new server
38
+ */
39
+ constructor(config: ExpandDeep<ServerConfig>);
40
+
41
+ /**
42
+ * Start server (http + websocket + init all http and websocet ralated functions)
43
+ */
44
+ startServer(callbackBefore404?: Promise<null>): Promise<null>;
45
+
46
+ /**
47
+ * Return config from {configName} (file name) on config folder.
48
+ * Support cache and updating confing into cache
49
+ * Also will update config based on NODE_ENV. If config.js and config.production.js
50
+ * and NODE_ENV is production then we will load base config (config.js) and the load
51
+ * environment config (config.production.js) and overwrite base config options
52
+ * @see updateConfig
53
+ * @TODO generate that based on real data
54
+ */
55
+ getConfig(configName: string): {};
56
+
57
+ /**
58
+ * Primary designed for tests when we need to update some configs before start testing
59
+ * Should be called before any initialization was done
60
+ */
61
+ updateConfig(configName: string, config: {}): {};
62
+
63
+ /**
64
+ * Return model from {modelName} (file name) on model folder.
65
+ * Support cache
66
+ */
67
+ getModel(modelName: string): MongooseModel<any>;
68
+
69
+ /**
70
+ * Run cli command into framework (http, ws, etc)
71
+ */
72
+ runCliCommand(commandName: string, args: {}): Promise<BaseCli['run']>;
73
+ }
74
+
75
+ export = Server;
package/server.js CHANGED
@@ -32,6 +32,8 @@ class Server {
32
32
  get cache() {
33
33
  return that.getCache();
34
34
  },
35
+ httpServer: null,
36
+ controllerManager: null,
35
37
  };
36
38
 
37
39
  this.cache = {
@@ -122,7 +124,7 @@ class Server {
122
124
  updateConfig(configName, config) {
123
125
  // const confName = configName.charAt(0).toUpperCase() + configName.slice(1);
124
126
  const conf = this.getConfig(configName);
125
- const newConf = Object.assign(conf, config);
127
+ const newConf = Object.assign(conf, config); // TODO deep clone
126
128
  this.cache.configs.set(configName, newConf);
127
129
  return newConf;
128
130
  }
@@ -134,6 +136,11 @@ class Server {
134
136
  * @returns {import('mongoose').Model}
135
137
  */
136
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
+ }
137
144
  if (!this.cache.models.has(modelName)) {
138
145
  const Model = this.getFileWithExtendingInhirence('models', modelName);
139
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;
@@ -57,6 +57,10 @@ class Cache extends Base {
57
57
  this.promiseMapping.delete(key);
58
58
  return result;
59
59
  }
60
+
61
+ static get loggerGroup() {
62
+ return 'Cache_';
63
+ }
60
64
  }
61
65
 
62
66
  module.exports = Cache;
@@ -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(bodyParser.urlencoded({ limit: '50mb', extended: true }));
45
- this.express.use(bodyParser.json({ limit: '50mb' }));
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,8 +1,17 @@
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;
10
+ this.logger.verbose(
11
+ `GetUserByToken token in BODY ${token}. Token if Authorization header ${req.get(
12
+ 'Authorization',
13
+ )}`,
14
+ );
6
15
  if (!token) {
7
16
  token = req.get('Authorization');
8
17
  if (!token || token === 'null') {
@@ -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();
@@ -0,0 +1,11 @@
1
+ export type Expand<T> = T extends object
2
+ ? T extends infer O
3
+ ? { [K in keyof O]: O[K] }
4
+ : never
5
+ : T;
6
+
7
+ export type ExpandDeep<T> = T extends object
8
+ ? T extends infer O
9
+ ? { [K in keyof O]: ExpandDeep<O[K]> }
10
+ : never
11
+ : T;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param config path to folder with config files
3
+ * @param models path to folder with moidels files
4
+ * @param controllers path to folder with controllers files
5
+ * @param views path to folder with view files
6
+ * @param public path to folder with public files
7
+ * @param locales path to folder with locales files
8
+ * @param emails path to folder with emails files
9
+ */
10
+ type FolderConfig = {
11
+ config: string;
12
+ models: string;
13
+ controllers: string;
14
+ views: string;
15
+ public: string;
16
+ emails: string;
17
+ };
18
+
19
+ export default FolderConfig;