nodejs-quickstart-structure 2.0.1 → 2.1.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 (165) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +64 -66
  3. package/bin/index.js +5 -2
  4. package/lib/generator.js +10 -4
  5. package/lib/modules/app-setup.js +76 -6
  6. package/lib/modules/auth-setup.js +143 -0
  7. package/lib/modules/caching-setup.js +8 -1
  8. package/lib/modules/config-files.js +6 -0
  9. package/lib/modules/database-setup.js +2 -1
  10. package/lib/modules/project-setup.js +1 -0
  11. package/lib/prompts.js +39 -0
  12. package/package.json +5 -4
  13. package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
  14. package/templates/clean-architecture/js/src/index.js.ejs +2 -0
  15. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
  16. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
  17. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +38 -1
  18. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
  19. package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +51 -0
  20. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
  21. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
  22. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +70 -5
  23. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
  24. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +55 -22
  25. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
  26. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  27. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
  28. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
  29. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
  30. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
  31. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +12 -3
  32. package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
  33. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
  34. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
  35. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +23 -1
  36. package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
  37. package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
  38. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
  39. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +9 -1
  40. package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
  41. package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
  42. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
  43. package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
  44. package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
  45. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +71 -10
  46. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
  47. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +43 -9
  48. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
  49. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +57 -24
  50. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
  51. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
  52. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  53. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
  54. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
  55. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
  56. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +12 -3
  57. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
  58. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +10 -1
  59. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
  60. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
  61. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
  62. package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +55 -0
  63. package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
  64. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +10 -1
  65. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
  66. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
  67. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
  68. package/templates/common/.cursorrules.ejs +9 -0
  69. package/templates/common/.env.example.ejs +17 -10
  70. package/templates/common/README.md.ejs +63 -18
  71. package/templates/common/auth/js/controllers/authController.js.ejs +170 -0
  72. package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
  73. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
  74. package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
  75. package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
  76. package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
  77. package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
  78. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
  79. package/templates/common/auth/ts/controllers/authController.ts.ejs +167 -0
  80. package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
  81. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
  82. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
  83. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
  84. package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
  85. package/templates/common/babel.config.js.ejs +5 -0
  86. package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
  87. package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
  88. package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
  89. package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
  90. package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
  91. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
  92. package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
  93. package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -1
  94. package/templates/common/caching/js/memoryCache.spec.js.ejs +2 -0
  95. package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
  96. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
  97. package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
  98. package/templates/common/database/js/models/User.js.ejs +14 -1
  99. package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
  100. package/templates/common/database/js/models/User.spec.js.ejs +12 -0
  101. package/templates/common/database/js/mongoose.spec.js.ejs +2 -0
  102. package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
  103. package/templates/common/database/ts/models/User.ts.ejs +17 -0
  104. package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
  105. package/templates/common/database/ts/mongoose.spec.ts.ejs +2 -0
  106. package/templates/common/docker-compose.yml.ejs +12 -0
  107. package/templates/common/ecosystem.config.js.ejs +9 -3
  108. package/templates/common/eslint.config.mjs.ejs +3 -0
  109. package/templates/common/jest.config.js.ejs +13 -9
  110. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
  111. package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
  112. package/templates/common/migrations/init.js.ejs +5 -4
  113. package/templates/common/package.json.ejs +11 -2
  114. package/templates/common/prompts/project-context.md.ejs +8 -1
  115. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
  116. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
  117. package/templates/common/swagger.yml.ejs +148 -0
  118. package/templates/common/tsconfig.eslint.json +15 -0
  119. package/templates/common/tsconfig.json +3 -1
  120. package/templates/common/views/ejs/index.ejs +264 -30
  121. package/templates/common/views/ejs/login.ejs.ejs +244 -0
  122. package/templates/common/views/ejs/signup.ejs.ejs +282 -0
  123. package/templates/common/views/pug/index.pug +269 -38
  124. package/templates/common/views/pug/login.pug.ejs +195 -0
  125. package/templates/common/views/pug/signup.pug.ejs +241 -0
  126. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
  127. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
  128. package/templates/mvc/js/src/config/env.js.ejs +12 -3
  129. package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
  130. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
  131. package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
  132. package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
  133. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
  134. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  135. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
  136. package/templates/mvc/js/src/index.js.ejs +16 -3
  137. package/templates/mvc/js/src/routes/api.js.ejs +14 -0
  138. package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
  139. package/templates/mvc/js/src/utils/errorMessages.js +1 -0
  140. package/templates/mvc/js/src/utils/httpCodes.js +1 -0
  141. package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
  142. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
  143. package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
  144. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
  145. package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
  146. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
  147. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  148. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
  149. package/templates/mvc/ts/src/index.ts.ejs +15 -3
  150. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
  151. package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
  152. package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
  153. package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
  154. package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
  155. package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
  156. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
  157. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
  158. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
  159. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
  160. package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
  161. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
  162. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
  163. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
  164. package/templates/mvc/js/src/routes/api.js +0 -10
  165. package/templates/mvc/ts/src/routes/api.ts +0 -12
