nodejs-quickstart-structure 2.1.2 → 2.2.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 (66) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +12 -17
  3. package/bin/index.js +1 -0
  4. package/lib/generator.js +1 -1
  5. package/lib/modules/app-setup.js +16 -0
  6. package/lib/modules/auth-setup.js +46 -4
  7. package/lib/prompts.js +44 -4
  8. package/package.json +1 -1
  9. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -2
  10. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +27 -0
  11. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +24 -0
  12. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -1
  13. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -2
  14. package/templates/clean-architecture/ts/src/domain/user.ts.ejs +14 -0
  15. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +24 -0
  16. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +43 -45
  17. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +5 -5
  18. package/templates/common/.env.example.ejs +10 -0
  19. package/templates/common/README.md.ejs +65 -14
  20. package/templates/common/auth/js/controllers/authController.js.ejs +326 -13
  21. package/templates/common/auth/js/controllers/authController.spec.js.ejs +237 -51
  22. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +10 -6
  23. package/templates/common/auth/js/routes/authRoutes.js.ejs +11 -0
  24. package/templates/common/auth/js/services/jwtService.js.ejs +3 -3
  25. package/templates/common/auth/js/services/jwtService.spec.js.ejs +30 -0
  26. package/templates/common/auth/js/services/socialAuthService.js.ejs +175 -0
  27. package/templates/common/auth/js/services/socialAuthService.spec.js.ejs +194 -0
  28. package/templates/common/auth/js/usecases/SocialLoginUseCase.js.ejs +114 -0
  29. package/templates/common/auth/js/usecases/SocialLoginUseCase.spec.js.ejs +143 -0
  30. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +344 -64
  31. package/templates/common/auth/ts/controllers/authController.ts.ejs +341 -9
  32. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +10 -6
  33. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +11 -0
  34. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +18 -0
  35. package/templates/common/auth/ts/services/jwtService.ts.ejs +3 -3
  36. package/templates/common/auth/ts/services/socialAuthService.spec.ts.ejs +187 -0
  37. package/templates/common/auth/ts/services/socialAuthService.ts.ejs +189 -0
  38. package/templates/common/auth/ts/usecases/SocialLoginUseCase.spec.ts.ejs +143 -0
  39. package/templates/common/auth/ts/usecases/SocialLoginUseCase.ts.ejs +117 -0
  40. package/templates/common/database/js/models/User.js.ejs +13 -5
  41. package/templates/common/database/js/models/User.js.mongoose.ejs +15 -1
  42. package/templates/common/database/ts/models/User.ts.ejs +23 -7
  43. package/templates/common/database/ts/models/User.ts.mongoose.ejs +18 -2
  44. package/templates/common/docker-compose.yml.ejs +21 -0
  45. package/templates/common/ecosystem.config.js.ejs +10 -0
  46. package/templates/common/jest.config.js.ejs +1 -1
  47. package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
  48. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +1 -1
  49. package/templates/common/package.json.ejs +2 -0
  50. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +13 -1
  51. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +13 -1
  52. package/templates/common/swagger.yml.ejs +62 -3
  53. package/templates/common/views/ejs/login.ejs.ejs +84 -0
  54. package/templates/common/views/ejs/signup.ejs.ejs +84 -0
  55. package/templates/common/views/pug/login.pug.ejs +78 -0
  56. package/templates/common/views/pug/signup.pug.ejs +78 -0
  57. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +3 -1
  58. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +3 -1
  59. package/templates/mvc/js/src/config/env.js.ejs +12 -2
  60. package/templates/mvc/js/src/controllers/userController.js.ejs +1 -1
  61. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +4 -3
  62. package/templates/mvc/ts/src/config/env.ts.ejs +12 -2
  63. package/templates/mvc/ts/src/controllers/userController.ts.ejs +1 -0
  64. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +5 -5
  65. package/templates/mvc/ts/src/index.ts.ejs +2 -1
  66. package/templates/clean-architecture/ts/src/domain/user.ts +0 -9
@@ -176,6 +176,63 @@ html(lang="en")
176
176
  font-size: 12px;
177
177
  opacity: 0.5;
178
178
  }
