nodester 0.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +125 -0
  3. package/docs/App.md +13 -0
  4. package/docs/Queries.md +61 -0
  5. package/docs/Readme.md +2 -0
  6. package/docs/Routing.md +34 -0
  7. package/examples/goal/index.js +23 -0
  8. package/examples/rest/index.js +25 -0
  9. package/examples/rest/node_modules/.package-lock.json +40 -0
  10. package/examples/rest/package-lock.json +72 -0
  11. package/examples/rest/package.json +14 -0
  12. package/lib/application/MiddlewareStack.js +125 -0
  13. package/lib/application/http/request.js +462 -0
  14. package/lib/application/http/response.js +1107 -0
  15. package/lib/application/http/utils.js +254 -0
  16. package/lib/application/index.js +292 -0
  17. package/lib/constants/ConstantsEnum.js +13 -0
  18. package/lib/constants/ResponseFormats.js +7 -0
  19. package/lib/controllers/Controller.js +474 -0
  20. package/lib/controllers/JWTController.js +240 -0
  21. package/lib/controllers/ServiceController.js +109 -0
  22. package/lib/controllers/WebController.js +75 -0
  23. package/lib/facades/Facade.js +388 -0
  24. package/lib/facades/FacadeParams.js +11 -0
  25. package/lib/facades/ServiceFacade.js +17 -0
  26. package/lib/facades/jwt.facade.js +273 -0
  27. package/lib/factories/errors/CustomError.js +22 -0
  28. package/lib/factories/errors/index.js +9 -0
  29. package/lib/factories/responses/api.js +90 -0
  30. package/lib/factories/responses/html.js +55 -0
  31. package/lib/logger/console.js +24 -0
  32. package/lib/models/DisabledRefreshToken.js +68 -0
  33. package/lib/models/Extractor.js +320 -0
  34. package/lib/models/define.js +62 -0
  35. package/lib/models/mixins.js +369 -0
  36. package/lib/policies/Role.js +77 -0
  37. package/lib/policies/RoleExtracting.js +97 -0
  38. package/lib/preprocessors/BodyPreprocessor.js +61 -0
  39. package/lib/preprocessors/IncludesPreprocessor.js +55 -0
  40. package/lib/preprocessors/QueryPreprocessor.js +64 -0
  41. package/lib/routers/Default/index.js +143 -0
  42. package/lib/routers/Default/layer.js +50 -0
  43. package/lib/routers/Main/index.js +10 -0
  44. package/lib/routers/Roles/index.js +81 -0
  45. package/lib/services/includes.service.js +79 -0
  46. package/lib/services/jwt.service.js +147 -0
  47. package/lib/tools/sql.tool.js +82 -0
  48. package/lib/utils/dates.util.js +23 -0
  49. package/lib/utils/forms.util.js +22 -0
  50. package/lib/utils/json.util.js +49 -0
  51. package/lib/utils/mappers/Routes/index.js +100 -0
  52. package/lib/utils/mappers/Routes/utils.js +20 -0
  53. package/lib/utils/modelAssociations.util.js +44 -0
  54. package/lib/utils/objects.util.js +69 -0
  55. package/lib/utils/params.util.js +19 -0
  56. package/lib/utils/path.util.js +26 -0
  57. package/lib/utils/queries.util.js +240 -0
  58. package/lib/utils/sanitizations.util.js +111 -0
  59. package/lib/utils/sql.util.js +78 -0
  60. package/lib/utils/strings.util.js +43 -0
  61. package/lib/utils/types.util.js +26 -0
  62. package/package.json +63 -0
  63. package/tests/index.test.js +35 -0
