exp-node-server 1.1.7 → 2.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.
Files changed (181) hide show
  1. package/README.md +82 -20
  2. package/dist/cli.js +525 -0
  3. package/package.json +29 -9
  4. package/templates/mongoose/.dockerignore +10 -0
  5. package/templates/mongoose/.env.example +56 -7
  6. package/templates/mongoose/.prettierignore +3 -0
  7. package/templates/mongoose/.prettierrc +7 -0
  8. package/templates/mongoose/Dockerfile +33 -0
  9. package/templates/mongoose/_gitignore +32 -0
  10. package/templates/mongoose/docker-compose.yml +21 -0
  11. package/templates/mongoose/eslint.config.js +35 -0
  12. package/templates/mongoose/package-lock.json +6377 -0
  13. package/templates/mongoose/package.json +49 -10
  14. package/templates/mongoose/readme.md +85 -0
  15. package/templates/mongoose/src/app/configs/index.ts +178 -26
  16. package/templates/mongoose/src/app/errors/zodError.ts +16 -16
  17. package/templates/mongoose/src/app/lib/db.ts +18 -0
  18. package/templates/mongoose/src/app/lib/logger.ts +12 -0
  19. package/templates/mongoose/src/app/middlewares/auth.ts +48 -44
  20. package/templates/mongoose/src/app/middlewares/global_error_handler.ts +59 -65
  21. package/templates/mongoose/src/app/middlewares/not_found_api.ts +7 -7
  22. package/templates/mongoose/src/app/middlewares/rate_limiter.ts +153 -0
  23. package/templates/mongoose/src/app/middlewares/request_validator.ts +11 -10
  24. package/templates/mongoose/src/app/middlewares/uploader.ts +17 -13
  25. package/templates/mongoose/src/app/modules/account/account.controller.ts +156 -0
  26. package/templates/mongoose/src/app/modules/account/account.interface.ts +65 -0
  27. package/templates/mongoose/src/app/modules/account/account.route.ts +101 -0
  28. package/templates/mongoose/src/app/modules/account/account.schema.ts +97 -0
  29. package/templates/mongoose/src/app/modules/account/account.service.ts +422 -0
  30. package/templates/mongoose/src/app/modules/account/account.swagger.ts +278 -0
  31. package/templates/mongoose/src/app/modules/account/account.validation.ts +63 -0
  32. package/templates/mongoose/src/app/modules/profile/profile.controller.ts +17 -0
  33. package/templates/mongoose/src/app/modules/profile/profile.interface.ts +7 -0
  34. package/templates/mongoose/src/app/modules/profile/profile.route.ts +28 -0
  35. package/templates/mongoose/src/app/modules/profile/profile.schema.ts +13 -0
  36. package/templates/mongoose/src/app/modules/profile/profile.service.ts +28 -0
  37. package/templates/mongoose/src/app/modules/profile/profile.swagger.ts +34 -0
  38. package/templates/mongoose/src/app/modules/profile/profile.validation.ts +8 -0
  39. package/templates/mongoose/src/app/passport-js/index.ts +87 -0
  40. package/templates/mongoose/src/app/passport-js/oauth-state.ts +150 -0
  41. package/templates/mongoose/src/app/passport-js/providers.ts +258 -0
  42. package/templates/mongoose/src/app/passport-js/social-account.ts +141 -0
  43. package/templates/mongoose/src/app/queues/agenda.ts +24 -0
  44. package/templates/mongoose/src/app/queues/email.queue.ts +53 -0
  45. package/templates/mongoose/src/app/queues/index.ts +13 -0
  46. package/templates/mongoose/src/app/templates/otpEmailTemplate.ts +185 -0
  47. package/templates/mongoose/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  48. package/templates/mongoose/src/app/types/error.d.ts +7 -7
  49. package/templates/mongoose/src/app/types/express.d.ts +11 -0
  50. package/templates/mongoose/src/app/types/passport-oauth1.d.ts +51 -0
  51. package/templates/mongoose/src/app/utils/JWT.ts +38 -18
  52. package/templates/mongoose/src/app/utils/app_error.ts +10 -10
  53. package/templates/mongoose/src/app/utils/auth_cookie.ts +27 -0
  54. package/templates/mongoose/src/app/utils/catch_async.ts +8 -8
  55. package/templates/mongoose/src/app/utils/cloudinary.ts +26 -59
  56. package/templates/mongoose/src/app/utils/mail_sender.ts +26 -92
  57. package/templates/mongoose/src/app/utils/manage_response.ts +19 -19
  58. package/templates/mongoose/src/app/utils/otp.ts +20 -0
  59. package/templates/mongoose/src/app/utils/session.ts +55 -0
  60. package/templates/mongoose/src/app.ts +73 -42
  61. package/templates/mongoose/src/routes.ts +13 -13
  62. package/templates/mongoose/src/server.ts +38 -18
  63. package/templates/mongoose/src/swaggerOptions.ts +23 -23
  64. package/templates/mongoose/tests/app.test.ts +97 -0
  65. package/templates/mongoose/tests/global-setup.ts +15 -0
  66. package/templates/mongoose/tests/helpers/fixtures.ts +42 -0
  67. package/templates/mongoose/tests/routes/account.test.ts +614 -0
  68. package/templates/mongoose/tests/routes/profile.test.ts +95 -0
  69. package/templates/mongoose/tests/routes/social.test.ts +310 -0
  70. package/templates/mongoose/tests/setup.ts +25 -0
  71. package/templates/mongoose/tsconfig.json +16 -20
  72. package/templates/mongoose/vitest.config.ts +43 -0
  73. package/templates/prisma/.dockerignore +2 -0
  74. package/templates/prisma/.env.example +62 -6
  75. package/templates/prisma/.prettierignore +3 -0
  76. package/templates/prisma/.prettierrc +7 -0
  77. package/templates/prisma/.projectrc.json +1 -1
  78. package/templates/prisma/Dockerfile +21 -7
  79. package/templates/prisma/_gitignore +35 -0
  80. package/templates/prisma/docker-compose.yml +8 -7
  81. package/templates/prisma/eslint.config.js +35 -0
  82. package/templates/prisma/package-lock.json +7327 -0
  83. package/templates/prisma/package.json +47 -9
  84. package/templates/prisma/prisma/schema/account.schema.prisma +92 -10
  85. package/templates/prisma/prisma/schema/profile.schema.prisma +8 -3
  86. package/templates/prisma/prisma/schema/schema.prisma +1 -2
  87. package/templates/prisma/prisma.config.ts +5 -5
  88. package/templates/prisma/readme.md +81 -0
  89. package/templates/prisma/src/app/configs/index.ts +175 -26
  90. package/templates/prisma/src/app/errors/zodError.ts +16 -16
  91. package/templates/prisma/src/app/lib/logger.ts +12 -0
  92. package/templates/prisma/src/app/lib/prisma.ts +4 -6
  93. package/templates/prisma/src/app/middlewares/auth.ts +37 -16
  94. package/templates/prisma/src/app/middlewares/global_error_handler.ts +42 -35
  95. package/templates/prisma/src/app/middlewares/not_found_api.ts +7 -7
  96. package/templates/prisma/src/app/middlewares/rate_limiter.ts +124 -0
  97. package/templates/prisma/src/app/middlewares/request_validator.ts +4 -3
  98. package/templates/prisma/src/app/middlewares/uploader.ts +17 -13
  99. package/templates/prisma/src/app/modules/account/account.controller.ts +74 -34
  100. package/templates/prisma/src/app/modules/account/account.route.ts +68 -22
  101. package/templates/prisma/src/app/modules/account/account.service.ts +329 -309
  102. package/templates/prisma/src/app/modules/account/account.swagger.ts +167 -76
  103. package/templates/prisma/src/app/modules/account/account.validation.ts +41 -19
  104. package/templates/prisma/src/app/modules/profile/profile.controller.ts +4 -4
  105. package/templates/prisma/src/app/modules/profile/profile.route.ts +18 -12
  106. package/templates/prisma/src/app/modules/profile/profile.service.ts +11 -18
  107. package/templates/prisma/src/app/modules/profile/profile.swagger.ts +11 -11
  108. package/templates/prisma/src/app/modules/profile/profile.validation.ts +2 -2
  109. package/templates/prisma/src/app/passport-js/index.ts +87 -0
  110. package/templates/prisma/src/app/passport-js/oauth-state.ts +150 -0
  111. package/templates/prisma/src/app/passport-js/providers.ts +263 -0
  112. package/templates/prisma/src/app/passport-js/social-account.ts +147 -0
  113. package/templates/prisma/src/app/queues/boss.ts +8 -0
  114. package/templates/prisma/src/app/queues/email.queue.ts +56 -0
  115. package/templates/prisma/src/app/queues/index.ts +14 -0
  116. package/templates/prisma/src/app/queues/maintenance.queue.ts +30 -0
  117. package/templates/prisma/src/app/templates/otpEmailTemplate.ts +185 -0
  118. package/templates/prisma/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  119. package/templates/prisma/src/app/types/error.d.ts +7 -7
  120. package/templates/prisma/src/app/types/express.d.ts +11 -0
  121. package/templates/prisma/src/app/types/passport-oauth1.d.ts +51 -0
  122. package/templates/prisma/src/app/utils/JWT.ts +36 -17
  123. package/templates/prisma/src/app/utils/app_error.ts +10 -10
  124. package/templates/prisma/src/app/utils/auth_cookie.ts +27 -0
  125. package/templates/prisma/src/app/utils/catch_async.ts +8 -8
  126. package/templates/prisma/src/app/utils/cloudinary.ts +26 -59
  127. package/templates/prisma/src/app/utils/mail_sender.ts +26 -92
  128. package/templates/prisma/src/app/utils/manage_response.ts +19 -19
  129. package/templates/prisma/src/app/utils/otp.ts +20 -0
  130. package/templates/prisma/src/app/utils/session.ts +57 -0
  131. package/templates/prisma/src/app.ts +73 -42
  132. package/templates/prisma/src/routes.ts +12 -10
  133. package/templates/prisma/src/server.ts +37 -19
  134. package/templates/prisma/src/swaggerOptions.ts +22 -26
  135. package/templates/prisma/tests/app.test.ts +86 -0
  136. package/templates/prisma/tests/helpers/fixtures.ts +35 -0
  137. package/templates/prisma/tests/helpers/prisma.ts +32 -0
  138. package/templates/prisma/tests/routes/account.test.ts +627 -0
  139. package/templates/prisma/tests/routes/profile.test.ts +91 -0
  140. package/templates/prisma/tests/routes/social.test.ts +317 -0
  141. package/templates/prisma/tests/setup.ts +10 -0
  142. package/templates/prisma/tsconfig.json +16 -23
  143. package/templates/prisma/vitest.config.ts +38 -0
  144. package/bin/cli.js +0 -41
  145. package/src/createProject.js +0 -61
  146. package/src/generateModule.js +0 -93
  147. package/src/utils/getProjectType.js +0 -13
  148. package/src/utils/mongoose/controllerGenerator.js +0 -70
  149. package/src/utils/mongoose/routeGenerator.js +0 -27
  150. package/src/utils/mongoose/schemaGenerator.js +0 -11
  151. package/src/utils/mongoose/serviceGenerator.js +0 -49
  152. package/src/utils/mongoose/swaggerGenerator.js +0 -112
  153. package/src/utils/mongoose/validationGenerator.js +0 -13
  154. package/src/utils/prisma/prismaSchemaGenerator.js +0 -10
  155. package/src/utils/prisma/prismaServiceGenerator.js +0 -47
  156. package/src/utils/routeInjector.js +0 -40
  157. package/src/utils/swaggerInjector.js +0 -52
  158. package/src/utils/validateModuleName.js +0 -17
  159. package/src/utils/validateProjectName.js +0 -15
  160. package/src/utils.js +0 -26
  161. package/templates/mongoose/src/app/errors/castError.ts +0 -23
  162. package/templates/mongoose/src/app/errors/duplicateError.ts +0 -26
  163. package/templates/mongoose/src/app/errors/validationError.ts +0 -25
  164. package/templates/mongoose/src/app/modules/auth/auth.controller.ts +0 -127
  165. package/templates/mongoose/src/app/modules/auth/auth.interface.ts +0 -24
  166. package/templates/mongoose/src/app/modules/auth/auth.route.ts +0 -46
  167. package/templates/mongoose/src/app/modules/auth/auth.schema.ts +0 -19
  168. package/templates/mongoose/src/app/modules/auth/auth.service.ts +0 -324
  169. package/templates/mongoose/src/app/modules/auth/auth.swagger.ts +0 -214
  170. package/templates/mongoose/src/app/modules/auth/auth.validation.ts +0 -37
  171. package/templates/mongoose/src/app/modules/user/user.controller.ts +0 -17
  172. package/templates/mongoose/src/app/modules/user/user.interface.ts +0 -15
  173. package/templates/mongoose/src/app/modules/user/user.route.ts +0 -20
  174. package/templates/mongoose/src/app/modules/user/user.schema.ts +0 -22
  175. package/templates/mongoose/src/app/modules/user/user.service.ts +0 -22
  176. package/templates/mongoose/src/app/modules/user/user.swagger.ts +0 -75
  177. package/templates/mongoose/src/app/modules/user/user.validation.ts +0 -18
  178. package/templates/mongoose/src/app/types/index.d.ts +0 -12
  179. package/templates/mongoose/src/app/utils/isAccountExist.ts +0 -31
  180. package/templates/prisma/src/app/types/index.d.ts +0 -12
  181. package/templates/prisma/src/app/utils/otpGenerator.ts +0 -29
