nodejs-quickstart-structure 2.0.0 → 2.1.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 (161) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +44 -40
  3. package/bin/index.js +6 -3
  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 +10 -0
  9. package/lib/modules/database-setup.js +2 -1
  10. package/lib/modules/project-setup.js +1 -0
  11. package/lib/prompts.js +40 -1
  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 +27 -0
  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 +49 -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 +69 -4
  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 +38 -21
  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 +3 -2
  32. package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
  33. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
  34. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +14 -0
  35. package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
  36. package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
  37. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
  38. package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
  39. package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
  40. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
  41. package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
  42. package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
  43. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +55 -9
  44. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
  45. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +26 -6
  46. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
  47. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +38 -23
  48. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
  49. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
  50. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  51. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
  52. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
  53. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
  54. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +3 -2
  55. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
  56. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +1 -0
  57. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
  58. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
  59. package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +47 -0
  60. package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
  61. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +1 -0
  62. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
  63. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
  64. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
  65. package/templates/common/.cursorrules.ejs +9 -0
  66. package/templates/common/.env.example.ejs +17 -10
  67. package/templates/common/.gitlab-ci.yml.ejs +3 -1
  68. package/templates/common/Jenkinsfile.ejs +10 -1
  69. package/templates/common/README.md.ejs +64 -19
  70. package/templates/common/_circleci/config.yml.ejs +96 -0
  71. package/templates/common/_github/workflows/ci.yml.ejs +1 -1
  72. package/templates/common/auth/js/controllers/authController.js.ejs +168 -0
  73. package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
  74. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
  75. package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
  76. package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
  77. package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
  78. package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
  79. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
  80. package/templates/common/auth/ts/controllers/authController.ts.ejs +165 -0
  81. package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
  82. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
  83. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
  84. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
  85. package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
  86. package/templates/common/bitbucket-pipelines.yml.ejs +60 -0
  87. package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
  88. package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
  89. package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
  90. package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
  91. package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
  92. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
  93. package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
  94. package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -2
  95. package/templates/common/database/js/models/User.js.ejs +14 -1
  96. package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
  97. package/templates/common/database/js/models/User.spec.js.ejs +12 -0
  98. package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
  99. package/templates/common/database/ts/models/User.ts.ejs +17 -0
  100. package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
  101. package/templates/common/docker-compose.yml.ejs +14 -0
  102. package/templates/common/ecosystem.config.js.ejs +9 -3
  103. package/templates/common/eslint.config.mjs.ejs +3 -0
  104. package/templates/common/jest.config.js.ejs +11 -9
  105. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
  106. package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
  107. package/templates/common/migrations/init.js.ejs +5 -4
  108. package/templates/common/package.json.ejs +10 -2
  109. package/templates/common/prompts/project-context.md.ejs +8 -1
  110. package/templates/common/scripts/run-e2e.js.ejs +26 -10
  111. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
  112. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
  113. package/templates/common/swagger.yml.ejs +148 -0
  114. package/templates/common/tsconfig.eslint.json +15 -0
  115. package/templates/common/tsconfig.json +2 -1
  116. package/templates/common/views/ejs/index.ejs +264 -30
  117. package/templates/common/views/ejs/login.ejs.ejs +244 -0
  118. package/templates/common/views/ejs/signup.ejs.ejs +282 -0
  119. package/templates/common/views/pug/index.pug +269 -38
  120. package/templates/common/views/pug/login.pug.ejs +195 -0
  121. package/templates/common/views/pug/signup.pug.ejs +241 -0
  122. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
  123. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
  124. package/templates/mvc/js/src/config/env.js.ejs +12 -3
  125. package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
  126. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
  127. package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
  128. package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
  129. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
  130. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  131. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
  132. package/templates/mvc/js/src/index.js.ejs +16 -3
  133. package/templates/mvc/js/src/routes/api.js.ejs +14 -0
  134. package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
  135. package/templates/mvc/js/src/utils/errorMessages.js +1 -0
  136. package/templates/mvc/js/src/utils/httpCodes.js +1 -0
  137. package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
  138. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
  139. package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
  140. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
  141. package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
  142. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
  143. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  144. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
  145. package/templates/mvc/ts/src/index.ts.ejs +15 -3
  146. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
  147. package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
  148. package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
  149. package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
  150. package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
  151. package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
  152. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
  153. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
  154. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
  155. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
  156. package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
  157. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
  158. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
  159. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
  160. package/templates/mvc/js/src/routes/api.js +0 -10
  161. package/templates/mvc/ts/src/routes/api.ts +0 -12
