@adaptivestone/framework 5.0.0-alpha.2 → 5.0.0-alpha.21

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 (54) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/commands/CreateUser.js +3 -1
  3. package/commands/GenerateRandomBytes.js +15 -0
  4. package/commands/migration/Migrate.js +1 -1
  5. package/config/auth.js +5 -1
  6. package/config/ipDetector.js +14 -0
  7. package/controllers/Auth.js +2 -2
  8. package/controllers/Home.js +1 -1
  9. package/folderConfig.js +0 -1
  10. package/helpers/files.js +8 -8
  11. package/jsconfig.json +9 -0
  12. package/models/User.js +7 -1
  13. package/modules/AbstractCommand.js +2 -1
  14. package/modules/AbstractController.js +22 -18
  15. package/modules/AbstractModel.d.ts +48 -0
  16. package/modules/AbstractModel.js +20 -2
  17. package/modules/Base.d.ts +5 -4
  18. package/modules/Base.js +11 -1
  19. package/package.json +13 -16
  20. package/server.d.ts +7 -5
  21. package/server.js +11 -7
  22. package/services/cache/Cache.d.ts +2 -2
  23. package/services/cache/Cache.js +10 -6
  24. package/services/http/HttpServer.js +12 -20
  25. package/services/http/middleware/GetUserByToken.js +3 -2
  26. package/services/http/middleware/I18n.js +20 -21
  27. package/services/http/middleware/IpDetector.js +59 -0
  28. package/services/http/middleware/Pagination.js +3 -2
  29. package/services/http/middleware/RateLimiter.js +8 -2
  30. package/services/http/middleware/RequestLogger.js +3 -3
  31. package/services/http/middleware/RequestParser.js +5 -2
  32. package/services/messaging/email/index.js +7 -15
  33. package/services/validate/ValidateService.js +1 -1
  34. package/tests/setup.js +3 -1
  35. package/tests/setupVitest.js +1 -1
  36. package/types/TFoldersConfig.d.ts +0 -2
  37. package/.eslintrc.cjs +0 -41
  38. package/controllers/Auth.test.js +0 -451
  39. package/controllers/Home.test.js +0 -12
  40. package/models/Migration.test.js +0 -20
  41. package/models/Sequence.test.js +0 -43
  42. package/models/User.test.js +0 -143
  43. package/modules/Modules.test.js +0 -18
  44. package/services/cache/Cache.test.js +0 -81
  45. package/services/http/middleware/Auth.test.js +0 -57
  46. package/services/http/middleware/Cors.test.js +0 -147
  47. package/services/http/middleware/GetUserByToken.test.js +0 -108
  48. package/services/http/middleware/I18n.test.js +0 -96
  49. package/services/http/middleware/PrepareAppInfo.test.js +0 -26
  50. package/services/http/middleware/RateLimiter.test.js +0 -233
  51. package/services/http/middleware/RequestParser.test.js +0 -112
  52. package/services/http/middleware/Role.test.js +0 -93
  53. package/services/http/middleware/StaticFiles.js +0 -59
  54. package/services/validate/ValidateService.test.js +0 -107