@@ -0,0 +1,86 @@
1
+ import request from 'supertest';
2
+ import { describe, expect, it } from 'vitest';
3
+ import app from '../src/app.js';
4
+ import { makeAccount } from './helpers/fixtures.js';
5
+ import { prismaMock } from './helpers/prisma.js';
6
+
7
+ describe('app', () => {
8
+ it('GET / responds with security headers', async () => {
9
+ const res = await request(app).get('/');
10
+
11
+ expect(res.status).toBe(200);
12
+ expect(res.headers['x-powered-by']).toBeUndefined();
13
+ expect(res.headers['x-content-type-options']).toBe('nosniff');
14
+ expect(res.headers['content-security-policy']).toBeDefined();
15
+ });
16
+
17
+ it('unknown routes are 404', async () => {
18
+ const res = await request(app).get('/api/nope');
19
+ expect(res.status).toBe(404);
20
+ });
21
+
22
+ it('malformed JSON is a 400, not a 500', async () => {
23
+ const res = await request(app)
24
+ .post('/api/auth/sign-in')
25
+ .set('Content-Type', 'application/json')
26
+ .send('{"email":');
27
+
28
+ expect(res.status).toBe(400);
29
+ expect(res.body.message).toBe('Invalid JSON body');
30
+ });
31
+
32
+ it('rejects bodies over 1mb', async () => {
33
+ const res = await request(app)
34
+ .post('/api/auth/sign-in')
35
+ .send({ email: 'a@b.c', password: 'x'.repeat(1_100_000) });
36
+
37
+ expect(res.status).toBe(413);
38
+ });
39
+
40
+ it('only allows the configured CORS origin', async () => {
41
+ const allowed = await request(app).get('/').set('Origin', 'http://localhost:5173');
42
+ const other = await request(app).get('/').set('Origin', 'https://evil.example');
43
+
44
+ expect(allowed.headers['access-control-allow-origin']).toBe('http://localhost:5173');
45
+ expect(other.headers['access-control-allow-origin']).toBeUndefined();
46
+ });
47
+ });
48
+
49
+ describe('rate limiting', () => {
50
+ it('limits sign-in attempts per email', async () => {
51
+ prismaMock.account.findUnique.mockResolvedValue(makeAccount());
52
+ const attempt = () =>
53
+ request(app)
54
+ .post('/api/auth/sign-in')
55
+ .send({ email: 'target@example.com', password: 'guess-guess' });
56
+
57
+ for (let i = 0; i < 10; i++) {
58
+ expect((await attempt()).status).toBe(401);
59
+ }
60
+ const blocked = await attempt();
61
+
62
+ expect(blocked.status).toBe(429);
63
+ expect(blocked.headers['ratelimit-policy']).toBeDefined();
64
+
65
+ // another account from the same client is still allowed
66
+ const other = await request(app)
67
+ .post('/api/auth/sign-in')
68
+ .send({ email: 'someone-else@example.com', password: 'guess-guess' });
69
+ expect(other.status).toBe(401);
70
+ });
71
+
72
+ it('limits emails sent to one address, whatever the endpoint', async () => {
73
+ prismaMock.account.findUnique.mockResolvedValue(null);
74
+ const email = 'victim@example.com';
75
+
76
+ await request(app).put('/api/auth/forget-password').send({ email }).expect(200);
77
+ await request(app).put('/api/auth/resend-otp').send({ email }).expect(200);
78
+ await request(app)
79
+ .put('/api/auth/forget-password')
80
+ .send({ email: email.toUpperCase() })
81
+ .expect(200);
82
+
83
+ const blocked = await request(app).put('/api/auth/forget-password').send({ email });
84
+ expect(blocked.status).toBe(429);
85
+ });
86
+ });
@@ -0,0 +1,35 @@
1
+ import type { Account } from '@prisma/client';
2
+ import bcrypt from 'bcrypt';
3
+ import { configs } from '../../src/app/configs/index.js';
4
+ import { jwtHelpers } from '../../src/app/utils/JWT.js';
5
+
6
+ export const PASSWORD = 'correct-horse-battery';
7
+ const passwordHash = bcrypt.hashSync(PASSWORD, 4);
8
+
9
+ /** A verified, active account with a password. */
10
+ export const makeAccount = (overrides: Partial<Account> = {}): Account => ({
11
+ id: 'acc-1',
12
+ email: 'user@example.com',
13
+ password: passwordHash,
14
+ provider: 'LOCAL',
15
+ role: 'USER',
16
+ isDeleted: false,
17
+ isAccountVerified: true,
18
+ tokenVersion: 0,
19
+ lastOtp: null,
20
+ lastOtpSendingTime: null,
21
+ otpAttempts: 0,
22
+ createdAt: new Date(),
23
+ updatedAt: new Date(),
24
+ ...overrides,
25
+ });
26
+
27
+ /** A signed access token, as the app would issue it for `account`. */
28
+ export const accessTokenFor = (account: Account) =>
29
+ jwtHelpers.generateToken(
30
+ { accountId: account.id, email: account.email, role: account.role, tv: account.tokenVersion },
31
+ configs.jwt.access_token,
32
+ '1h',
33
+ );
34
+
35
+ export const bearer = (account: Account) => `Bearer ${accessTokenFor(account)}`;
@@ -0,0 +1,32 @@
1
+ import { type Mock, vi } from 'vitest';
2
+
3
+ const model = () => ({
4
+ findUnique: vi.fn(),
5
+ findFirst: vi.fn(),
6
+ findMany: vi.fn(),
7
+ count: vi.fn(),
8
+ create: vi.fn(),
9
+ update: vi.fn(),
10
+ updateMany: vi.fn(),
11
+ upsert: vi.fn(),
12
+ delete: vi.fn(),
13
+ deleteMany: vi.fn(),
14
+ });
15
+
16
+ type ModelMock = ReturnType<typeof model>;
17
+
18
+ const models = new Map<string, ModelMock>();
19
+ const client = { $queryRaw: vi.fn(), $transaction: vi.fn() };
20
+
21
+ /**
22
+ * Stands in for the Prisma client (see tests/setup.ts), so tests need no database.
23
+ * Any model works without registering it: `prismaMock.order.findMany` is created
24
+ * on first use.
25
+ */
26
+ export const prismaMock = new Proxy(client, {
27
+ get(target, prop: string) {
28
+ if (prop in target) return target[prop as keyof typeof target];
29
+ if (!models.has(prop)) models.set(prop, model());
30
+ return models.get(prop);
31
+ },
32
+ }) as typeof client & Record<string, ModelMock> & { $queryRaw: Mock };