@@ -0,0 +1,60 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import crypto from 'crypto';
3
+ import { env } from '@/config/env';
4
+
5
+ export interface JwtPayload {
6
+ id: string | number;
7
+ email: string;
8
+ jti?: string;
9
+ sid?: string;
10
+ exp?: number;
11
+ }
12
+
13
+ export class JwtService {
14
+ private static readonly SECRET = env.JWT_SECRET || 'your-secret-key';
15
+ private static readonly REFRESH_SECRET = env.JWT_REFRESH_SECRET || 'your-refresh-secret-key';
16
+ private static readonly EXPIRES_IN = env.JWT_EXPIRES_IN || '15m'; // Access tokens should be short-lived
17
+ private static readonly REFRESH_EXPIRES_IN = env.JWT_REFRESH_EXPIRES_IN || '7d';
18
+
19
+ static generateToken(payload: Partial<JwtPayload>, expiresIn?: string): string {
20
+ const jti = crypto.randomUUID();
21
+ return jwt.sign({ ...payload, jti }, this.SECRET, {
22
+ expiresIn: expiresIn || this.EXPIRES_IN
23
+ } as jwt.SignOptions);
24
+ }
25
+
26
+ static generateRefreshToken(payload: Partial<JwtPayload>, expiresIn?: string): string {
27
+ const jti = crypto.randomUUID();
28
+ return jwt.sign({ ...payload, jti }, this.REFRESH_SECRET, {
29
+ expiresIn: expiresIn || this.REFRESH_EXPIRES_IN
30
+ } as jwt.SignOptions);
31
+ }
32
+
33
+ static verifyToken(token: string): JwtPayload | null {
34
+ try {
35
+ return jwt.verify(token, this.SECRET) as JwtPayload;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ static verifyRefreshToken(token: string): JwtPayload | null {
42
+ try {
43
+ return jwt.verify(token, this.REFRESH_SECRET) as JwtPayload;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ static decodeToken(token: string): JwtPayload | null {
50
+ try {
51
+ return jwt.decode(token) as JwtPayload;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ // Fallback in-memory storage if caching = 'None'
58
+ static activeRefreshTokens = new Map<string, string[]>();
59
+ static blacklistedTokens = new Map<string, number>();
60
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ presets: [
3
+ ['@babel/preset-env', { targets: { node: 'current' } }]
4
+ ]
5
+ };
@@ -1,9 +1,12 @@
1
1
  const User = require('../domain/models/User');
2
- <%_ if (caching === 'Redis') { -%>
2
+ <% if (auth.includes('JWT')) { -%>
3
+ const bcrypt = require('bcryptjs');
4
+ <% } -%>
5
+ <% if (caching === 'Redis') { -%>
3
6
  const cacheService = require('../infrastructure/caching/redisClient');
4
- <%_ } else if (caching === 'Memory Cache') { -%>
7
+ <% } else if (caching === 'Memory Cache') { -%>
5
8
  const cacheService = require('../infrastructure/caching/memoryCache');
6
- <%_ } -%>
9
+ <% } -%>
7
10
  const logger = require('../infrastructure/log/logger');
8
11
 
9
12
  class CreateUser {
@@ -11,8 +14,14 @@ class CreateUser {
11
14
  this.userRepository = userRepository;
12
15
  }
13
16
 
14
- async execute(name, email) {
15
- const user = new User(null, name, email);
17
+ async execute(name, email, password) {
18
+ let finalPassword = password;
19
+ <% if (auth.includes('JWT')) { -%>
20
+ if (password) {
21
+ finalPassword = await bcrypt.hash(password, 10);
22
+ }
23
+ <% } -%>
24
+ const user = new User(null, name, email, finalPassword);
16
25
  const savedUser = await this.userRepository.save(user);
17
26
 
18
27
  try {
@@ -15,7 +15,8 @@ class DeleteUser {
15
15
 
16
16
  try {
17
17
  await cacheService.del('users:all');
18
- logger.info('Invalidated users:all cache');
18
+ await cacheService.del(`user:${id}`);
19
+ logger.info(`Invalidated cache for user:${id} and all users`);
19
20
  } catch (error) {
20
21
  logger.error('Cache error (del):', error);
21
22
  }
@@ -0,0 +1,39 @@
1
+ <%_ if (caching === 'Redis') { -%>
2
+ const cacheService = require('../infrastructure/caching/redisClient');
3
+ <%_ } else if (caching === 'Memory Cache') { -%>
4
+ const cacheService = require('../infrastructure/caching/memoryCache');
5
+ <%_ } -%>
6
+ const logger = require('../infrastructure/log/logger');
7
+
8
+ class GetUserById {
9
+ constructor(userRepository) {
10
+ this.userRepository = userRepository;
11
+ }
12
+
13
+ async execute(id) {
14
+ const cacheKey = `user:${id}`;
15
+ try {
16
+ const cachedUser = await cacheService.get(cacheKey);
17
+ if (cachedUser) {
18
+ logger.info(`Serving user ${id} from cache`);
19
+ return cachedUser;
20
+ }
21
+ } catch (error) {
22
+ logger.error('Cache error (get):', error);
23
+ }
24
+
25
+ const user = await this.userRepository.findById(id);
26
+
27
+ if (user) {
28
+ try {
29
+ await cacheService.set(cacheKey, user, 3600); // Cache for 1 hour
30
+ } catch (error) {
31
+ logger.error('Cache error (set):', error);
32
+ }
33
+ }
34
+
35
+ return user;
36
+ }
37
+ }
38
+
39
+ module.exports = GetUserById;
@@ -15,7 +15,8 @@ class UpdateUser {
15
15
 
16
16
  try {
17
17
  await cacheService.del('users:all');
18
- logger.info('Invalidated users:all cache');
18
+ await cacheService.del(`user:${id}`);
19
+ logger.info(`Invalidated cache for user:${id} and all users`);
19
20
  } catch (error) {
20
21
  logger.error('Cache error (del):', error);
21
22
  }
@@ -1,18 +1,26 @@
1
1
  import { User } from '@/domain/user';
2
-
3
2
  import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
- <%_ if (caching === 'Redis') { -%>
3
+ <% if (auth.includes('JWT')) { -%>
4
+ import bcrypt from 'bcryptjs';
5
+ <% } -%>
6
+ <% if (caching === 'Redis') { -%>
5
7
  import cacheService from '@/infrastructure/caching/redisClient';
6
- <%_ } else if (caching === 'Memory Cache') { -%>
8
+ <% } else if (caching === 'Memory Cache') { -%>
7
9
  import cacheService from '@/infrastructure/caching/memoryCache';
8
- <%_ } -%>
10
+ <% } -%>
9
11
  import logger from '@/infrastructure/log/logger';
10
12
 
11
13
  export default class CreateUser {
12
14
  constructor(private userRepository: UserRepository) {}
13
15
 
14
- async execute(name: string, email: string) {
15
- const user = new User(null, name, email);
16
+ async execute(name: string, email: string, password?: string) {
17
+ let finalPassword = password;
18
+ <% if (auth.includes('JWT')) { -%>
19
+ if (password) {
20
+ finalPassword = await bcrypt.hash(password, 10);
21
+ }
22
+ <% } -%>
23
+ const user = new User(null, name, email, finalPassword);
16
24
  const savedUser = await this.userRepository.save(user);
17
25
 
18
26
  try {
@@ -14,7 +14,8 @@ export default class DeleteUser {
14
14
 
15
15
  try {
16
16
  await cacheService.del('users:all');
17
- logger.info('Invalidated users:all cache');
17
+ await cacheService.del(`user:${id}`);
18
+ logger.info(`Invalidated cache for user:${id} and all users`);
18
19
  } catch (error) {
19
20
  logger.error('Cache error (del):', error);
20
21
  }
@@ -0,0 +1,32 @@
1
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
2
+ import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
3
+ import logger from '@/infrastructure/log/logger';
4
+
5
+ export default class GetUserById {
6
+ constructor(private userRepository: UserRepository) {}
7
+
8
+ async execute(id: string | number) {
9
+ const cacheKey = `user:${id}`;
10
+ try {
11
+ const cachedUser = await cacheService.get(cacheKey);
12
+ if (cachedUser) {
13
+ logger.info(`Serving user ${id} from cache`);
14
+ return cachedUser;
15
+ }
16
+ } catch (error) {
17
+ logger.error('Cache error (get):', error);
18
+ }
19
+
20
+ const user = await this.userRepository.findById(id);
21
+
22
+ if (user) {
23
+ try {
24
+ await cacheService.set(cacheKey, user, 3600); // Cache for 1 hour
25
+ } catch (error) {
26
+ logger.error('Cache error (set):', error);
27
+ }
28
+ }
29
+
30
+ return user;
31
+ }
32
+ }
@@ -14,7 +14,8 @@ export default class UpdateUser {
14
14
 
15
15
  try {
16
16
  await cacheService.del('users:all');
17
- logger.info('Invalidated users:all cache');
17
+ await cacheService.del(`user:${id}`);
18
+ logger.info(`Invalidated cache for user:${id} and all users`);
18
19
  } catch (error) {
19
20
  logger.error('Cache error (del):', error);
20
21
  }
@@ -9,6 +9,8 @@ jest.mock('node-cache', () => {
9
9
  jest.mock('<%- loggerPath %>', () => ({
10
10
  info: jest.fn(),
11
11
  error: jest.fn(),
12
+ warn: jest.fn(),
13
+ debug: jest.fn(),
12
14
  }));
13
15
 
14
16
  describe('Memory Cache Client', () => {
@@ -16,6 +16,8 @@ jest.mock('dotenv', () => ({
16
16
  const mockLogger = {
17
17
  info: jest.fn(),
18
18
  error: jest.fn(),
19
+ warn: jest.fn(),
20
+ debug: jest.fn(),
19
21
  };
20
22
 
21
23
  jest.mock('<%- loggerPath %>', () => mockLogger);
@@ -9,6 +9,8 @@ jest.mock('node-cache', () => {
9
9
  jest.mock('<%- loggerPath %>', () => ({
10
10
  info: jest.fn(),
11
11
  error: jest.fn(),
12
+ warn: jest.fn(),
13
+ debug: jest.fn(),
12
14
  }));
13
15
 
14
16
  describe('Memory Cache Client', () => {
@@ -16,6 +16,8 @@ jest.mock('dotenv', () => ({
16
16
  const mockLogger = {
17
17
  info: jest.fn(),
18
18
  error: jest.fn(),
19
+ warn: jest.fn(),
20
+ debug: jest.fn(),
19
21
  };
20
22
 
21
23
  jest.mock('<%- loggerPath %>', () => mockLogger);
@@ -6,12 +6,18 @@ class UserModel {
6
6
  return this.mockData;
7
7
  }
8
8
 
9
+ static async findOne(query) {
10
+ return this.mockData.find((u) => {
11
+ return Object.entries(query).every(([key, value]) => u[key] === value);
12
+ });
13
+ }
14
+
9
15
  static async findAll() {
10
16
  return this.mockData;
11
17
  }
12
18
 
13
19
  static async create(data) {
14
- const { id, ...rest } = data;
20
+ const { id: _, ...rest } = data;
15
21
  const newUser = { id: String(this.mockData.length + 1), ...rest };
16
22
  this.mockData.push(newUser);
17
23
  return newUser;
@@ -62,6 +68,12 @@ User.init(
62
68
  allowNull: false,
63
69
  unique: true,
64
70
  },
71
+ <% if (auth.includes('JWT')) { %>
72
+ password: {
73
+ type: DataTypes.STRING,
74
+ allowNull: false,
75
+ },
76
+ <% } %>
65
77
  deletedAt: {
66
78
  type: DataTypes.DATE,
67
79
  allowNull: true,
@@ -75,5 +87,6 @@ User.init(
75
87
  }
76
88
  );
77
89
 
90
+
78
91
  module.exports = User;
79
92
  <% } -%>
@@ -10,6 +10,12 @@ const UserSchema = new mongoose.Schema({
10
10
  required: true,
11
11
  unique: true
12
12
  },
13
+ <% if (auth.includes('JWT')) { %>
14
+ password: {
15
+ type: String,
16
+ required: true
17
+ },
18
+ <% } %>
13
19
  createdAt: {
14
20
  type: Date,
15
21
  default: Date.now
@@ -20,4 +26,5 @@ const UserSchema = new mongoose.Schema({
20
26
  }
21
27
  });
22
28
 
29
+
23
30
  module.exports = mongoose.model('User', UserSchema);
@@ -73,15 +73,27 @@ describe('User Model', () => {
73
73
  expect(await User.find()).toBeDefined();
74
74
  expect(await User.findAll()).toBeDefined();
75
75
 
76
+ // Test findOne
77
+ const oneUser = await User.findOne({ email: data.email });
78
+ expect(oneUser.email).toBe(data.email);
79
+
76
80
  const found = await User.findByPk(user.id);
77
81
  expect(found.id).toBe(user.id);
78
82
 
79
83
  const updated = await User.update(user.id, { name: 'New Name' });
80
84
  expect(updated.name).toBe('New Name');
81
85
 
86
+ // Test update non-existent
87
+ const nonExistentUpdate = await User.update('999', { name: 'Fail' });
88
+ expect(nonExistentUpdate).toBeUndefined();
89
+
82
90
  const deleted = await User.destroy(user.id);
83
91
  expect(deleted).toBe(true);
84
92
  expect(await User.findByPk(user.id)).toBeUndefined();
93
+
94
+ // Test destroy non-existent
95
+ const notDeleted = await User.destroy('999');
96
+ expect(notDeleted).toBe(false);
85
97
  <% } else { %>
86
98
  User.create.mockResolvedValue({ id: 1, ...data });
87
99
  User.findAll.mockResolvedValue([{ id: 1, ...data }]);
@@ -5,6 +5,8 @@ jest.mock('mongoose', () => ({
5
5
  const mockLogger = {
6
6
  info: jest.fn(),
7
7
  error: jest.fn(),
8
+ warn: jest.fn(),
9
+ debug: jest.fn(),
8
10
  };
9
11
 
10
12
  jest.mock('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>', () => mockLogger);
@@ -40,6 +40,11 @@ jest.mock('@/infrastructure/database/database', () => ({}));
40
40
  describe('User Model', () => {
41
41
  beforeEach(() => {
42
42
  jest.clearAllMocks();
43
+ <% if (database === 'None') { %>
44
+ const UserPath = '<% if (architecture === "MVC") { %>@/models/User<% } else { %>@/infrastructure/database/models/User<% } %>';
45
+ const User = require(UserPath).default;
46
+ User.mockData = [];
47
+ <% } %>
43
48
  });
44
49
 
45
50
  it('should be defined and initialized', () => {
@@ -78,6 +83,11 @@ describe('User Model', () => {
78
83
  expect(found).toEqual(user);
79
84
  expect(await User.findByPk('invalid')).toBeUndefined();
80
85
 
86
+ // findOne
87
+ const foundOne = await User.findOne({ email: data.email });
88
+ expect(foundOne).toEqual(user);
89
+ expect(await User.findOne({ email: 'nonexistent' })).toBeUndefined();
90
+
81
91
  // update
82
92
  const updated = await User.update(user.id, { name: 'Updated' });
83
93
  expect(updated.name).toBe('Updated');
@@ -3,6 +3,9 @@ export interface User {
3
3
  id: string;
4
4
  name: string;
5
5
  email: string;
6
+ <% if (auth.includes('JWT')) { -%>
7
+ password?: string;
8
+ <% } -%>
6
9
  }
7
10
 
8
11
  export default class UserModel {
@@ -12,6 +15,12 @@ export default class UserModel {
12
15
  return this.mockData;
13
16
  }
14
17
 
18
+ static async findOne(query: Partial<User>) {
19
+ return this.mockData.find((u) => {
20
+ return Object.entries(query).every(([key, value]) => (u as unknown as Record<string, unknown>)[key] === value);
21
+ });
22
+ }
23
+
15
24
  static async findAll() {
16
25
  return this.mockData;
17
26
  }
@@ -52,6 +61,7 @@ class User extends Model {
52
61
  public id!: number;
53
62
  public name!: string;
54
63
  public email!: string;
64
+ <% if (auth.includes('JWT')) { %>public password!: string;<% } %>
55
65
  }
56
66
 
57
67
  User.init(
@@ -70,6 +80,12 @@ User.init(
70
80
  allowNull: false,
71
81
  unique: true,
72
82
  },
83
+ <% if (auth.includes('JWT')) { %>
84
+ password: {
85
+ type: DataTypes.STRING,
86
+ allowNull: false,
87
+ },
88
+ <% } %>
73
89
  deletedAt: {
74
90
  type: DataTypes.DATE,
75
91
  allowNull: true,
@@ -83,5 +99,6 @@ User.init(
83
99
  }
84
100
  );
85
101
 
102
+
86
103
  export default User;
87
104
  <% } -%>
@@ -3,6 +3,7 @@ import mongoose, { Schema, Document } from 'mongoose';
3
3
  export interface IUser extends Document {
4
4
  name: string;
5
5
  email: string;
6
+ <% if (auth.includes('JWT')) { %>password: string;<% } %>
6
7
  createdAt: Date;
7
8
  deletedAt?: Date | null;
8
9
  }
@@ -17,6 +18,12 @@ const UserSchema: Schema = new Schema({
17
18
  required: true,
18
19
  unique: true
19
20
  },
21
+ <% if (auth.includes('JWT')) { %>
22
+ password: {
23
+ type: String,
24
+ required: true
25
+ },
26
+ <% } %>
20
27
  createdAt: {
21
28
  type: Date,
22
29
  default: Date.now
@@ -27,4 +34,5 @@ const UserSchema: Schema = new Schema({
27
34
  }
28
35
  });
29
36
 
37
+
30
38
  export default mongoose.model<IUser>('User', UserSchema);
@@ -5,6 +5,8 @@ jest.mock('mongoose', () => ({
5
5
  const logger = {
6
6
  info: jest.fn(),
7
7
  error: jest.fn(),
8
+ warn: jest.fn(),
9
+ debug: jest.fn(),
8
10
  };
9
11
 
10
12
  jest.mock('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>', () => logger);
@@ -39,6 +39,12 @@ services:
39
39
  - DB_PORT=27017
40
40
  <%_ } -%>
41
41
  <%_ } -%>
42
+ <%_ if (auth.includes('JWT')) { -%>
43
+ - JWT_SECRET=your_jwt_secret_key_here_change_it
44
+ - JWT_REFRESH_SECRET=your_jwt_refresh_secret_key_here_change_it
45
+ - JWT_EXPIRES_IN=1d
46
+ - JWT_REFRESH_EXPIRES_IN=7d
47
+ <%_ } -%>
42
48
  <%_ } else { -%>
43
49
  environment:
44
50
  - PORT=3000
@@ -64,6 +70,12 @@ services:
64
70
  - DB_PORT=27017
65
71
  <%_ } -%>
66
72
  <%_ } -%>
73
+ <%_ if (auth.includes('JWT')) { -%>
74
+ - JWT_SECRET=your_jwt_secret_key_here_change_it
75
+ - JWT_REFRESH_SECRET=your_jwt_refresh_secret_key_here_change_it
76
+ - JWT_EXPIRES_IN=1d
77
+ - JWT_REFRESH_EXPIRES_IN=7d
78
+ <%_ } -%>
67
79
  <%_ } -%>
68
80
  <%_ if (database !== 'None') { -%>
69
81
  db:
@@ -24,16 +24,22 @@ module.exports = {
24
24
  DB_USER: "root",
25
25
  DB_PASSWORD: "root",
26
26
  DB_NAME: "<%= dbName %>",
27
- DB_PORT: 3306
27
+ DB_PORT: 3306,
28
28
  <%_ } else if (database === 'PostgreSQL') { -%>
29
29
  DB_USER: "postgres",
30
30
  DB_PASSWORD: "root",
31
31
  DB_NAME: "<%= dbName %>",
32
- DB_PORT: 5432
32
+ DB_PORT: 5432,
33
33
  <%_ } else if (database === 'MongoDB') { -%>
34
34
  DB_NAME: "<%= dbName %>",
35
- DB_PORT: 27017
35
+ DB_PORT: 27017,
36
36
  <%_ } -%>
37
+ <%_ } -%>
38
+ <%_ if (auth.includes('JWT')) { -%>
39
+ JWT_SECRET: "your_jwt_secret_here",
40
+ JWT_REFRESH_SECRET: "your_jwt_refresh_secret_here",
41
+ JWT_EXPIRES_IN: "1h",
42
+ JWT_REFRESH_EXPIRES_IN: "7d",
37
43
  <%_ } -%>
38
44
  }
39
45
  }]
@@ -19,6 +19,9 @@ export default tseslint.config(
19
19
  },
20
20
  },
21
21
  languageOptions: {
22
+ parserOptions: {
23
+ project: ['./tsconfig.json', './tsconfig.eslint.json'],
24
+ },
22
25
  globals: {
23
26
  ...globals.node,
24
27
  ...globals.jest,
@@ -1,8 +1,10 @@
1
1
  module.exports = {
2
2
  testEnvironment: 'node',
3
+ testTimeout: 30000,
3
4
  coverageDirectory: 'coverage',
4
5
  collectCoverageFrom: ['src/**/*.{js,ts}'],
5
6
  testMatch: ['**/*.test.ts', '**/*.test.js', '**/*.spec.ts', '**/*.spec.js'],
7
+ transformIgnorePatterns: ['/node_modules/(?!.*uuid)'],
6
8
  testPathIgnorePatterns: ['/node_modules/', '/tests/e2e/'],
7
9
  <% if (language === 'TypeScript') { %>preset: 'ts-jest',<% } %>
8
10
  moduleNameMapper: {
@@ -11,15 +13,17 @@ module.exports = {
11
13
  coveragePathIgnorePatterns: [
12
14
  "/node_modules/",
13
15
  "/dist/",
14
- "src/index",
15
- "src/app",
16
- "src/config/env",
17
- "src/infrastructure/config/env",
18
- "src/config/swagger",
19
- "src/infrastructure/webserver/swagger",
20
- "src/infrastructure/webserver/server",
21
- "src/utils/logger",
22
- "src/infrastructure/log/logger"
16
+ "src/index.(js|ts)",
17
+ "src/app.(js|ts)",
18
+ "src/config",
19
+ "src/infrastructure/config",
20
+ "src/routes",
21
+ "src/infrastructure/routes",
22
+ "src/interfaces/routes",
23
+ "src/graphql",
24
+ "src/interfaces/graphql",
25
+ "src/utils",
26
+ "src/models"
23
27
  ],
24
28
  coverageThreshold: {
25
29
  global: {
@@ -22,7 +22,7 @@ class BaseConsumer {
22
22
  }
23
23
  }
24
24
 
25
- async handle(data) {
25
+ async handle(_data) {
26
26
  throw new Error("Method 'handle()' must be implemented");
27
27
  }
28
28
  }
@@ -34,7 +34,7 @@ const connectKafka = async (retries = 10) => {
34
34
  await consumer.run({
35
35
  eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
36
36
  });
37
- } catch (error) {
37
+ } catch {
38
38
  // Fallback or no consumers found
39
39
  await consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
40
40
  await consumer.run({
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- async up(db, client) {
2
+ async up(db, _client) {
3
3
  const adminEmail = 'admin@example.com';
4
4
  const existingAdmin = await db.collection('users').findOne({ email: adminEmail });
5
5
 
@@ -7,16 +7,17 @@ module.exports = {
7
7
  await db.collection('users').insertOne({
8
8
  name: 'Admin User',
9
9
  email: adminEmail,
10
+ <% if (auth.includes('JWT')) { %>password: '$2a$10$X.fO9PeyF0Lq0lF8uV6G9u4Vb4e5T0rF8l/JzM6S7X9u4Vb4e5T0r', // password: password123<% } %>
10
11
  createdAt: new Date(),
11
12
  updatedAt: new Date()
12
13
  });
13
- console.log('Admin User seeded successfully');
14
+ console.log('Admin User seeded successfully'); // eslint-disable-line no-console
14
15
  } else {
15
- console.log('Admin User already exists, skipping seed');
16
+ console.log('Admin User already exists, skipping seed'); // eslint-disable-line no-console
16
17
  }
17
18
  },
18
19
 
19
- async down(db, client) {
20
+ async down(_db, _client) {
20
21
  // Optional: Undo the seed. Usually for seeds we might want to keep data, but strictly speaking 'down' should reverse 'up'.
21
22
  // await db.collection('users').deleteOne({ email: 'admin@example.com' });
22
23
  }