@@ -1,233 +0,0 @@
1
- import { setTimeout } from 'node:timers/promises';
2
- import crypto from 'node:crypto';
3
- import { beforeAll, afterAll, describe, it, expect } from 'vitest';
4
-
5
- import RateLimiter from './RateLimiter.js';
6
-
7
- let mongoRateLimiter;
8
-
9
- describe('rate limiter methods', () => {
10
- beforeAll(async () => {
11
- await setTimeout(20);
12
-
13
- mongoRateLimiter = new RateLimiter(global.server.app, {
14
- driver: 'mongo',
15
- limiterOptions: {
16
- keyPrefix: `mongo_${Date.now()}_${crypto.randomUUID()}}`,
17
- },
18
- });
19
- });
20
-
21
- afterAll(async () => {
22
- // we need to wait because redis mongo ask mongo to create indexes
23
- await setTimeout(200);
24
- });
25
- it('have description fields', async () => {
26
- expect.assertions(1);
27
- const middleware = new RateLimiter(global.server.app, {
28
- driver: 'redis',
29
- });
30
- expect(middleware.constructor.description).toBeDefined();
31
- });
32
-
33
- it('can create redis rateLimiter', async () => {
34
- expect.assertions(1);
35
-
36
- const redisRateLimiter = new RateLimiter(global.server.app, {
37
- driver: 'redis',
38
- });
39
-
40
- expect(redisRateLimiter.limiter).toBeDefined();
41
- });
42
-
43
- it('can not create rateLimiter with unknown driver', async () => {
44
- expect.assertions(1);
45
-
46
- const rateLimiter = new RateLimiter(global.server.app, {
47
- driver: 'unknown',
48
- });
49
-
50
- expect(rateLimiter.limiter).toBeNull();
51
- });
52
-
53
- it('generateConsumeKey works correctly', async () => {
54
- expect.assertions(1);
55
-
56
- const redisRateLimiter = new RateLimiter(global.server.app, {
57
- driver: 'redis',
58
- });
59
-
60
- const res = await redisRateLimiter.gerenateConsumeKey({
61
- ip: '192.168.0.0',
62
- appInfo: {
63
- user: {
64
- id: 'someId',
65
- },
66
- },
67
- });
68
-
69
- expect(res).toBe('192.168.0.0__someId');
70
- });
71
-
72
- it('generateConsumeKey with request works correctly', async () => {
73
- expect.assertions(1);
74
-
75
- const redisRateLimiter = new RateLimiter(global.server.app, {
76
- driver: 'redis',
77
- consumeKeyComponents: {
78
- request: ['email'],
79
- },
80
- });
81
-
82
- const res = await redisRateLimiter.gerenateConsumeKey({
83
- ip: '192.168.0.0',
84
- body: {
85
- email: 'foo@example.com',
86
- },
87
- });
88
-
89
- expect(res).toBe('192.168.0.0__foo@example.com');
90
- });
91
-
92
- it('middleware without driver should fail', async () => {
93
- expect.assertions(2);
94
- const rateLimiter = new RateLimiter(global.server.app, {
95
- driver: 'unknown',
96
- });
97
- const req = {
98
- appInfo: {},
99
- };
100
- let status;
101
- let isSend;
102
- await rateLimiter.middleware(
103
- req,
104
- {
105
- status(statusCode) {
106
- status = statusCode;
107
- return this;
108
- },
109
- json() {
110
- isSend = true;
111
- },
112
- },
113
- () => {},
114
- );
115
- expect(status).toBe(500);
116
- expect(isSend).toBeTruthy();
117
- });
118
-
119
- const makeOneRequest = async ({ rateLimiter, driver, request }) => {
120
- let realRateLimiter = rateLimiter;
121
- if (!realRateLimiter) {
122
- realRateLimiter = new RateLimiter(global.server.app, {
123
- driver,
124
- });
125
- }
126
- const req = {
127
- appInfo: {},
128
- ...request,
129
- };
130
- let status;
131
- let isSend = false;
132
- let isNextCalled = false;
133
- await realRateLimiter.middleware(
134
- req,
135
- {
136
- status(statusCode) {
137
- status = statusCode;
138
- return this;
139
- },
140
- json() {
141
- isSend = true;
142
- },
143
- },
144
- () => {
145
- isNextCalled = true;
146
- },
147
- );
148
- return { status, isSend, isNextCalled };
149
- };
150
-
151
- it('middleware should works with a mongo drivers', async () => {
152
- expect.assertions(1);
153
- const { isNextCalled } = await makeOneRequest({
154
- rateLimiter: mongoRateLimiter,
155
- request: { ip: '10.10.0.1' },
156
- });
157
- expect(isNextCalled).toBeTruthy();
158
- });
159
-
160
- it('middleware should works with a memory drivers', async () => {
161
- expect.assertions(1);
162
- const { isNextCalled } = await makeOneRequest({
163
- driver: 'memory',
164
- request: { ip: '10.10.0.1' },
165
- });
166
- expect(isNextCalled).toBeTruthy();
167
- });
168
-
169
- it('middleware should works with a redis drivers', async () => {
170
- expect.assertions(1);
171
- const { isNextCalled } = await makeOneRequest({
172
- driver: 'redis',
173
- request: { ip: '10.10.0.1' },
174
- });
175
- expect(isNextCalled).toBeTruthy();
176
- });
177
-
178
- it('middleware should rate limits for us. mongo driver', async () => {
179
- expect.assertions(2);
180
-
181
- const middlewares = Array.from({ length: 20 }, () =>
182
- makeOneRequest({ rateLimiter: mongoRateLimiter }),
183
- );
184
-
185
- const data = await Promise.all(middlewares);
186
-
187
- const status = data.find((obj) => obj.status === 429);
188
- const isSend = data.find((obj) => obj.isSend);
189
-
190
- expect(status.status).toBe(429);
191
- expect(isSend.isSend).toBeTruthy();
192
- });
193
-
194
- it('middleware should rate limits for us. memory driver', async () => {
195
- expect.assertions(2);
196
-
197
- const rateLimiter = new RateLimiter(global.server.app, {
198
- driver: 'memory',
199
- });
200
-
201
- const middlewares = Array.from({ length: 20 }, () =>
202
- makeOneRequest({ rateLimiter }),
203
- );
204
-
205
- const data = await Promise.all(middlewares);
206
-
207
- const status = data.find((obj) => obj.status === 429);
208
- const isSend = data.find((obj) => obj.isSend);
209
-
210
- expect(status.status).toBe(429);
211
- expect(isSend.isSend).toBeTruthy();
212
- });
213
-
214
- it('middleware should rate limits for us. redis driver', async () => {
215
- expect.assertions(2);
216
-
217
- const rateLimiter = new RateLimiter(global.server.app, {
218
- driver: 'redis',
219
- });
220
-
221
- const middlewares = Array.from({ length: 20 }, () =>
222
- makeOneRequest({ rateLimiter }),
223
- );
224
-
225
- const data = await Promise.all(middlewares);
226
-
227
- const status = data.find((obj) => obj.status === 429);
228
- const isSend = data.find((obj) => obj.isSend);
229
-
230
- expect(status.status).toBe(429);
231
- expect(isSend.isSend).toBeTruthy();
232
- });
233
- });
@@ -1,112 +0,0 @@
1
- import { createServer } from 'node:http';
2
- import { describe, it, expect } from 'vitest';
3
- import { PersistentFile } from 'formidable';
4
-
5
- import RequestParser from './RequestParser.js';
6
-
7
- describe('reqest parser limiter methods', () => {
8
- it('have description fields', async () => {
9
- expect.assertions(1);
10
- const middleware = new RequestParser(global.server.app);
11
- expect(middleware.constructor.description).toBeDefined();
12
- });
13
- it('middleware that works', async () => {
14
- expect.assertions(4);
15
-
16
- await new Promise((done) => {
17
- // from https://github.com/node-formidable/formidable/blob/master/test-node/standalone/promise.test.js
18
-
19
- const server = createServer(async (req, res) => {
20
- req.appInfo = {};
21
- const middleware = new RequestParser(global.server.app);
22
- middleware.middleware(req, {}, (err) => {
23
- expect(err).toBeUndefined();
24
- expect(req.body.title).toBeDefined();
25
- expect(req.body.multipleFiles).toBeDefined();
26
- expect(
27
- req.body.multipleFiles[0] instanceof PersistentFile,
28
- ).toBeTruthy();
29
-
30
- res.writeHead(200);
31
- res.end('ok');
32
- });
33
- });
34
- server.listen(null, async () => {
35
- const chosenPort = server.address().port;
36
- const body = `----13068458571765726332503797717\r
37
- Content-Disposition: form-data; name="title"\r
38
- \r
39
- a\r
40
- ----13068458571765726332503797717\r
41
- Content-Disposition: form-data; name="multipleFiles"; filename="x.txt"\r
42
- Content-Type: application/x-javascript\r
43
- \r
44
- \r
45
- \r
46
- a\r
47
- b\r
48
- c\r
49
- d\r
50
- \r
51
- ----13068458571765726332503797717--\r
52
- `;
53
- await fetch(String(new URL(`http:localhost:${chosenPort}/`)), {
54
- method: 'POST',
55
-
56
- headers: {
57
- 'Content-Length': body.length,
58
- Host: `localhost:${chosenPort}`,
59
- 'Content-Type':
60
- 'multipart/form-data; boundary=--13068458571765726332503797717',
61
- },
62
- body,
63
- }).catch((err) => {
64
- console.error(err);
65
- done(err);
66
- });
67
- server.close(() => {
68
- done();
69
- });
70
- });
71
- });
72
- });
73
- it('middleware with a problem', async () => {
74
- expect.assertions(1);
75
-
76
- await new Promise((done) => {
77
- // from https://github.com/node-formidable/formidable/blob/master/test-node/standalone/promise.test.js
78
-
79
- const server = createServer(async (req, res) => {
80
- req.appInfo = {};
81
- const middleware = new RequestParser(global.server.app);
82
- middleware.middleware(req, {}, (err) => {
83
- expect(err).toBeDefined();
84
-
85
- res.writeHead(200);
86
- res.end('ok');
87
- });
88
- });
89
- server.listen(null, async () => {
90
- const chosenPort = server.address().port;
91
- const body = 'someBadBody';
92
-
93
- await fetch(String(new URL(`http:localhost:${chosenPort}/`)), {
94
- method: 'POST',
95
-
96
- headers: {
97
- 'Content-Length': body.length,
98
- Host: `localhost:${chosenPort}`,
99
- 'Content-Type': 'badContentType',
100
- },
101
- body,
102
- }).catch((err) => {
103
- console.error(err);
104
- done(err);
105
- });
106
- server.close(() => {
107
- done();
108
- });
109
- });
110
- });
111
- });
112
- });
@@ -1,93 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import Role from './Role.js';
3
-
4
- describe('role middleware methods', () => {
5
- it('have description fields', async () => {
6
- expect.assertions(1);
7
- const middleware = new Role(global.server.app);
8
- expect(middleware.constructor.description).toBeDefined();
9
- });
10
-
11
- it('middleware pass when user presented with a right role', async () => {
12
- expect.assertions(1);
13
- let isCalled = false;
14
- const nextFunction = () => {
15
- isCalled = true;
16
- };
17
- const req = {
18
- appInfo: {
19
- user: {
20
- roles: ['role1', 'role2'],
21
- },
22
- },
23
- };
24
- const middleware = new Role(global.server.app, {
25
- roles: ['admin', 'role1'],
26
- });
27
-
28
- await middleware.middleware(req, {}, nextFunction);
29
- expect(isCalled).toBeTruthy();
30
- });
31
-
32
- it('middleware NOT pass when user NOT presented', async () => {
33
- expect.assertions(3);
34
- let isCalled = false;
35
- let status;
36
- let isSend;
37
- const nextFunction = () => {
38
- isCalled = true;
39
- };
40
- const req = {
41
- appInfo: {}, // no user
42
- };
43
- const middleware = new Role(global.server.app);
44
- await middleware.middleware(
45
- req,
46
- {
47
- status(statusCode) {
48
- status = statusCode;
49
- return this;
50
- },
51
- json() {
52
- isSend = true;
53
- },
54
- },
55
- nextFunction,
56
- );
57
- expect(isCalled).toBeFalsy();
58
- expect(status).toBe(401);
59
- expect(isSend).toBeTruthy();
60
- });
61
-
62
- it('middleware NOT pass when user have a wrong role', async () => {
63
- expect.assertions(3);
64
- let isCalled = false;
65
- let status;
66
- let isSend;
67
- const nextFunction = () => {
68
- isCalled = true;
69
- };
70
- const req = {
71
- appInfo: {
72
- user: { roles: ['role1', 'role2'] },
73
- },
74
- };
75
- const middleware = new Role(global.server.app, { roles: ['admin'] });
76
- await middleware.middleware(
77
- req,
78
- {
79
- status(statusCode) {
80
- status = statusCode;
81
- return this;
82
- },
83
- json() {
84
- isSend = true;
85
- },
86
- },
87
- nextFunction,
88
- );
89
- expect(isCalled).toBeFalsy();
90
- expect(status).toBe(403);
91
- expect(isSend).toBeTruthy();
92
- });
93
- });
@@ -1,59 +0,0 @@
1
- import fsPromises from 'node:fs/promises';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import mime from 'mime';
5
- import AbstractMiddleware from './AbstractMiddleware.js';
6
- /**
7
- * Middleware for static files
8
- */
9
- class StaticFiles extends AbstractMiddleware {
10
- constructor(app, params) {
11
- super(app);
12
- this.params = params;
13
- if (!params || !params.folders || !params.folders.length) {
14
- throw new Error('StaticFiles inited without folders config');
15
- }
16
- }
17
-
18
- static get description() {
19
- return 'Static file server middleware. Host you static files from public foolder. Mostly for dev.';
20
- }
21
-
22
- async middleware(req, res, next) {
23
- if (req.method !== 'GET') {
24
- // only get supported
25
- return next();
26
- }
27
- const { folders } = this.params;
28
-
29
- const promises = [];
30
-
31
- for (const f of folders) {
32
- const filePath = path.join(f, req.url);
33
- promises.push(
34
- fsPromises
35
- .stat(filePath)
36
- .catch(() => {
37
- // nothing there, file just not exists
38
- })
39
- .then((stats) => ({ stats, file: filePath })),
40
- );
41
- }
42
-
43
- const fileStats = await Promise.all(promises);
44
-
45
- for (const fileStat of fileStats) {
46
- if (fileStat.stats && fileStat.stats.isFile()) {
47
- const contentType = mime.getType(fileStat.file);
48
- const fileStream = fs.createReadStream(fileStat.file);
49
- res.set('Content-Type', contentType);
50
- fileStream.pipe(res);
51
- return null;
52
- }
53
- }
54
-
55
- return next();
56
- }
57
- }
58
-
59
- export default StaticFiles;
@@ -1,107 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- import yup from 'yup';
4
- import ValidateService from './ValidateService.js';
5
- import YupValidator from './drivers/YupValidator.js';
6
- import CustomValidator from './drivers/CustomValidator.js';
7
-
8
- describe('validate service', () => {
9
- describe('validateSchema funtion', () => {
10
- const data = {
11
- name: '1213123123',
12
- };
13
- const req = {};
14
-
15
- it('returns an empty object if no validator is provided', async () => {
16
- expect.assertions(1);
17
- const result = await new ValidateService(
18
- global.server.app,
19
- new YupValidator(
20
- global.server.app,
21
- yup.object().shape({ name: '123' }),
22
- ),
23
- ).validateSchema(req, undefined, data);
24
- expect(result).toStrictEqual({});
25
- });
26
-
27
- it('calls validateFields and castFields if validator is provided', async () => {
28
- expect.assertions(1);
29
- const validator = new YupValidator(
30
- global.server.app,
31
- yup.object().shape({ name: yup.string() }),
32
- );
33
- const result = await new ValidateService(
34
- global.server.app,
35
- {},
36
- ).validateSchema(req, validator, data);
37
- expect(result).toStrictEqual({
38
- name: '1213123123',
39
- });
40
- });
41
- });
42
-
43
- describe('isValidatorExists funtion', () => {
44
- it('returns false for non-object input', () => {
45
- expect.assertions(1);
46
- const validator = 'not an object';
47
- const result = ValidateService.isValidatorExists(validator);
48
- expect(result).toBeFalsy();
49
- });
50
-
51
- it('returns true if validator is an instance of one of the drivers', () => {
52
- expect.assertions(1);
53
- const validator = new ValidateService.drivers.YupValidator();
54
- const result = ValidateService.isValidatorExists(validator);
55
- expect(result).toBeTruthy();
56
- });
57
-
58
- it('returns false if validator is not an instance of any of the drivers', () => {
59
- expect.assertions(1);
60
- const validator = {};
61
- const result = ValidateService.isValidatorExists(validator);
62
- expect(result).toBeFalsy();
63
- });
64
- });
65
-
66
- describe('getDriverByValidatorBody', () => {
67
- it('should return the body if it is already a validator', () => {
68
- expect.assertions(1);
69
- const body = new YupValidator(
70
- global.server.app,
71
- yup.object().shape({ name: yup.string() }),
72
- );
73
-
74
- const validator = ValidateService.getDriverByValidatorBody(
75
- global.server.app,
76
- body,
77
- );
78
-
79
- expect(validator).toStrictEqual(body);
80
- });
81
-
82
- it('should return a YupValidator instance if the body is a Yup schema', () => {
83
- expect.assertions(1);
84
- const body = yup.object().shape({
85
- name: '1234',
86
- });
87
-
88
- const validator = ValidateService.getDriverByValidatorBody(
89
- global.server.app,
90
- body,
91
- );
92
-
93
- expect(validator).toBeInstanceOf(YupValidator);
94
- });
95
-
96
- it('should return CustomValidator if the body is neither a validator nor a Yup schema', () => {
97
- expect.assertions(1);
98
- const body = 'string';
99
- const validator = ValidateService.getDriverByValidatorBody(
100
- global.server.app,
101
- body,
102
- );
103
-
104
- expect(validator).toBeInstanceOf(CustomValidator);
105
- });
106
- });
107
- });