@@ -0,0 +1,195 @@
1
+ doctype html
2
+ html(lang="en")
3
+ head
4
+ meta(charset="UTF-8")
5
+ meta(name="viewport" content="width=device-width, initial-scale=1.0")
6
+ title Login - <%= projectName %>
7
+ link(href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet")
8
+ style.
9
+ :root {
10
+ --primary: #6366f1;
11
+ --primary-glow: rgba(99, 102, 241, 0.5);
12
+ --bg-dark: #0f172a;
13
+ --card-bg: rgba(255, 255, 255, 0.03);
14
+ --card-border: rgba(255, 255, 255, 0.08);
15
+ --text-main: #f8fafc;
16
+ --text-muted: #94a3b8;
17
+ }
18
+
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ font-family: 'Poppins', sans-serif;
24
+ }
25
+
26
+ body {
27
+ background-color: var(--bg-dark);
28
+ color: var(--text-main);
29
+ min-height: 100vh;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ overflow: hidden;
34
+ position: relative;
35
+ }
36
+
37
+ .background-blobs {
38
+ position: absolute;
39
+ width: 100%;
40
+ height: 100%;
41
+ z-index: -1;
42
+ filter: blur(80px);
43
+ }
44
+
45
+ .blob {
46
+ position: absolute;
47
+ width: 400px;
48
+ height: 400px;
49
+ border-radius: 50%;
50
+ opacity: 0.4;
51
+ animation: float 20s infinite alternate;
52
+ }
53
+
54
+ .blob-1 {
55
+ background: radial-gradient(circle, #6366f1 0%, transparent 70%);
56
+ top: -100px;
57
+ left: -100px;
58
+ }
59
+
60
+ .blob-2 {
61
+ background: radial-gradient(circle, #ec4899 0%, transparent 70%);
62
+ bottom: -150px;
63
+ right: -100px;
64
+ animation-delay: -5s;
65
+ }
66
+
67
+ @keyframes float {
68
+ 0% { transform: translate(0, 0) scale(1); }
69
+ 100% { transform: translate(100px, 50px) scale(1.2); }
70
+ }
71
+
72
+ .auth-card {
73
+ background: var(--card-bg);
74
+ backdrop-filter: blur(12px);
75
+ -webkit-backdrop-filter: blur(12px);
76
+ border: 1px solid var(--card-border);
77
+ border-radius: 24px;
78
+ padding: 48px;
79
+ width: 100%;
80
+ max-width: 440px;
81
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
82
+ z-index: 1;
83
+ animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1);
84
+ }
85
+
86
+ @keyframes slideUp {
87
+ from { opacity: 0; transform: translateY(20px); }
88
+ to { opacity: 1; transform: translateY(0); }
89
+ }
90
+
91
+ .auth-card h2 {
92
+ font-size: 32px;
93
+ font-weight: 600;
94
+ margin-bottom: 8px;
95
+ text-align: center;
96
+ background: linear-gradient(to right, #fff, #94a3b8);
97
+ -webkit-background-clip: text;
98
+ -webkit-text-fill-color: transparent;
99
+ }
100
+
101
+ .subtitle {
102
+ text-align: center;
103
+ color: var(--text-muted);
104
+ font-size: 14px;
105
+ margin-bottom: 32px;
106
+ }
107
+
108
+ .form-group {
109
+ margin-bottom: 24px;
110
+ }
111
+
112
+ .form-group label {
113
+ display: block;
114
+ margin-bottom: 8px;
115
+ font-size: 14px;
116
+ font-weight: 500;
117
+ color: var(--text-muted);
118
+ }
119
+
120
+ .form-group input {
121
+ width: 100%;
122
+ background: rgba(255, 255, 255, 0.05);
123
+ border: 1px solid var(--card-border);
124
+ border-radius: 12px;
125
+ padding: 12px 16px;
126
+ color: #fff;
127
+ font-size: 15px;
128
+ transition: all 0.3s ease;
129
+ outline: none;
130
+ }
131
+
132
+ .form-group input:focus {
133
+ background: rgba(255, 255, 255, 0.08);
134
+ border-color: var(--primary);
135
+ box-shadow: 0 0 0 4px var(--primary-glow);
136
+ }
137
+
138
+ .btn-primary {
139
+ width: 100%;
140
+ background: var(--primary);
141
+ color: white;
142
+ border: none;
143
+ border-radius: 12px;
144
+ padding: 14px;
145
+ font-size: 16px;
146
+ font-weight: 600;
147
+ cursor: pointer;
148
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
149
+ margin-top: 8px;
150
+ }
151
+
152
+ .btn-primary:hover {
153
+ transform: translateY(-2px);
154
+ background: #4f46e5;
155
+ box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.4);
156
+ }
157
+
158
+ .footer {
159
+ margin-top: 32px;
160
+ text-align: center;
161
+ font-size: 14px;
162
+ color: var(--text-muted);
163
+ }
164
+
165
+ .footer a {
166
+ color: var(--primary);
167
+ text-decoration: none;
168
+ font-weight: 500;
169
+ }
170
+
171
+ .back-link {
172
+ display: inline-block;
173
+ margin-top: 16px;
174
+ font-size: 13px;
175
+ opacity: 0.6;
176
+ }
177
+ body
178
+ div.background-blobs
179
+ div.blob.blob-1
180
+ div.blob.blob-2
181
+ div.auth-card
182
+ h2 Welcome Back
183
+ p.subtitle Please enter your details to sign in
184
+ form(action="/api/auth/login" method="POST")
185
+ div.form-group
186
+ label(for="email") Email Address
187
+ input(type="email" id="email" name="email" placeholder="name@example.com" required)
188
+ div.form-group
189
+ label(for="password") Password
190
+ input(type="password" id="password" name="password" placeholder="••••••••" required)
191
+ button.btn-primary(type="submit") Sign In
192
+ div.footer
193
+ p Don't have an account?
194
+ a(href="/signup") Create account
195
+ a.back-link(href="/") ← Back to home
@@ -0,0 +1,241 @@
1
+ doctype html
2
+ html(lang="en")
3
+ head
4
+ meta(charset="UTF-8")
5
+ meta(name="viewport" content="width=device-width, initial-scale=1.0")
6
+ title Sign Up - <%= projectName %>
7
+ link(href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet")
8
+ style.
9
+ :root {
10
+ --primary: #22c55e;
11
+ --primary-glow: rgba(34, 197, 94, 0.4);
12
+ --bg-dark: #0f172a;
13
+ --card-bg: rgba(255, 255, 255, 0.03);
14
+ --card-border: rgba(255, 255, 255, 0.08);
15
+ --text-main: #f8fafc;
16
+ --text-muted: #94a3b8;
17
+ }
18
+
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ font-family: 'Poppins', sans-serif;
24
+ }
25
+
26
+ body {
27
+ background-color: var(--bg-dark);
28
+ color: var(--text-main);
29
+ min-height: 100vh;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ overflow-x: hidden;
34
+ position: relative;
35
+ }
36
+
37
+ .background-blobs {
38
+ position: absolute;
39
+ width: 100%;
40
+ height: 100%;
41
+ z-index: -1;
42
+ filter: blur(80px);
43
+ }
44
+
45
+ .blob {
46
+ position: absolute;
47
+ width: 500px;
48
+ height: 500px;
49
+ border-radius: 50%;
50
+ opacity: 0.3;
51
+ animation: float 25s infinite alternate;
52
+ }
53
+
54
+ .blob-1 {
55
+ background: radial-gradient(circle, #22c55e 0%, transparent 70%);
56
+ top: -200px;
57
+ right: -100px;
58
+ }
59
+
60
+ .blob-2 {
61
+ background: radial-gradient(circle, #3b82f6 0%, transparent 70%);
62
+ bottom: -200px;
63
+ left: -100px;
64
+ animation-delay: -7s;
65
+ }
66
+
67
+ @keyframes float {
68
+ 0% { transform: translate(0, 0) scale(1); }
69
+ 100% { transform: translate(-150px, 100px) scale(1.3); }
70
+ }
71
+
72
+ .auth-card {
73
+ background: var(--card-bg);
74
+ backdrop-filter: blur(15px);
75
+ -webkit-backdrop-filter: blur(15px);
76
+ border: 1px solid var(--card-border);
77
+ border-radius: 28px;
78
+ padding: 48px;
79
+ width: 100%;
80
+ max-width: 480px;
81
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
82
+ z-index: 1;
83
+ margin: 40px 20px;
84
+ animation: fadeIn 0.8s ease-out;
85
+ }
86
+
87
+ @keyframes fadeIn {
88
+ from { opacity: 0; transform: translateY(15px); }
89
+ to { opacity: 1; transform: translateY(0); }
90
+ }
91
+
92
+ .auth-card h2 {
93
+ font-size: 32px;
94
+ font-weight: 600;
95
+ margin-bottom: 8px;
96
+ text-align: center;
97
+ background: linear-gradient(to right, #fff, #94a3b8);
98
+ -webkit-background-clip: text;
99
+ -webkit-text-fill-color: transparent;
100
+ }
101
+
102
+ .subtitle {
103
+ text-align: center;
104
+ color: var(--text-muted);
105
+ font-size: 14px;
106
+ margin-bottom: 40px;
107
+ }
108
+
109
+ .form-grid {
110
+ display: grid;
111
+ gap: 20px;
112
+ }
113
+
114
+ .form-group label {
115
+ display: block;
116
+ margin-bottom: 8px;
117
+ font-size: 13px;
118
+ font-weight: 500;
119
+ color: var(--text-muted);
120
+ }
121
+
122
+ .form-group input {
123
+ width: 100%;
124
+ background: rgba(255, 255, 255, 0.04);
125
+ border: 1px solid var(--card-border);
126
+ border-radius: 14px;
127
+ padding: 13px 18px;
128
+ color: #fff;
129
+ font-size: 15px;
130
+ transition: all 0.3s;
131
+ outline: none;
132
+ }
133
+
134
+ .form-group input:focus {
135
+ background: rgba(255, 255, 255, 0.07);
136
+ border-color: var(--primary);
137
+ box-shadow: 0 0 0 4px var(--primary-glow);
138
+ }
139
+
140
+ .btn-primary {
141
+ width: 100%;
142
+ background: var(--primary);
143
+ color: white;
144
+ border: none;
145
+ border-radius: 14px;
146
+ padding: 15px;
147
+ font-size: 16px;
148
+ font-weight: 600;
149
+ cursor: pointer;
150
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
151
+ margin-top: 15px;
152
+ }
153
+
154
+ .btn-primary:hover {
155
+ transform: scale(1.02);
156
+ filter: brightness(1.1);
157
+ box-shadow: 0 15px 25px -5px rgba(34, 197, 94, 0.4);
158
+ }
159
+
160
+ .footer {
161
+ margin-top: 40px;
162
+ text-align: center;
163
+ font-size: 14px;
164
+ color: var(--text-muted);
165
+ }
166
+
167
+ .footer a {
168
+ color: var(--primary);
169
+ text-decoration: none;
170
+ font-weight: 600;
171
+ }
172
+
173
+ .back-home {
174
+ display: block;
175
+ margin-top: 20px;
176
+ font-size: 12px;
177
+ opacity: 0.5;
178
+ }
179
+ body
180
+ div.background-blobs
181
+ div.blob.blob-1
182
+ div.blob.blob-2
183
+ div.auth-card
184
+ h2 Join Us
185
+ p.subtitle Create your account in seconds
186
+ form.form-grid(action="/api/users" method="POST")
187
+ div.form-group
188
+ label(for="name") Full Name
189
+ input(type="text" id="name" name="name" placeholder="John Doe" required)
190
+ div.form-group
191
+ label(for="email") Email Address
192
+ input(type="email" id="email" name="email" placeholder="john@example.com" required)
193
+ div.form-group
194
+ label(for="password") Choose Password
195
+ input(type="password" id="password" name="password" placeholder="••••••••" required)
196
+ button.btn-primary(type="submit") Create Account
197
+ div.footer
198
+ p Already have an account?
199
+ a(href="/login") Sign in
200
+ a.back-home(href="/") ← Back to home
201
+
202
+ <% if (communication === 'GraphQL') { %>
203
+ script.
204
+ document.querySelector('form').addEventListener('submit', async (e) => {
205
+ e.preventDefault();
206
+ const formData = new FormData(e.target);
207
+ const data = Object.fromEntries(formData.entries());
208
+
209
+ const query = `
210
+ mutation CreateUser($name: String!, $email: String!, $password: String!) {
211
+ createUser(name: $name, email: $email, password: $password) {
212
+ id
213
+ name
214
+ email
215
+ }
216
+ }
217
+ `;
218
+
219
+ try {
220
+ const response = await fetch('/graphql', {
221
+ method: 'POST',
222
+ headers: { 'Content-Type': 'application/json' },
223
+ body: JSON.stringify({
224
+ query,
225
+ variables: data
226
+ })
227
+ });
228
+
229
+ const result = await response.json();
230
+ if (result.errors) {
231
+ alert('Error: ' + result.errors[0].message);
232
+ } else {
233
+ alert('Account created successfully! Please login.');
234
+ window.location.href = '/login';
235
+ }
236
+ } catch (err) {
237
+ console.error(err);
238
+ alert('An error occurred during signup.');
239
+ }
240
+ });
241
+ <% } %>
@@ -2,9 +2,15 @@ CREATE TABLE users (
2
2
  id INT AUTO_INCREMENT PRIMARY KEY,
3
3
  name VARCHAR(255) NOT NULL,
4
4
  email VARCHAR(255) NOT NULL UNIQUE,
5
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NOT NULL, <% } %>
5
6
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
6
7
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
7
8
  deleted_at TIMESTAMP NULL
8
9
  );
9
10
 
11
+ <% if (auth.includes('JWT')) { %>
12
+ INSERT INTO users (name, email, password) VALUES ('Admin User', 'admin@example.com', '$2a$10$X.fO9PeyF0Lq0lF8uV6G9u4Vb4e5T0rF8l/JzM6S7X9u4Vb4e5T0r'); -- password: password123
13
+ <% } else { %>
10
14
  INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
15
+ <% } %>
16
+
@@ -2,9 +2,15 @@ CREATE TABLE users (
2
2
  id SERIAL PRIMARY KEY,
3
3
  name VARCHAR(255) NOT NULL,
4
4
  email VARCHAR(255) NOT NULL UNIQUE,
5
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NOT NULL, <% } %>
5
6
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
6
7
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
7
8
  deleted_at TIMESTAMP NULL
8
9
  );
9
10
 
11
+ <% if (auth.includes('JWT')) { %>
12
+ INSERT INTO users (name, email, password) VALUES ('Admin User', 'admin@example.com', '$2a$10$X.fO9PeyF0Lq0lF8uV6G9u4Vb4e5T0rF8l/JzM6S7X9u4Vb4e5T0r'); -- password: password123
13
+ <% } else { %>
10
14
  INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
15
+ <% } %>
16
+
@@ -29,6 +29,12 @@ const envSchema = z.object({
29
29
  REDIS_PORT: z.string().transform(Number),
30
30
  REDIS_PASSWORD: z.string().optional(),
31
31
  <%_ } -%>
32
+ <%_ if (auth.includes('JWT')) { -%>
33
+ JWT_SECRET: z.string(),
34
+ JWT_EXPIRES_IN: z.string().default('15m'),
35
+ JWT_REFRESH_SECRET: z.string().default('your-secret-refresh-key'),
36
+ JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
37
+ <%_ } -%>
32
38
  <%_ if (communication === 'Kafka') { -%>
33
39
  KAFKA_BROKER: z.string(),
34
40
  <%_ } -%>
@@ -37,10 +43,13 @@ const envSchema = z.object({
37
43
  const _env = envSchema.safeParse(process.env);
38
44
 
39
45
  if (!_env.success) {
40
- logger.error('❌ Invalid environment variables:', _env.error.format());
41
- process.exit(1);
46
+ if (process.env.NODE_ENV !== 'test') {
47
+ logger.error('❌ Invalid environment variables:', _env.error.format());
48
+ process.exit(1);
49
+ }
50
+ logger.warn('⚠️ Environment validation failed. Continuing in test mode.');
42
51
  }
43
52
 
44
- const env = _env.data;
53
+ const env = _env.success ? _env.data : process.env;
45
54
 
46
55
  module.exports = { env };
@@ -13,7 +13,7 @@ const cacheService = require('../config/memoryCache');
13
13
  const { sendMessage } = require('../services/kafkaService');
14
14
  const { KAFKA_ACTIONS } = require('../utils/kafkaEvents');
15
15
  <%_ } -%>
16
-
16
+ <% if (auth.includes('JWT')) { %>const bcrypt = require('bcryptjs');<% } %>
17
17
  <% if (communication === 'GraphQL') { -%>
18
18
  const getUsers = async () => {
19
19
  try {
@@ -41,8 +41,19 @@ const getUsers = async () => {
41
41
 
42
42
  const createUser = async (data) => {
43
43
  try {
44
- const { name, email } = data;
44
+ const { name, email<% if (auth.includes('JWT')) { %>, password<% } %> } = data;
45
+ <% if (auth.includes('JWT')) { %>
46
+ let user;
47
+ if (password) {
48
+ const hashedPassword = await bcrypt.hash(password, 10);
49
+ user = await User.create({ name, email, password: hashedPassword });
50
+ } else {
51
+ user = await User.create({ name, email });
52
+ }
53
+ <% } else { %>
45
54
  const user = await User.create({ name, email });
55
+ <% } %>
56
+
46
57
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
47
58
  await cacheService.del('users:all');
48
59
  <%_ } -%>
@@ -52,7 +63,9 @@ const createUser = async (data) => {
52
63
  payload: { id: user.id || user._id, email: user.email }
53
64
  }), (user.id || user._id).toString());
54
65
  <%_ } -%>
55
- return user;
66
+ const userObj = user.toJSON ? user.toJSON() : { ...user };
67
+ delete userObj.password;
68
+ return userObj;
56
69
  } catch (error) {
57
70
  logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
58
71
  throw error;
@@ -148,8 +161,17 @@ const getUsers = async (req, res, next) => {
148
161
 
149
162
  const createUser = async (req, res, next) => {
150
163
  try {
151
- const { name, email } = req.body || {};
164
+ const { name, email, password } = req.body || {};
165
+ <% if (auth.includes('JWT')) { %>
166
+ if (!password) {
167
+ return res.status(HTTP_STATUS.BAD_REQUEST).json({ error: 'Password is required' });
168
+ }
169
+ const hashedPassword = await bcrypt.hash(password, 10);
170
+ const user = await User.create({ name, email, password: hashedPassword });
171
+ <% } else { %>
152
172
  const user = await User.create({ name, email });
173
+ <% } %>
174
+
153
175
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
154
176
  await cacheService.del('users:all');
155
177
  <%_ } -%>
@@ -159,7 +181,9 @@ const createUser = async (req, res, next) => {
159
181
  payload: { id: user.id || user._id, email: user.email }
160
182
  }), (user.id || user._id).toString());
161
183
  <%_ } -%>
162
- res.status(HTTP_STATUS.CREATED).json(user);
184
+ const userObj = user.toJSON ? user.toJSON() : { ...user };
185
+ delete userObj.password;
186
+ res.status(HTTP_STATUS.CREATED).json(userObj);
163
187
  } catch (error) {
164
188
  logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
165
189
  next(error);
@@ -1,5 +1,9 @@
1
+ <% if (communication !== 'GraphQL') { -%>
1
2
  const HTTP_STATUS = require('@/utils/httpCodes');
3
+ <% } -%>
4
+ <% if (communication === 'GraphQL') { -%>
2
5
  const ERROR_MESSAGES = require('@/utils/errorMessages');
6
+ <% } -%>
3
7
  <% if (communication !== 'GraphQL') { -%>
4
8
  // Express-only imports would go here
5
9
  <% } -%>
@@ -40,6 +44,11 @@ jest.mock('@/config/memoryCache', () => ({
40
44
  }));
41
45
  <%_ } -%>
42
46
  jest.mock('@/utils/logger');
47
+ <%_ if (auth.includes('JWT')) { _%>
48
+ jest.mock('bcryptjs', () => ({
49
+ hash: jest.fn().mockResolvedValue('hashed_password')
50
+ }));
51
+ <%_ } _%>
43
52
  <%_ if (communication === 'Kafka') { -%>
44
53
  jest.mock('@/services/kafkaService', () => {
45
54
  const mockSendMessage = jest.fn().mockResolvedValue(undefined);
@@ -166,7 +175,7 @@ describe('UserController', () => {
166
175
  describe('createUser', () => {
167
176
  it('should successfully create a new user (Happy Path)', async () => {
168
177
  // Arrange
169
- const payload = { name: 'Alice', email: 'alice@example.com' };
178
+ const payload = { name: 'Alice', email: 'alice@example.com', password: 'password123' };
170
179
  <% if (communication === 'GraphQL') { -%>
171
180
  const dataArg = payload;
172
181
  <% } else { -%>
@@ -185,20 +194,26 @@ describe('UserController', () => {
185
194
  const result = await createUser(dataArg);
186
195
 
187
196
  // Assert
188
- <%_ if (database === 'None') { -%>
197
+ expect(result.password).toBeUndefined();
189
198
  expect(result.name).toBe(payload.name);
190
199
  expect(result.email).toBe(payload.email);
191
- <%_ } else { -%>
192
- expect(result).toEqual(expectedUser);
193
- expect(User.create).toHaveBeenCalledWith(payload);
194
- <%_ } -%>
200
+
201
+ expect(User.create).toHaveBeenCalledWith({
202
+ name: payload.name,
203
+ email: payload.email
204
+ <%_ if (auth.includes('JWT')) { _%>, password: 'hashed_password'<%_ } _%>
205
+ });
195
206
  <% } else { -%>
196
207
  await createUser(mockRequest, mockResponse, mockNext);
197
208
 
198
209
  // Assert
199
210
  expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
200
- expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
201
- expect(User.create).toHaveBeenCalledWith(payload);
211
+ expect(mockResponse.json).toHaveBeenCalledWith(expect.not.objectContaining({ password: expect.anything() }));
212
+ expect(User.create).toHaveBeenCalledWith({
213
+ name: payload.name,
214
+ email: payload.email
215
+ <%_ if (auth.includes('JWT')) { _%>, password: 'hashed_password'<%_ } _%>
216
+ });
202
217
  <%_ } -%>
203
218
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
204
219
  expect(cacheService.del).toHaveBeenCalledWith('users:all');
@@ -211,7 +226,7 @@ describe('UserController', () => {
211
226
  it('should handle errors when creation fails (Error Handling)', async () => {
212
227
  // Arrange
213
228
  const error = new Error('Creation Error');
214
- const payload = { name: 'Bob', email: 'bob@example.com' };
229
+ const payload = { name: 'Bob', email: 'bob@example.com', password: 'password123' };
215
230
  <% if (communication === 'GraphQL') { -%>
216
231
  const dataArg = payload;
217
232
  <% } else { -%>
@@ -232,7 +247,7 @@ describe('UserController', () => {
232
247
  <%_ if (communication === 'Kafka') { -%>
233
248
  it('should successfully create a new user with _id for Kafka (Happy Path)', async () => {
234
249
  // Arrange
235
- const payload = { name: 'Bob', email: 'bob@example.com' };
250
+ const payload = { name: 'Bob', email: 'bob@example.com', password: 'password123' };
236
251
  <% if (communication === 'GraphQL') { -%>
237
252
  const dataArg = payload;
238
253
  <% } else { -%>
@@ -446,9 +461,9 @@ describe('UserController', () => {
446
461
  const error = new Error('Database Error');
447
462
  User.create.mockRejectedValue(error);
448
463
  <% if (communication === 'GraphQL') { -%>
449
- await expect(createUser({ name: 'Alice', email: 'alice@example.com' })).rejects.toThrow(error);
464
+ await expect(createUser({ name: 'Alice', email: 'alice@example.com'<% if (auth.includes('JWT')) { %>, password: 'password123'<% } %> })).rejects.toThrow(error);
450
465
  <% } else { -%>
451
- mockRequest.body = { name: 'Alice', email: 'alice@example.com' };
466
+ mockRequest.body = { name: 'Alice', email: 'alice@example.com', password: 'password123' };
452
467
  await createUser(mockRequest, mockResponse, mockNext);
453
468
  expect(mockNext).toHaveBeenCalledWith(error);
454
469
  <% } -%>
@@ -1,7 +1,18 @@
1
+ <% if (auth.includes('JWT')) { %>const JwtService = require('../services/jwtService');<% } %>
2
+
1
3
  const gqlContext = async ({ req }) => {
2
- // Setup authorization or context here
3
- const token = req.headers.authorization || '';
4
- return { token };
4
+ const context = {};
5
+ <% if (auth.includes('JWT')) { %>
6
+ const authHeader = req.headers.authorization || '';
7
+ if (authHeader.startsWith('Bearer ')) {
8
+ const token = authHeader.split(' ')[1];
9
+ const user = JwtService.verifyToken(token);
10
+ if (user) {
11
+ context.user = user;
12
+ }
13
+ }<% } %>
14
+
15
+ return context;
5
16
  };
6
17
 
7
18
  module.exports = { gqlContext };