delore-crm-core 1.0.0 → 1.0.3
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/README.md +155 -43
- package/index.js +26 -3
- package/package.json +16 -4
- package/access.js +0 -144
- package/util.js +0 -19
package/README.md
CHANGED
|
@@ -1,81 +1,193 @@
|
|
|
1
1
|
# delore-crm-core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
registration, access metadata, and a few shared helpers.
|
|
3
|
+
Biblioteca central de utilidades do CRM da Delore. Reúne registro de rotas e acesso, helpers compartilhados, autenticação/token, sincronismo de permissões, conexão com banco (MySQL + MongoDB), modelos e utilidades de impressão (PDF/Excel).
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## Instalação
|
|
7
6
|
|
|
8
7
|
```bash
|
|
9
8
|
npm i delore-crm-core
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
##
|
|
11
|
+
## Uso rápido (rotas e acesso)
|
|
13
12
|
|
|
14
13
|
```js
|
|
15
|
-
const express = require(
|
|
16
|
-
const core = require(
|
|
14
|
+
const express = require("express");
|
|
15
|
+
const core = require("delore-crm-core");
|
|
17
16
|
|
|
18
17
|
const app = express();
|
|
19
18
|
|
|
20
|
-
core.add(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
core.const.GROUPOPE,
|
|
26
|
-
(req, res) => {
|
|
27
|
-
res.json({ ok: true });
|
|
28
|
-
}
|
|
19
|
+
core.access.add(
|
|
20
|
+
"get",
|
|
21
|
+
"/s/exemplo",
|
|
22
|
+
"Exemplo",
|
|
23
|
+
"Rota de exemplo",
|
|
24
|
+
core.access.const.GROUPOPE,
|
|
25
|
+
(req, res) => res.json({ ok: true }),
|
|
29
26
|
);
|
|
30
27
|
|
|
31
|
-
app.use(core.makeRouter());
|
|
28
|
+
app.use(core.access.makeRouter());
|
|
32
29
|
app.listen(3000);
|
|
33
30
|
```
|
|
34
31
|
|
|
35
|
-
##
|
|
32
|
+
## Exportações principais
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
O módulo principal exporta:
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
- `access`: registro de rotas, metadados de acesso e criação de `router`.
|
|
37
|
+
- `util`: helpers de data, máscara, número, telefone, validações e afins.
|
|
38
|
+
- `logger`: logger baseado em `pino`/`pino-pretty`.
|
|
39
|
+
- `memory`: cache simples em memória (chave/valor).
|
|
40
|
+
- `print`: geração de PDF e Excel.
|
|
41
|
+
- `authControl`: controle de segredo de autenticação (MongoDB + cache em memória).
|
|
42
|
+
- `ret`: helpers de retorno e geração/validação de tokens.
|
|
43
|
+
- `validRequest`: middlewares de validação de token.
|
|
44
|
+
- `syncAccess`: sincronismo de acessos com o banco.
|
|
45
|
+
- `sequelizeDB`: conexão com MySQL (Sequelize) + MongoDB.
|
|
46
|
+
- `models`: helpers de models, sync, migration e utilidades.
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
Exemplo de import:
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- `makeAccessScope()` returns JSON string of access IDs
|
|
53
|
-
- `makeAccessScopeAll()` returns JSON string of `id#name#desc#group#isMenu`
|
|
54
|
-
- `getResourceFromReq(req)` maps a request to its access metadata
|
|
50
|
+
```js
|
|
51
|
+
const { access, util, logger } = require("delore-crm-core");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Access (rotas e ACL)
|
|
55
|
+
|
|
56
|
+
Registra rotas e mantém os metadados de acesso em memória. Depois monta um `router` do Express.
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
Funções principais:
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
- `access.add(method, path, name, desc, group, fn)`
|
|
61
|
+
- `access.addMenu(method, path, name, desc, group, fn)`
|
|
62
|
+
- `access.addOpen(method, path, name, desc, group, fn)`
|
|
63
|
+
- `access.addNoAccessControl(method, path, fn)`
|
|
64
|
+
- `access.addEsp(name, desc, group)`
|
|
65
|
+
- `access.makeRouter()`
|
|
66
|
+
- `access.getAccess()`
|
|
67
|
+
- `access.makeAccessScope()`
|
|
68
|
+
- `access.makeAccessScopeAll()`
|
|
69
|
+
- `access.getResourceFromReq(req)`
|
|
70
|
+
|
|
71
|
+
Constantes de grupos:
|
|
59
72
|
|
|
60
73
|
- `GROUPSYS`, `GROUPCAD`, `GROUPOPE`, `GROUPUTL`, `GROUPESP`, `GROUPCON`, `GROUPADM`
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
## Auth e validação
|
|
76
|
+
|
|
77
|
+
### Tokens e respostas
|
|
63
78
|
|
|
64
|
-
`
|
|
79
|
+
O módulo `ret` centraliza geração de token e respostas padronizadas:
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
- `ret.returnLogin(...)`, `ret.returnLoginADM(...)`, `ret.returnLoginMASTER(...)`, `ret.returnLoginDEVELOPER(...)`
|
|
82
|
+
- `ret.validToken(req)`, `ret.validTokenADM(req)`, `ret.validTokenMASTER(req)`, `ret.validTokenDEVELOPER(req)`
|
|
83
|
+
- `ret.makeTokenSocketIo(...)`, `ret.validTokenSocketIo(...)`
|
|
84
|
+
- `ret.returnTokenMsg(...)`, `ret.validTokenMSG(req)`
|
|
85
|
+
- `ret.makeTokenForRememberUser(...)`, `ret.validTokenForRememberUser(token)`
|
|
67
86
|
|
|
68
|
-
|
|
87
|
+
### Middlewares
|
|
69
88
|
|
|
70
89
|
```js
|
|
71
|
-
const
|
|
72
|
-
|
|
90
|
+
const { validRequest } = require("delore-crm-core");
|
|
91
|
+
|
|
92
|
+
app.use("/s", validRequest.validaRequestSystem);
|
|
93
|
+
app.use("/adm", validRequest.validaRequestADM);
|
|
73
94
|
```
|
|
74
95
|
|
|
75
|
-
##
|
|
96
|
+
## Conexão com banco (MySQL + MongoDB)
|
|
76
97
|
|
|
77
|
-
|
|
98
|
+
O `sequelizeDB` conecta ao MySQL via Sequelize e ao MongoDB (coleção `authControl`), carregando secrets em memória.
|
|
78
99
|
|
|
79
100
|
```js
|
|
80
|
-
const
|
|
101
|
+
const { sequelizeDB } = require("delore-crm-core");
|
|
102
|
+
|
|
103
|
+
sequelizeDB.init(() => {
|
|
104
|
+
console.log("DB conectado");
|
|
105
|
+
});
|
|
81
106
|
```
|
|
107
|
+
|
|
108
|
+
Variáveis de ambiente usadas:
|
|
109
|
+
|
|
110
|
+
- `DB_DATABASE`, `DB_USER`, `DB_PASS`, `DB_HOST`
|
|
111
|
+
- `MONGODB_URI`
|
|
112
|
+
- `SECRET_LOGIN_USER`, `SECRET_REMEMBER_USER`
|
|
113
|
+
- `PINO_LOG_LEVEL`
|
|
114
|
+
- `TELEGRAM_BOT_TOKEN_NOTIFY`, `TELEGRAM_BOT_ENV_NOTIFY`
|
|
115
|
+
|
|
116
|
+
## Models e sincronismo
|
|
117
|
+
|
|
118
|
+
O módulo `models` dá suporte para:
|
|
119
|
+
|
|
120
|
+
- registrar models: `addModel`, `addModelIn`, `addModelInOne`
|
|
121
|
+
- sincronizar: `initSync(callback)`
|
|
122
|
+
- migration: `Migration()`
|
|
123
|
+
- exportar modelos para front: `ExportModels()`
|
|
124
|
+
- utilitários: `canDelete`, `canDelete2`, `makeSQLTransf`, `seedFrom`
|
|
125
|
+
|
|
126
|
+
O módulo `syncAccess` sincroniza a lista de acessos registrados em memória com a tabela `access`.
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const { syncAccess } = require("delore-crm-core");
|
|
130
|
+
|
|
131
|
+
syncAccess.init("crm-service");
|
|
132
|
+
await syncAccess.sync();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Print (PDF e Excel)
|
|
136
|
+
|
|
137
|
+
Gera relatórios em PDF (tabela/linhas) e exporta para Excel.
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
const { print } = require("delore-crm-core");
|
|
141
|
+
|
|
142
|
+
const report = print.getPrint(req, res);
|
|
143
|
+
report.addColumns("Nome", "name", "L", 30);
|
|
144
|
+
report.addColumVal("Valor", "amount", "R", 12, 2);
|
|
145
|
+
await report.printList(data, req, res);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Para Excel:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
await report.printListEXCEL(data, req, res);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Util, Logger e Memory
|
|
155
|
+
|
|
156
|
+
### Util
|
|
157
|
+
|
|
158
|
+
Helpers variados: datas (timezone America/Sao_Paulo), máscaras, números, telefone, validação de CPF, geração de schema JSON, etc.
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const { util } = require("delore-crm-core");
|
|
162
|
+
|
|
163
|
+
const today = util.nowDateTime();
|
|
164
|
+
const cpfOk = util.isValidCPF("00000000000");
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Logger
|
|
168
|
+
|
|
169
|
+
Logger configurado com `pino-pretty`:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
const { logger } = require("delore-crm-core");
|
|
173
|
+
|
|
174
|
+
logger.info("serviço iniciado");
|
|
175
|
+
logger.setLevel("info");
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Memory
|
|
179
|
+
|
|
180
|
+
Cache simples em memória:
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
const { memory } = require("delore-crm-core");
|
|
184
|
+
|
|
185
|
+
await memory.addMemory("chave", { x: 1 });
|
|
186
|
+
const value = await memory.findMemory("chave");
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Observações
|
|
190
|
+
|
|
191
|
+
- O módulo é CommonJS.
|
|
192
|
+
- Os helpers de acesso dependem do registro das rotas antes do `makeRouter()`.
|
|
193
|
+
- Tokens e segredos usam dados do MongoDB (`authControl`).
|
package/index.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const access = require('./access');
|
|
4
|
-
const util = require('./util');
|
|
3
|
+
const access = require('./utils/access');
|
|
4
|
+
const util = require('./utils/util');
|
|
5
|
+
const logger = require('./utils/logger');
|
|
6
|
+
const memory = require('./utils/memory');
|
|
7
|
+
const print = require('./print');
|
|
8
|
+
const authControl = require('./auth/auth.control');
|
|
9
|
+
const ret = require('./auth/ret');
|
|
10
|
+
const validRequest = require('./auth/valid.request');
|
|
11
|
+
const syncAccess = require('./sync/sync.access');
|
|
12
|
+
const sequelizeDB = require('./db/sequelize_db');
|
|
13
|
+
const models = require('./db/models');
|
|
5
14
|
|
|
6
|
-
|
|
15
|
+
const exported = Object.assign({}, {
|
|
16
|
+
access,
|
|
17
|
+
util,
|
|
18
|
+
logger,
|
|
19
|
+
memory,
|
|
20
|
+
print,
|
|
21
|
+
authControl,
|
|
22
|
+
ret,
|
|
23
|
+
validRequest,
|
|
24
|
+
syncAccess,
|
|
25
|
+
sequelizeDB,
|
|
26
|
+
models,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
module.exports = exported;
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delore-crm-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Core CRM utilities for Delore projects",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./index.js",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"./*": "./*.js",
|
|
10
|
+
"./*/": "./*/index.js",
|
|
11
|
+
"./*/*": "./*/*.js"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"index.js",
|
|
@@ -45,6 +46,17 @@
|
|
|
45
46
|
"license": "ISC",
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"express": "^5.2.1",
|
|
48
|
-
"
|
|
49
|
+
"get-stream": "^9.0.1",
|
|
50
|
+
"jwt-simple": "^0.5.5",
|
|
51
|
+
"moment": "^2.29.2",
|
|
52
|
+
"moment-timezone": "^0.6.0",
|
|
53
|
+
"mongodb": "^6.13.1",
|
|
54
|
+
"mysql2": "^3.3.3",
|
|
55
|
+
"pdfkit": "^0.17.2",
|
|
56
|
+
"pino": "^10.3.0",
|
|
57
|
+
"pino-pretty": "^13.1.3",
|
|
58
|
+
"shorthash2": "^1.0.5",
|
|
59
|
+
"sequelize": "^6.37.7",
|
|
60
|
+
"xlsx": "^0.18.5"
|
|
49
61
|
}
|
|
50
62
|
}
|
package/access.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const express = require('express');
|
|
3
|
-
const router = express.Router();
|
|
4
|
-
const shorthash2 = require('shorthash2');
|
|
5
|
-
|
|
6
|
-
const util = require('./util');
|
|
7
|
-
var list = [];
|
|
8
|
-
|
|
9
|
-
exports.const = {
|
|
10
|
-
GROUPSYS: 'Sistema',
|
|
11
|
-
GROUPCAD: 'Cadastros',
|
|
12
|
-
GROUPOPE: 'Operacional',
|
|
13
|
-
GROUPUTL: 'Utils',
|
|
14
|
-
GROUPESP: 'Especiais',
|
|
15
|
-
GROUPCON: 'Consultas',
|
|
16
|
-
GROUPADM: 'Gerencial',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Configura acesso para rotas/todos usuários
|
|
20
|
-
exports.add = function (method, path, name, desc, group, fn) {
|
|
21
|
-
list.push({ service: process.env.SERVICE_NAME, id: shorthash2(method + path), type: 'router', accessControl: true, method: method, path: path, name: name, desc: desc, group: group, fn: fn, general: false, opened: false, isMenu: false });
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
exports.addMenu = function (method, path, name, desc, group, fn) {
|
|
25
|
-
list.push({ service: process.env.SERVICE_NAME, id: shorthash2(method + path), type: 'router', accessControl: true, method: method, path: path, name: name, desc: desc, group: group, fn: fn, general: false, opened: false, isMenu: true });
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Configura acesso para rotas/todos usuários - Liberada por Padrão para usuário ativos já cadastrados
|
|
29
|
-
exports.addOpen = function (method, path, name, desc, group, fn) {
|
|
30
|
-
list.push({ service: process.env.SERVICE_NAME, id: shorthash2(method + path), type: 'router', accessControl: true, method: method, path: path, name: name, desc: desc, group: group, fn: fn, general: false, opened: true, isMenu: false });
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// Configura acesso para rotinas
|
|
34
|
-
exports.addEsp = function (name, desc, group) {
|
|
35
|
-
var id = shorthash2(name, desc, group);
|
|
36
|
-
list.push({ service: process.env.SERVICE_NAME, id: id, type: 'esp', accessControl: true, method: 'esp', path: '', name: name, desc: desc, group: group, fn: null, general: false, opened: false });
|
|
37
|
-
return id;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Configura acesso para rotas liberadas para todos usuários
|
|
41
|
-
exports.addNoAccessControl = function (method, path, fn) {
|
|
42
|
-
list.push({ service: process.env.SERVICE_NAME, id: shorthash2(method + path), type: 'router', accessControl: false, method: method, path: path, name: '', desc: '', group: '', fn: fn, general: false, opened: false, isMenu: false });
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
exports.makeRouter = function () {
|
|
46
|
-
for (var i = 0; i < list.length; i++) {
|
|
47
|
-
if (list[i].method == 'get') {
|
|
48
|
-
router.get(list[i].path, list[i].fn);
|
|
49
|
-
} else if (list[i].method == 'post') {
|
|
50
|
-
router.post(list[i].path, list[i].fn);
|
|
51
|
-
} else if (list[i].method == 'put') {
|
|
52
|
-
router.put(list[i].path, list[i].fn);
|
|
53
|
-
} else if (list[i].method == 'delete') {
|
|
54
|
-
router.delete(list[i].path, list[i].fn);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return router;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
exports.makeAccessScope = function () {
|
|
61
|
-
var ret = '[';
|
|
62
|
-
for (var i = 0; i < list.length; i++) {
|
|
63
|
-
if (list[i].accessControl) {
|
|
64
|
-
if (i == 0) {
|
|
65
|
-
ret = ret + '"' + list[i].id + '"';
|
|
66
|
-
} else {
|
|
67
|
-
ret = ret + ', ' + '"' + list[i].id + '"';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
ret = ret + ']';
|
|
72
|
-
return ret;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
exports.makeAccessScopeAll = function () {
|
|
76
|
-
var ret = '[';
|
|
77
|
-
for (var i = 0; i < list.length; i++) {
|
|
78
|
-
if (list[i].accessControl) {
|
|
79
|
-
if (i == 0) {
|
|
80
|
-
ret = ret + '"' + list[i].id + '#' + list[i].name + '#' + list[i].desc + '#' + list[i].group + '#' + list[i].isMenu + '"';
|
|
81
|
-
} else {
|
|
82
|
-
ret = ret + ', ' + '"' + list[i].id + '#' + list[i].name + '#' + list[i].desc + '#' + list[i].group + '#' + list[i].isMenu + '"';
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
ret = ret + ']';
|
|
87
|
-
return ret;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
exports.getAccess = function () {
|
|
91
|
-
return list;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
exports.getResourceFromReq = function (req) {
|
|
95
|
-
var ret = {};
|
|
96
|
-
var urlReq = util.getUrlFromReq(req.baseUrl);
|
|
97
|
-
|
|
98
|
-
if (urlReq == '') {
|
|
99
|
-
ret.accessControl = false;
|
|
100
|
-
ret.id = '';
|
|
101
|
-
ret.group = '';
|
|
102
|
-
ret.name = '';
|
|
103
|
-
ret.desc = '';
|
|
104
|
-
ret.general = false;
|
|
105
|
-
return ret;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (var i = 0; i < list.length; i++) {
|
|
109
|
-
if (list[i].accessControl) {
|
|
110
|
-
ret.accessControl = true;
|
|
111
|
-
var urls = getUrlFromReq(list[i].path);
|
|
112
|
-
if (urlReq == urls && list[i].method == req.method.toLowerCase()) {
|
|
113
|
-
ret.id = list[i].id;
|
|
114
|
-
ret.group = list[i].group;
|
|
115
|
-
ret.name = list[i].name;
|
|
116
|
-
ret.desc = list[i].desc;
|
|
117
|
-
ret.general = list[i].general;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
} else {
|
|
121
|
-
ret.accessControl = false;
|
|
122
|
-
ret.id = '';
|
|
123
|
-
ret.group = '';
|
|
124
|
-
ret.name = '';
|
|
125
|
-
ret.desc = '';
|
|
126
|
-
ret.general = false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return ret;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
function getUrlFromReq(baseUrl) {
|
|
133
|
-
var urlReq = '';
|
|
134
|
-
if (baseUrl.indexOf('/s/') > -1) {
|
|
135
|
-
urlReq = baseUrl.substring(0, baseUrl.indexOf('/s/') + 3);
|
|
136
|
-
} else {
|
|
137
|
-
var partes = baseUrl.split('/s');
|
|
138
|
-
for (let index = 0; index < partes.length - 1; index++) {
|
|
139
|
-
const element = partes[index];
|
|
140
|
-
urlReq = urlReq + '' + element + '/s';
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return urlReq;
|
|
144
|
-
}
|
package/util.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function getUrlFromReq(baseUrl) {
|
|
4
|
-
var urlReq = '';
|
|
5
|
-
if (baseUrl.indexOf('/s/') > -1) {
|
|
6
|
-
urlReq = baseUrl.substring(0, baseUrl.indexOf('/s/') + 3);
|
|
7
|
-
} else {
|
|
8
|
-
var partes = baseUrl.split('/s');
|
|
9
|
-
for (let index = 0; index < partes.length - 1; index++) {
|
|
10
|
-
const element = partes[index];
|
|
11
|
-
urlReq = urlReq + '' + element + '/s';
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return urlReq;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = {
|
|
18
|
-
getUrlFromReq,
|
|
19
|
-
};
|