179
+
180
+ .divider {
181
+ display: flex;
182
+ align-items: center;
183
+ text-align: center;
184
+ margin: 24px 0;
185
+ color: var(--text-muted);
186
+ font-size: 13px;
187
+ }
188
+
189
+ .divider::before,
190
+ .divider::after {
191
+ content: '';
192
+ flex: 1;
193
+ border-bottom: 1px solid var(--card-border);
194
+ }
195
+
196
+ .divider:not(:empty)::before {
197
+ margin-right: .5em;
198
+ }
199
+
200
+ .divider:not(:empty)::after {
201
+ margin-left: .5em;
202
+ }
203
+
204
+ .social-login {
205
+ display: grid;
206
+ grid-template-columns: 1fr 1fr;
207
+ gap: 12px;
208
+ }
209
+
210
+ .btn-social {
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ gap: 8px;
215
+ background: rgba(255, 255, 255, 0.05);
216
+ border: 1px solid var(--card-border);
217
+ border-radius: 12px;
218
+ padding: 12px;
219
+ color: #fff;
220
+ font-size: 14px;
221
+ font-weight: 500;
222
+ cursor: pointer;
223
+ transition: all 0.3s ease;
224
+ text-decoration: none;
225
+ }
226
+
227
+ .btn-social:hover {
228
+ background: rgba(255, 255, 255, 0.08);
229
+ border-color: var(--text-muted);
230
+ transform: translateY(-1px);
231
+ }
232
+
233
+ .social-full {
234
+ grid-column: span 2;
235
+ }
179
236
  body
180
237
  div.background-blobs
181
238
  div.blob.blob-1
@@ -194,6 +251,27 @@ html(lang="en")
194
251
  label(for="password") Choose Password
195
252
  input(type="password" id="password" name="password" placeholder="••••••••" required)
196
253
  button.btn-primary(type="submit") Create Account
254
+
255
+ <% if (socialAuth && socialAuth.filter(a => a !== 'None').length > 0) { %>
256
+ div.divider Or join with
257
+ div.social-login
258
+ <% if (socialAuth.includes('Google')) { %>
259
+ a.btn-social(href="/api/auth/google" class="<%= socialAuth.includes('GitHub') ? '' : 'social-full' %>")
260
+ svg(width="20" height="20" viewBox="0 0 24 24" fill="currentColor")
261
+ path(d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4")
262
+ path(d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853")
263
+ path(d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05")
264
+ path(d="M12 5.38c1.62 0 3.06.56 4.21 1.66l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335")
265
+ | Google
266
+ <% } %>
267
+ <% if (socialAuth.includes('GitHub')) { %>
268
+ a.btn-social(href="/api/auth/github" class="<%= socialAuth.includes('Google') ? '' : 'social-full' %>")
269
+ svg(width="20" height="20" viewBox="0 0 24 24" fill="currentColor")
270
+ path(d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12")
271
+ | GitHub
272
+ <% } %>
273
+ <% } %>
274
+
197
275
  div.footer
198
276
  p Already have an account?
199
277
  a(href="/login") Sign in
@@ -2,7 +2,9 @@ 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
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NULL, <% } %>
6
+ <% if (socialAuth && socialAuth.includes('Google')) { %>google_id VARCHAR(255) NULL UNIQUE, <% } %>
7
+ <% if (socialAuth && socialAuth.includes('GitHub')) { %>github_id VARCHAR(255) NULL UNIQUE, <% } %>
6
8
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
7
9
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
8
10
  deleted_at TIMESTAMP NULL
@@ -2,7 +2,9 @@ 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
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NULL, <% } %>
6
+ <% if (socialAuth && socialAuth.includes('Google')) { %>google_id VARCHAR(255) NULL UNIQUE, <% } %>
7
+ <% if (socialAuth && socialAuth.includes('GitHub')) { %>github_id VARCHAR(255) NULL UNIQUE, <% } %>
6
8
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
7
9
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
8
10
  deleted_at TIMESTAMP NULL
@@ -29,12 +29,22 @@ 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')) { -%>
32
+ <%_ if (auth.includes('JWT')) { _%>
33
33
  JWT_SECRET: z.string(),
34
34
  JWT_EXPIRES_IN: z.string().default('15m'),
35
35
  JWT_REFRESH_SECRET: z.string().default('your-secret-refresh-key'),
36
36
  JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
37
- <%_ } -%>
37
+ <%_ if (socialAuth && socialAuth.includes('Google')) { _%>
38
+ GOOGLE_CLIENT_ID: z.string(),
39
+ GOOGLE_CLIENT_SECRET: z.string(),
40
+ GOOGLE_CALLBACK_URL: z.string().optional(),
41
+ <%_ } _%>
42
+ <%_ if (socialAuth && socialAuth.includes('GitHub')) { _%>
43
+ GITHUB_CLIENT_ID: z.string(),
44
+ GITHUB_CLIENT_SECRET: z.string(),
45
+ GITHUB_CALLBACK_URL: z.string().optional(),
46
+ <%_ } _%>
47
+ <%_ } _%>
38
48
  <%_ if (communication === 'Kafka') { -%>
39
49
  KAFKA_BROKER: z.string(),
40
50
  <%_ } -%>
@@ -266,5 +266,5 @@ const deleteUser = async (req, res, next) => {
266
266
  }
267
267
  };