@@ -0,0 +1,64 @@
1
+ // Constants:
2
+ const VISITOR = 'visitor';
3
+ const DefaultAvailableParamsForRoles = { [VISITOR]: [ 'skip', 'limit', 'order' ] };
4
+ const DefaultStaticParamsForRoles = { [VISITOR]: { limit: 50 } };
5
+
6
+ // Custom error.
7
+ const { Err } = require('nodester/factories/errors');
8
+
9
+
10
+ module.exports = class QueryPreprocessor {
11
+
12
+ constructor(
13
+ availableParamsForRoles,
14
+ staticParamsForRoles,
15
+ customProcessFunction
16
+ ) {
17
+ this.availableParamsForRoles = availableParamsForRoles ?? DefaultAvailableParamsForRoles;
18
+ this.staticParamsForRoles = staticParamsForRoles ?? DefaultStaticParamsForRoles;
19
+
20
+ this.customProcessFunction = customProcessFunction ? customProcessFunction : ()=>{};
21
+ }
22
+
23
+ async extract(
24
+ req,
25
+ role
26
+ ) {
27
+ try {
28
+ const requestQuery = req.query;
29
+
30
+ if (!requestQuery || typeof requestQuery !== 'object') {
31
+ const err = new Err();
32
+ err.name = 'ValidationError';
33
+ throw err;
34
+ }
35
+
36
+ // Get role or set "visitor"
37
+ const _role = typeof role === 'string' && role.length > 1 ? role : [VISITOR];
38
+
39
+ const resultQuery = {};
40
+
41
+ const params = this.availableParamsForRoles[_role] ?? [];
42
+ const staticValues = this.staticParamsForRoles[_role] ?? {};
43
+
44
+ params.forEach((param) => {
45
+ // If such param is set in query:
46
+ if (!!requestQuery[param]) {
47
+ resultQuery[param] = staticValues[param] ?? requestQuery[param];
48
+ }
49
+ // If such param is not set, but we have a "static" for it:
50
+ else if (!requestQuery[param] && !!staticValues[param]) {
51
+ resultQuery[param] = staticValues[param];
52
+ }
53
+ });
54
+
55
+ // Make further preprocessing using customly defined function.
56
+ await this.customProcessFunction.call(this, req, role, resultQuery);
57
+
58
+ return Promise.resolve(resultQuery);
59
+ }
60
+ catch(error) {
61
+ return Promise.reject(error);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,143 @@
1
+ const Layer = require('./layer');
2
+
3
+ // Utils:
4
+ const { getType } = require('../../utils/types.util');
5
+
6
+
7
+ module.exports = function NodesterDefaultRouter(app=null) {
8
+ this.markers = {};
9
+ this.layer = new Layer();
10
+
11
+ if (!!app) {
12
+ this.app = app
13
+ }
14
+
15
+ // Expose methods:
16
+ // Markers:
17
+ this.addMarker = _addMarker;
18
+ this.getMarker = _getMarker;
19
+
20
+ // Routing:
21
+ this.handle = _handle;
22
+ this.only = _only;
23
+ this.route = _route;
24
+ }
25
+
26
+ /**
27
+ * Adds marker to the stack.
28
+ * Returns itself for chaining.
29
+ * @return {NodesterDefaultRouter}
30
+ *
31
+ * @alias addMarker
32
+ * @public
33
+ */
34
+ function _addMarker(nameOrSymbol='', fn=()=>{}) {
35
+ if (typeof fn !== 'function') {
36
+ const err = new TypeError(`Router.addMarker() requires a middleware function but got a ${ getType(fn) }`);
37
+ throw err;
38
+ }
39
+
40
+ const marker = this.getMarker(nameOrSymbol);
41
+ if (marker.marker.index > -1) {
42
+ const err = new TypeError(`Marker with key ${ nameOrSymbol } is already set.`);
43
+ throw err;
44
+ }
45
+
46
+ this.markers[nameOrSymbol] = fn;
47
+
48
+ return this;
49
+ }
50
+
51
+
52
+ /**
53
+ * Tries to find marker's data by provided key.
54
+ * @return {Object}
55
+ *
56
+ * @alias getMarker
57
+ * @public
58
+ */
59
+ function _getMarker(nameOrSymbol='') {
60
+ const result = {
61
+ marker: {
62
+ key: nameOrSymbol,
63
+ index: -1,
64
+ },
65
+ middleware: undefined
66
+ }
67
+ try {
68
+ const keys = Object.keys(this.markers);
69
+
70
+ const index = keys.indexOf(nameOrSymbol);
71
+ if (keys.indexOf(nameOrSymbol) === -1) {
72
+ const err = new Error('NotFound');
73
+ throw err;
74
+ }
75
+
76
+ // Marker found:
77
+ result.marker.index = index;
78
+ result.middleware = this.markers[nameOrSymbol];
79
+ }
80
+ catch(error) {
81
+ result.marker.index = -1;
82
+ }
83
+
84
+ return result;
85
+ }
86
+
87
+ /**
88
+ * Start routes pipeline processing.
89
+ *
90
+ * If no callback is provided, then default error handlers will respond
91
+ * in the event of an error bubbling through the stack.
92
+ *
93
+ * @alias handle
94
+ * @public
95
+ */
96
+ function _handle(req, res, callback) {
97
+ const method = req.method;
98
+ const requestPath = req.url;
99
+
100
+ console.log(method, requestPath);
101
+
102
+ let markerName = null;
103
+
104
+ // Check if this request satisfies any markers:
105
+ const markers = Object.entries(this.markers);
106
+ console.log({ markers });
107
+
108
+ for (const [marker, fn] of markers) {
109
+ const result = fn(req, res, callback);
110
+ console.log(result);
111
+
112
+ if (result === true) {
113
+ markerName = marker;
114
+ break;
115
+ }
116
+ }
117
+
118
+ console.log({ markerName });
119
+
120
+ return res.send(markerName ?? 'Hi!');
121
+ }
122
+
123
+
124
+ /**
125
+ *
126
+ * @alias only
127
+ * @public
128
+ */
129
+ function _only(condition) {
130
+ this.layer.push('condition', condition);
131
+ // Return layer for chaining.
132
+ return this.layer;
133
+ }
134
+
135
+
136
+ /**
137
+ *
138
+ * @alias route
139
+ * @public
140
+ */
141
+ function _route() {
142
+
143
+ }
@@ -0,0 +1,50 @@
1
+
2
+ module.exports = function NodesterDefaultRouterLayer() {
3
+ this.conditions = [];
4
+ this.routesList = {};
5
+
6
+ this.push = _push;
7
+ this.route = _route;
8
+ }
9
+
10
+
11
+ /**
12
+ *
13
+ * @alias push
14
+ * @public
15
+ */
16
+ function _push(key='condition') {
17
+ let args = [...arguments].shift();
18
+
19
+ switch(key) {
20
+ case 'condition':
21
+ // this.conditions[]
22
+ break;
23
+ case 'route':
24
+ const route = args.shift();
25
+ this.routesList[route] = args;
26
+ break;
27
+ break;
28
+ }
29
+
30
+ return this;
31
+ }
32
+
33
+
34
+ /**
35
+ *
36
+ * @alias route
37
+ * @public
38
+ */
39
+ function _route(path='/') {
40
+ const middlewares = [...arguments].shift();
41
+
42
+ console.log({ path });
43
+ }
44
+
45
+
46
+ /**
47
+ *
48
+ * @alias push
49
+ * @public
50
+ */
@@ -0,0 +1,10 @@
1
+ const express = require('express');
2
+
3
+
4
+ class MainRouter extends express.Router {
5
+ constructor() {
6
+ super();
7
+ }
8
+ }
9
+
10
+ module.exports = MainRouter;
@@ -0,0 +1,81 @@
1
+ const express = require('express');
2
+ // Utils:
3
+ const cwd = process.cwd();
4
+ const Path = require('path');
5
+ // Custom error.
6
+ const { Err } = require('nodester/factories/errors');
7
+
8
+
9
+ class RolesRouter extends express.Router {
10
+ constructor(
11
+ rolesToRoutesMap={},
12
+ pathToControllers=null
13
+ ) {
14
+ super();
15
+
16
+ // rolesToRoutesMap is a map of role -> route -> controller:
17
+ if (!rolesToRoutesMap) {
18
+ const err = new Err('"rolesToRoutesMap" argument is required.');
19
+ throw err;
20
+ }
21
+
22
+ if (!pathToControllers) {
23
+ const err = new Err('"pathToControllers" argument is required.');
24
+ throw err;
25
+ }
26
+
27
+ // Extract all available roles.
28
+ const roles = Object.keys(rolesToRoutesMap);
29
+
30
+ // Flip map (Make it route -> role -> controller):
31
+ const routesToRolesMap = {};
32
+ for (const role of roles) {
33
+ const roleRoutes = rolesToRoutesMap[role]
34
+
35
+ for (const route in roleRoutes) {
36
+ // If this route is set:
37
+ if (!!routesToRolesMap[route]) {
38
+ // Add role to set.
39
+ routesToRolesMap[route][role] = roleRoutes[route];
40
+ }
41
+ else {
42
+ // Create set for this role.
43
+ routesToRolesMap[route] = { [role]: roleRoutes[route] };
44
+ }
45
+ }
46
+ }
47
+
48
+ // At this point we have a map of route -> role -> controller.
49
+ // Let's now set these routes to this Router:
50
+ for (const route in routesToRolesMap) {
51
+ const rolesAndControllersMap = routesToRolesMap[route];
52
+
53
+ // Split route by space, as it has structure [<Rest method> <Route>]
54
+ const routeParts = route.split(/\s+/);
55
+ const routeMethod = routeParts[0].toLocaleLowerCase();
56
+ const routePath = routeParts[1].replace(/\s\s+/g, ' ');
57
+
58
+ // Set up this route:
59
+ this.route(routePath)[routeMethod]((req, res, next) => {
60
+ // Extract role:
61
+ const role = req?.user?.role ?? 'visitor';
62
+
63
+ // If no handler for this role-route, skip:
64
+ if (!rolesAndControllersMap[role]) {
65
+ return next();
66
+ }
67
+
68
+ const controllerAndMethod = rolesAndControllersMap[role].split('.');
69
+ const controllerName = controllerAndMethod[0];
70
+ const controllerMethod = controllerAndMethod[1];
71
+
72
+ // Get controller from path.
73
+ const controller = require(Path.join(cwd, pathToControllers, controllerName));
74
+
75
+ return controller[controllerMethod](req, res, next);
76
+ });
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = RolesRouter;
@@ -0,0 +1,79 @@
1
+ const {
2
+ splitByComma,
3
+ splitByDot
4
+ } = require('nodester/utils/strings.util');
5
+ // Query util.
6
+ const {
7
+ hasSubIncludesQuery,
8
+ cutSubIncludesQuery,
9
+ parseSubIncludesQuery
10
+ } = require('nodester/utils/queries.util');
11
+
12
+
13
+ module.exports = {
14
+ parseIncludesQuery: _parseIncludesQuery
15
+ }
16
+
17
+ function _parseIncludesQuery(
18
+ modelDefinition,
19
+ includesQueryString
20
+ ) {
21
+ const associationAndSubs = splitByComma(includesQueryString);
22
+ const allowedAssociations = Object.keys(modelDefinition.associations);
23
+
24
+ associationAndSubs.forEach((a) => {
25
+ let association = a.split('.')[0];
26
+
27
+ if (hasSubIncludesQuery(association)) {
28
+ const [ nestedQuery, _association ] = cutSubIncludesQuery(association);
29
+ association = _association;
30
+ }
31
+
32
+ if (allowedAssociations.indexOf(association) === -1) {
33
+ const err = new Error(`Association with name "${ association }" doesn't exist on this model.`);
34
+ err.name = 'NotFound';
35
+ throw err;
36
+ }
37
+ });
38
+
39
+ const result = associationAndSubs.map(associationAndSubsString => _parseSubIncludes(associationAndSubsString));
40
+
41
+ return result;
42
+ }
43
+
44
+ function _parseSubIncludes(associationAndSubsString) {
45
+ const associationAndSubs = splitByDot(associationAndSubsString);
46
+
47
+ // If association has SubIncludes:
48
+ if (associationAndSubs?.length > 0) {
49
+ const clearAssociation = associationAndSubs[0];
50
+ const subs = associationAndSubs.splice(1, associationAndSubs.length - 1);
51
+
52
+ if (subs.length > 0) {
53
+ const result = {
54
+ association: clearAssociation,
55
+ include: [ _parseSubIncludes(subs.join('.')) ]
56
+ };
57
+
58
+ _parseSubIncludesQueryIfPresent(result);
59
+
60
+ return result;
61
+ }
62
+ }
63
+
64
+ // By default just return association.
65
+ const result = { association: associationAndSubsString };
66
+ _parseSubIncludesQueryIfPresent(result);
67
+ return result;
68
+ }
69
+
70
+ function _parseSubIncludesQueryIfPresent(resultQuery) {
71
+ if (hasSubIncludesQuery(resultQuery.association)) {
72
+ const [ nestedQuery, clearAssociation ] = cutSubIncludesQuery(resultQuery.association);
73
+
74
+ resultQuery.association = clearAssociation;
75
+ parseSubIncludesQuery(clearAssociation, nestedQuery, resultQuery);
76
+ }
77
+
78
+ return resultQuery;
79
+ }
@@ -0,0 +1,147 @@
1
+ // Format of token: "Authorization: Bearer [token]".
2
+ const ACCESS_TOKEN_NAME = 'Authorization';
3
+ const REFRESH_TOKEN_NAME = 'x-refresh-token';
4
+ // JWT module.
5
+ const jwt = require('jsonwebtoken');
6
+ // Utils.
7
+ const { addSeconds } = require('nodester/utils/dates.util');
8
+
9
+
10
+ module.exports = class JWTService {
11
+ constructor(
12
+ accessTokenConfigs,
13
+ refreshTokenConfigs
14
+ ) {
15
+ if (!accessTokenConfigs || !refreshTokenConfigs){
16
+ throw new Error('"accessTokenConfigs" and "refreshTokenConfigs" are required arguments.');
17
+ }
18
+
19
+ this.accessTokenConfigs = { ...accessTokenConfigs };
20
+ this.refreshTokenConfigs = { ...refreshTokenConfigs };
21
+ }
22
+
23
+ extractTokenFromRequest(request) {
24
+ let token;
25
+
26
+ if (request.header(ACCESS_TOKEN_NAME)) {
27
+ token = this._parseAccessToken(
28
+ request.header(ACCESS_TOKEN_NAME)
29
+ );
30
+ }
31
+ else if (request.cookies[ACCESS_TOKEN_NAME]) {
32
+ token = this._parseAccessToken(
33
+ request.cookies[ACCESS_TOKEN_NAME]
34
+ );
35
+ }
36
+ // Check token in query:
37
+ else if (!!request.query.token) {
38
+ token = request.query.token;
39
+ delete request.body.token;
40
+ }
41
+ // Check token in body:
42
+ else if (!!request.body.token) {
43
+ token = request.body.token;
44
+ delete request.query.token;
45
+ }
46
+ else {
47
+ const err = new Error(`No ${ACCESS_TOKEN_NAME} was found`);
48
+ err.name = 'NoToken';
49
+ err.details = { message:err.message };
50
+ throw err;
51
+ }
52
+
53
+ return token;
54
+ }
55
+
56
+ extractRefreshTokenFromRequest(request) {
57
+ let token;
58
+
59
+ if (request.header(REFRESH_TOKEN_NAME)) {
60
+ token = request.header(REFRESH_TOKEN_NAME);
61
+ }
62
+ else if (request.cookies[REFRESH_TOKEN_NAME]) {
63
+ token = request.cookies[REFRESH_TOKEN_NAME];
64
+ }
65
+ // Check token in query:
66
+ else if (!!request.query.token) {
67
+ token = request.query.token;
68
+ delete request.body.token;
69
+ }
70
+ // Check token in body:
71
+ else if (!!request.body.token) {
72
+ token = request.body.token;
73
+ delete request.query.token;
74
+ }
75
+ else {
76
+ const err = new Error(`No ${REFRESH_TOKEN_NAME} was found`);
77
+ err.name = 'NoToken';
78
+ err.details = { message:err.message };
79
+ throw err;
80
+ }
81
+
82
+ return token;
83
+ }
84
+
85
+ issueAccessToken(payload) {
86
+ const { secret, expiresIn } = this.accessTokenConfigs;
87
+ return this._issueToken({ payload, secret, expiresIn });
88
+ }
89
+
90
+ issueRefreshToken(payload) {
91
+ const { secret, expiresIn } = this.refreshTokenConfigs;
92
+ return this._issueToken({ payload, secret, expiresIn });
93
+ }
94
+
95
+ verifyAccessToken(token) {
96
+ const { secret } = this.accessTokenConfigs;
97
+ return this._verifyToken({ token, secret });
98
+ }
99
+
100
+ verifyRefreshToken(token) {
101
+ const { secret } = this.refreshTokenConfigs;
102
+ return this._verifyToken({ token, secret });
103
+ }
104
+
105
+ async _issueToken({ payload, secret, expiresIn }) {
106
+ try {
107
+ const token = jwt.sign(payload, secret, { expiresIn });
108
+ const expirationDateValue = (addSeconds(new Date(), expiresIn/1000)).valueOf();
109
+
110
+ const fullToken = { token, expiresIn, expirationDateValue };
111
+ return Promise.resolve(fullToken);
112
+ }
113
+ catch(error) {
114
+ return Promise.reject(error);
115
+ }
116
+ }
117
+
118
+ async _verifyToken({ token, secret }) {
119
+ try {
120
+ const parsedToken = await jwt.verify(token, secret, {});
121
+ return Promise.resolve(parsedToken);
122
+ }
123
+ catch(error) {
124
+ const err = new Error('Invalid signature.');
125
+ err.name = 'ValidationError';
126
+ return Promise.reject(err);
127
+ }
128
+ }
129
+
130
+ _parseAccessToken(token) {
131
+ let parsed = '';
132
+
133
+ const parts = token.split(' ');
134
+
135
+ if (parts.length === 2 && /^Bearer$/.test(parts[0])) {
136
+ parsed = parts[1];
137
+ }
138
+ else {
139
+ const err = new Error(`Format for ${ACCESS_TOKEN_NAME}: Bearer [token]`);
140
+ err.name = 'InvalidFormat';
141
+ err.details = { message: err.message };
142
+ throw err;
143
+ }
144
+
145
+ return parsed;
146
+ }
147
+ }
@@ -0,0 +1,82 @@
1
+ const { QueryTypes } = require('sequelize');
2
+ // Utils:
3
+ const fs = require('fs');
4
+ const { promisify } = require('util');
5
+ const readFile = promisify(fs.readFile);
6
+
7
+
8
+ module.exports = {
9
+ parseSQLFileContents: _parseSQLFileContents,
10
+
11
+ rawSelect: _rawSelect,
12
+ rawUpdate: _rawUpdate,
13
+ rawInsert: _rawInsert
14
+ }
15
+
16
+ async function _parseSQLFileContents(filePath) {
17
+ try {
18
+ const fileContent = await readFile(filePath, 'utf8');
19
+ const sqlCommands = fileContent.toString()
20
+ .split('\n')
21
+ .filter(command => command !== '');
22
+
23
+ const output = { commands: sqlCommands };
24
+ return Promise.resolve(output);
25
+ }
26
+ catch(error) {
27
+ return Promise.reject(error);
28
+ }
29
+ }
30
+
31
+ function _rawSelect(db, tableName) {
32
+ const query = `select * from ${ tableName }`;
33
+ return db.query(query, { type: QueryTypes.SELECT });
34
+ }
35
+
36
+ function _rawUpdate(db, tableName, where, data) {
37
+ const dataKeys = Object.keys(data);
38
+
39
+ let sqlAttributes = '';
40
+
41
+ for (let i=0; i < dataKeys.length; i++) {
42
+ const key = dataKeys[i];
43
+ const val = data[key];
44
+
45
+ sqlAttributes += `${ key }='${ val }'`;
46
+
47
+ if (i < dataKeys.length-1) {
48
+ sqlAttributes += ', ';
49
+ }
50
+ }
51
+
52
+ const sql = `UPDATE \`${ tableName }\` SET ${ sqlAttributes } where ${ where };`;
53
+
54
+ return db.query(sql, {
55
+ type: QueryTypes.INSERT
56
+ });
57
+ }
58
+
59
+ function _rawInsert(db, tableName, data) {
60
+ const dataKeys = Object.keys(data);
61
+ const dataValues = Object.values(data);
62
+
63
+ let values = '';
64
+ for (let i=0; i < dataValues.length-1; i++) {
65
+ const val = dataValues[i];
66
+
67
+ if (val === undefined || val === null)
68
+ values += 'null, ';
69
+ else if (typeof val === 'object')
70
+ values += `'${ JSON.stringify(val) }', `;
71
+ // values += `'{}', `;
72
+ else
73
+ values += `'${val}', `;
74
+ }
75
+ values += `'${ dataValues[dataValues.length-1] }'`;
76
+
77
+ const sql = `INSERT INTO \`${ tableName }\` (${ dataKeys.join(', ') }) VALUES (${ values });`;
78
+
79
+ return db.query(sql, {
80
+ type: QueryTypes.INSERT
81
+ });
82
+ }
@@ -0,0 +1,23 @@
1
+
2
+ module.exports = {
3
+ addSeconds: _addSeconds,
4
+ addDays: _addDays
5
+ }
6
+
7
+ function _addSeconds(
8
+ date=null,
9
+ seconds=0
10
+ ) {
11
+ const newDate = new Date(date.valueOf());
12
+ newDate.setSeconds(newDate.getSeconds() + seconds);
13
+ return newDate;
14
+ }
15
+
16
+ function _addDays(
17
+ date=null,
18
+ days=0
19
+ ) {
20
+ const newDate = new Date(date.valueOf());
21
+ newDate.setDate(newDate.getDate() + days);
22
+ return newDate;
23
+ }
@@ -0,0 +1,22 @@
1
+ // Utils.
2
+ const formidable = require('formidable');
3
+
4
+
5
+ module.exports = {
6
+ parseRequestForm: _parseRequestForm,
7
+ }
8
+
9
+ function _parseRequestForm(req) {
10
+ return new Promise(function (resolve, reject) {
11
+ const form = new formidable.IncomingForm();
12
+
13
+ form.parse(req, function (err, fields, files) {
14
+ if (err) {
15
+ return reject(err);
16
+ }
17
+
18
+ const result = { fields, files };
19
+ return resolve(result);
20
+ });
21
+ });
22
+ }