268
268
 
269
- module.exports = { getUsers, createUser, updateUser, deleteUser };
269
+ module.exports = { getUsers, createUser, updateUser, deleteUser, model: User };
270
270
  <% } -%>
@@ -17,11 +17,12 @@ const userResolvers = {
17
17
  <% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
18
18
  return await userController.updateUser(id, { name, email });
19
19
  },
20
- deleteUser: async (_, { id }, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
21
- <% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
20
+ deleteUser: async (_, { id }, <% if (auth.includes('JWT')) { %>{ user: authUser }<% } else { %>_context<% } %>) => {
21
+ <% if (auth.includes('JWT')) { %>if (!authUser) throw new Error('Unauthorized');<% } %>
22
22
  return await userController.deleteUser(id);
23
23
  }
24
- }<%_ if (database === 'MongoDB') { -%>,
24
+ }<%_ if (database === 'MongoDB') { -%>
25
+ ,
25
26
  User: {
26
27
  id: (parent) => parent.id || parent._id
27
28
  }<%_ } %>
@@ -33,12 +33,22 @@ const envSchema = z.object({
33
33
  <%_ if (communication === 'Kafka') { -%>
34
34
  KAFKA_BROKER: z.string(),
35
35
  <%_ } -%>
36
- <%_ if (auth.includes('JWT')) { -%>
36
+ <%_ if (auth.includes('JWT')) { _%>
37
37
  JWT_SECRET: z.string(),
38
38
  JWT_EXPIRES_IN: z.string().default('15m'),
39
39
  JWT_REFRESH_SECRET: z.string().default('your-secret-refresh-key'),
40
40
  JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
41
- <%_ } -%>
41
+ <%_ if (socialAuth && socialAuth.includes('Google')) { _%>
42
+ GOOGLE_CLIENT_ID: z.string(),
43
+ GOOGLE_CLIENT_SECRET: z.string(),
44
+ GOOGLE_CALLBACK_URL: z.string().optional(),
45
+ <%_ } _%>
46
+ <%_ if (socialAuth && socialAuth.includes('GitHub')) { _%>
47
+ GITHUB_CLIENT_ID: z.string(),
48
+ GITHUB_CLIENT_SECRET: z.string(),
49
+ GITHUB_CALLBACK_URL: z.string().optional(),
50
+ <%_ } _%>
51
+ <%_ } _%>
42
52
  });
43
53
 
44
54
  const _env = envSchema.safeParse(process.env);
@@ -17,6 +17,7 @@ import bcrypt from 'bcryptjs';
17
17
  <%_ } -%>
18
18
 
19
19
  export class UserController {
20
+ static model = User;
20
21
  <% if (communication === 'GraphQL') { -%>
21
22
  async getUsers() {
22
23
  try {
@@ -25,15 +25,15 @@ export const userResolvers = {
25
25
  const user = await userController.updateUser(id, { name, email });
26
26
  return user;
27
27
  },
28
- deleteUser: async (_: unknown, { id }: { id: string }, <% if (auth.includes('JWT')) { %>context<% } else { %>_context<% } %>: MyContext) => {
29
- <%_ if (auth.includes('JWT')) { -%>
28
+ deleteUser: async (_: unknown, { id }: { id: string }, <%_ if (auth.includes('JWT')) { _%>context<%_ } else { _%>_context<%_ } _%>: MyContext) => {
29
+ <%_ if (auth.includes('JWT')) { _%>
30
30
  if (!context.user) throw new Error('Unauthorized');
31
- <%_ } -%>
31
+ <%_ } _%>
32
32
  const result = await userController.deleteUser(id);
33
33
  return result;
34
34
  }
35
- }<%_ if (database === 'MongoDB') { -%>,
35
+ }<%_ if (database === 'MongoDB') { _%>,
36
36
  User: {
37
37
  id: (parent: { id?: string; _id?: unknown }) => parent.id || parent._id
38
- }<%_ } %>
38
+ }<%_ } _%>
39
39
  };
@@ -66,7 +66,8 @@ app.set('view engine', '<%= viewEngine.toLowerCase() %>');
66
66
  app.use(express.static(path.join(__dirname, '../public')));<%_ } %>
67
67
  <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
68
68
  app.use('/api', apiRoutes);
69
- <%_ } -%><%_ if (auth.includes('JWT')) { -%>
69
+ <%_ } -%>
70
+ <%_ if (auth.includes('JWT')) { -%>
70
71
  app.use('/api/auth', authRoutes);
71
72
  <%_ } -%>
72
73
 
@@ -1,9 +0,0 @@
1
- export class User {
2
- constructor(
3
- public id: number | string | null,
4
- public name: string,
5
- public email: string,
6
- public password?: string
7
- ) { }
8
-
9
- }