express-genix 1.1.4 → 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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -259
  3. package/index.js +229 -113
  4. package/lib/cleanup.js +41 -129
  5. package/lib/features.js +239 -0
  6. package/lib/generator.js +286 -204
  7. package/lib/utils.js +43 -91
  8. package/package.json +81 -63
  9. package/templates/cicd/github-actions.yml.ejs +70 -0
  10. package/templates/config/database.mongo.js.ejs +29 -33
  11. package/templates/config/database.postgres.js.ejs +41 -40
  12. package/templates/config/database.prisma.js.ejs +26 -0
  13. package/templates/config/redis.js.ejs +28 -0
  14. package/templates/config/schema.prisma.ejs +20 -0
  15. package/templates/config/swagger.js.ejs +30 -0
  16. package/templates/config/websocket.js.ejs +62 -0
  17. package/templates/controllers/authController.js.ejs +152 -129
  18. package/templates/controllers/exampleController.js.ejs +92 -152
  19. package/templates/controllers/userController.js.ejs +52 -60
  20. package/templates/core/Dockerfile.ejs +41 -31
  21. package/templates/core/README.md.ejs +191 -179
  22. package/templates/core/app.js.ejs +114 -64
  23. package/templates/core/docker-compose.yml.ejs +59 -47
  24. package/templates/core/dockerignore.ejs +7 -0
  25. package/templates/core/env.ejs +25 -19
  26. package/templates/core/env.example.ejs +26 -0
  27. package/templates/core/eslintrc.json.ejs +50 -20
  28. package/templates/core/gitignore.ejs +51 -51
  29. package/templates/core/healthcheck.js.ejs +24 -24
  30. package/templates/core/jest.config.js.ejs +19 -22
  31. package/templates/core/package.json.ejs +70 -33
  32. package/templates/core/prettierrc.json.ejs +11 -11
  33. package/templates/core/server.js.ejs +64 -48
  34. package/templates/core/tsconfig.json.ejs +19 -0
  35. package/templates/middleware/auth.js.ejs +80 -66
  36. package/templates/middleware/cache.js.ejs +67 -0
  37. package/templates/middleware/errorHandler.js.ejs +50 -46
  38. package/templates/middleware/requestId.js.ejs +9 -0
  39. package/templates/middleware/validation.js.ejs +109 -47
  40. package/templates/migrations/create-users.js.ejs +50 -0
  41. package/templates/migrations/seed-users.js.ejs +34 -0
  42. package/templates/migrations/sequelizerc.ejs +8 -0
  43. package/templates/models/User.mongo.js.ejs +29 -29
  44. package/templates/models/User.postgres.js.ejs +40 -40
  45. package/templates/models/index.mongo.js.ejs +7 -7
  46. package/templates/models/index.postgres.js.ejs +11 -11
  47. package/templates/routes/authRoutes.js.ejs +222 -13
  48. package/templates/routes/exampleRoutes.js.ejs +100 -12
  49. package/templates/routes/index.js.ejs +34 -24
  50. package/templates/routes/userRoutes.js.ejs +78 -15
  51. package/templates/services/authService.js.ejs +111 -35
  52. package/templates/services/exampleService.js.ejs +112 -112
  53. package/templates/services/userService.mongodb.js.ejs +33 -33
  54. package/templates/services/userService.postgres.js.ejs +30 -30
  55. package/templates/services/userService.prisma.js.ejs +36 -0
  56. package/templates/tests/auth.test.js.ejs +83 -66
  57. package/templates/tests/example.test.js.ejs +109 -112
  58. package/templates/tests/setup.js.ejs +11 -11
  59. package/templates/tests/users.test.js.ejs +42 -42
  60. package/templates/utils/envValidator.js.ejs +23 -0
  61. package/templates/utils/errors.js.ejs +12 -12
  62. package/templates/utils/logger.js.ejs +37 -28
  63. package/templates/utils/response.js.ejs +28 -0
  64. package/templates/utils/validators.js.ejs +34 -34
  65. package/templates/config/swagger.json.ejs +0 -194
  66. package/templates/core/index.js.ejs +0 -24
@@ -1,16 +1,79 @@
1
- // templates/routes/userRoutes.js.ejs
2
- const express = require('express');
3
- const userController = require('../controllers/userController');
4
- const authController = require('../controllers/authController');
5
-
6
- const router = express.Router();
7
-
8
- // All user routes require authentication
9
- router.use(authController.authenticateToken);
10
-
11
- // User routes
12
- router.get('/profile', userController.getProfile);
13
- router.put('/profile', userController.updateProfile);
14
- router.delete('/profile', userController.deleteProfile);
15
-
1
+ const express = require('express');
2
+ const userController = require('../controllers/userController');
3
+ const { authenticateToken } = require('../middleware/auth');
4
+
5
+ const router = express.Router();
6
+
7
+ /**
8
+ * @swagger
9
+ * /users/profile:
10
+ * get:
11
+ * summary: Get user profile
12
+ * tags: [Users]
13
+ * security:
14
+ * - bearerAuth: []
15
+ * responses:
16
+ * 200:
17
+ * description: User profile retrieved
18
+ * content:
19
+ * application/json:
20
+ * schema:
21
+ * type: object
22
+ * properties:
23
+ * success:
24
+ * type: boolean
25
+ * data:
26
+ * type: object
27
+ * properties:
28
+ * user:
29
+ * type: object
30
+ * properties:
31
+ * id:
32
+ * type: string
33
+ * username:
34
+ * type: string
35
+ * email:
36
+ * type: string
37
+ * createdAt:
38
+ * type: string
39
+ * format: date-time
40
+ * 401:
41
+ * description: Unauthorized — provide a Bearer token from login
42
+ * put:
43
+ * summary: Update user profile
44
+ * tags: [Users]
45
+ * security:
46
+ * - bearerAuth: []
47
+ * requestBody:
48
+ * required: true
49
+ * content:
50
+ * application/json:
51
+ * schema:
52
+ * type: object
53
+ * properties:
54
+ * username:
55
+ * type: string
56
+ * example: newusername
57
+ * responses:
58
+ * 200:
59
+ * description: Profile updated
60
+ * 401:
61
+ * description: Unauthorized
62
+ * delete:
63
+ * summary: Delete user profile
64
+ * tags: [Users]
65
+ * security:
66
+ * - bearerAuth: []
67
+ * responses:
68
+ * 200:
69
+ * description: Profile deleted
70
+ * 401:
71
+ * description: Unauthorized
72
+ */
73
+ router.use(authenticateToken);
74
+
75
+ router.get('/profile', userController.getProfile);
76
+ router.put('/profile', userController.updateProfile);
77
+ router.delete('/profile', userController.deleteProfile);
78
+
16
79
  module.exports = router;
@@ -1,36 +1,112 @@
1
- const jwt = require('jsonwebtoken');
2
-
3
- const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
4
- const jwtRefreshSecret = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret-key';
5
-
6
- const generateTokens = (user) => {
7
- const payload = {
8
- userId: user.id,
9
- username: user.username,
10
- email: user.email,
11
- };
12
-
13
- const accessToken = jwt.sign(payload, jwtSecret, {
14
- expiresIn: process.env.JWT_EXPIRE || '15m',
15
- });
16
-
17
- const refreshToken = jwt.sign({ userId: user.id }, jwtRefreshSecret, {
18
- expiresIn: process.env.JWT_REFRESH_EXPIRE || '7d',
19
- });
20
-
21
- return { accessToken, refreshToken };
22
- };
23
-
24
- const verifyToken = (token) => {
25
- return jwt.verify(token, jwtSecret);
26
- };
27
-
28
- const verifyRefreshToken = (token) => {
29
- return jwt.verify(token, jwtRefreshSecret);
30
- };
31
-
32
- module.exports = {
33
- generateTokens,
34
- verifyToken,
35
- verifyRefreshToken,
1
+ const jwt = require('jsonwebtoken');
2
+ const crypto = require('crypto');
3
+ <% if (hasRedis) { %>const { redis } = require('../config/redis');
4
+ <% } %>
5
+ <% if (!hasRedis) { %>
6
+ // In-memory token blacklist replace with Redis for production.
7
+ const tokenBlacklist = new Set();
8
+ const resetTokens = new Map(); // token → { email, expiresAt }
9
+ <% } %>
10
+ const generateTokens = (user) => {
11
+ const payload = {
12
+ userId: user.id,
13
+ username: user.username,
14
+ email: user.email,
15
+ };
16
+
17
+ const accessToken = jwt.sign(payload, process.env.JWT_SECRET, {
18
+ expiresIn: process.env.JWT_EXPIRE || '15m',
19
+ });
20
+
21
+ const refreshToken = jwt.sign({ userId: user.id }, process.env.JWT_REFRESH_SECRET, {
22
+ expiresIn: process.env.JWT_REFRESH_EXPIRE || '7d',
23
+ });
24
+
25
+ return { accessToken, refreshToken };
26
+ };
27
+
28
+ const verifyToken = (token) => jwt.verify(token, process.env.JWT_SECRET);
29
+
30
+ const verifyRefreshToken = (token) => jwt.verify(token, process.env.JWT_REFRESH_SECRET);
31
+ <% if (hasRedis) { %>
32
+ const blacklistToken = async (token) => {
33
+ const decoded = jwt.decode(token);
34
+ const ttl = decoded && decoded.exp ? decoded.exp - Math.floor(Date.now() / 1000) : 86400;
35
+ if (ttl > 0) {
36
+ await redis.set(`bl:${token}`, '1', 'EX', ttl);
37
+ }
38
+ };
39
+
40
+ const isTokenBlacklisted = async (token) => {
41
+ const result = await redis.get(`bl:${token}`);
42
+ return result !== null;
43
+ };
44
+
45
+ const generateResetToken = async (email) => {
46
+ const token = crypto.randomBytes(32).toString('hex');
47
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
48
+ await redis.set(`reset:${hash}`, email, 'EX', 3600); // 1 hour
49
+ return token;
50
+ };
51
+
52
+ const verifyResetToken = async (token) => {
53
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
54
+ const email = await redis.get(`reset:${hash}`);
55
+ return email;
56
+ };
57
+
58
+ const consumeResetToken = async (token) => {
59
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
60
+ const email = await redis.get(`reset:${hash}`);
61
+ if (email) {
62
+ await redis.del(`reset:${hash}`);
63
+ }
64
+ return email;
65
+ };
66
+ <% } else { %>
67
+ const blacklistToken = (token) => {
68
+ tokenBlacklist.add(token);
69
+ };
70
+
71
+ const isTokenBlacklisted = (token) => {
72
+ return tokenBlacklist.has(token);
73
+ };
74
+
75
+ const generateResetToken = (email) => {
76
+ const token = crypto.randomBytes(32).toString('hex');
77
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
78
+ resetTokens.set(hash, { email, expiresAt: Date.now() + 3600000 }); // 1 hour
79
+ return token;
80
+ };
81
+
82
+ const verifyResetToken = (token) => {
83
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
84
+ const entry = resetTokens.get(hash);
85
+ if (!entry || entry.expiresAt < Date.now()) {
86
+ resetTokens.delete(hash);
87
+ return null;
88
+ }
89
+ return entry.email;
90
+ };
91
+
92
+ const consumeResetToken = (token) => {
93
+ const hash = crypto.createHash('sha256').update(token).digest('hex');
94
+ const entry = resetTokens.get(hash);
95
+ if (!entry || entry.expiresAt < Date.now()) {
96
+ resetTokens.delete(hash);
97
+ return null;
98
+ }
99
+ resetTokens.delete(hash);
100
+ return entry.email;
101
+ };
102
+ <% } %>
103
+ module.exports = {
104
+ generateTokens,
105
+ verifyToken,
106
+ verifyRefreshToken,
107
+ blacklistToken,
108
+ isTokenBlacklisted,
109
+ generateResetToken,
110
+ verifyResetToken,
111
+ consumeResetToken,
36
112
  };
@@ -1,113 +1,113 @@
1
- const { createLogger } = require('../utils/logger');
2
-
3
- const logger = createLogger('ExampleService');
4
-
5
- // In-memory storage for examples (replace with database in real applications)
6
- let examples = [
7
- {
8
- id: '1',
9
- title: 'Example 1',
10
- description: 'This is the first example',
11
- createdAt: new Date('2024-01-01'),
12
- updatedAt: new Date('2024-01-01'),
13
- },
14
- {
15
- id: '2',
16
- title: 'Example 2',
17
- description: 'This is the second example',
18
- createdAt: new Date('2024-01-02'),
19
- updatedAt: new Date('2024-01-02'),
20
- },
21
- ];
22
-
23
- let nextId = 3;
24
-
25
- /**
26
- * Get all examples with pagination
27
- */
28
- const getAllExamples = async (page = 1, limit = 10) => {
29
- logger.info('Fetching all examples', { page, limit });
30
-
31
- const startIndex = (page - 1) * limit;
32
- const endIndex = startIndex + limit;
33
-
34
- const paginatedExamples = examples.slice(startIndex, endIndex);
35
-
36
- return {
37
- examples: paginatedExamples,
38
- total: examples.length,
39
- totalPages: Math.ceil(examples.length / limit),
40
- currentPage: page,
41
- };
42
- };
43
-
44
- /**
45
- * Get example by ID
46
- */
47
- const getExampleById = async (id) => {
48
- logger.info('Fetching example by ID', { id });
49
-
50
- const example = examples.find(ex => ex.id === id);
51
- return example || null;
52
- };
53
-
54
- /**
55
- * Create new example
56
- */
57
- const createExample = async (exampleData) => {
58
- logger.info('Creating new example', { title: exampleData.title });
59
-
60
- const newExample = {
61
- id: nextId.toString(),
62
- ...exampleData,
63
- };
64
-
65
- examples.push(newExample);
66
- nextId++;
67
-
68
- return newExample;
69
- };
70
-
71
- /**
72
- * Update example by ID
73
- */
74
- const updateExample = async (id, updateData) => {
75
- logger.info('Updating example', { id });
76
-
77
- const index = examples.findIndex(ex => ex.id === id);
78
-
79
- if (index === -1) {
80
- return null;
81
- }
82
-
83
- examples[index] = {
84
- ...examples[index],
85
- ...updateData,
86
- };
87
-
88
- return examples[index];
89
- };
90
-
91
- /**
92
- * Delete example by ID
93
- */
94
- const deleteExample = async (id) => {
95
- logger.info('Deleting example', { id });
96
-
97
- const index = examples.findIndex(ex => ex.id === id);
98
-
99
- if (index === -1) {
100
- return false;
101
- }
102
-
103
- examples.splice(index, 1);
104
- return true;
105
- };
106
-
107
- module.exports = {
108
- getAllExamples,
109
- getExampleById,
110
- createExample,
111
- updateExample,
112
- deleteExample,
1
+ const { createLogger } = require('../utils/logger');
2
+
3
+ const logger = createLogger('ExampleService');
4
+
5
+ // In-memory storage for examples (replace with database in real applications)
6
+ let examples = [
7
+ {
8
+ id: '1',
9
+ title: 'Example 1',
10
+ description: 'This is the first example',
11
+ createdAt: new Date('2024-01-01'),
12
+ updatedAt: new Date('2024-01-01'),
13
+ },
14
+ {
15
+ id: '2',
16
+ title: 'Example 2',
17
+ description: 'This is the second example',
18
+ createdAt: new Date('2024-01-02'),
19
+ updatedAt: new Date('2024-01-02'),
20
+ },
21
+ ];
22
+
23
+ let nextId = 3;
24
+
25
+ /**
26
+ * Get all examples with pagination
27
+ */
28
+ const getAllExamples = async (page = 1, limit = 10) => {
29
+ logger.info('Fetching all examples', { page, limit });
30
+
31
+ const startIndex = (page - 1) * limit;
32
+ const endIndex = startIndex + limit;
33
+
34
+ const paginatedExamples = examples.slice(startIndex, endIndex);
35
+
36
+ return {
37
+ examples: paginatedExamples,
38
+ total: examples.length,
39
+ totalPages: Math.ceil(examples.length / limit),
40
+ currentPage: page,
41
+ };
42
+ };
43
+
44
+ /**
45
+ * Get example by ID
46
+ */
47
+ const getExampleById = async (id) => {
48
+ logger.info('Fetching example by ID', { id });
49
+
50
+ const example = examples.find(ex => ex.id === id);
51
+ return example || null;
52
+ };
53
+
54
+ /**
55
+ * Create new example
56
+ */
57
+ const createExample = async (exampleData) => {
58
+ logger.info('Creating new example', { title: exampleData.title });
59
+
60
+ const newExample = {
61
+ id: nextId.toString(),
62
+ ...exampleData,
63
+ };
64
+
65
+ examples.push(newExample);
66
+ nextId++;
67
+
68
+ return newExample;
69
+ };
70
+
71
+ /**
72
+ * Update example by ID
73
+ */
74
+ const updateExample = async (id, updateData) => {
75
+ logger.info('Updating example', { id });
76
+
77
+ const index = examples.findIndex(ex => ex.id === id);
78
+
79
+ if (index === -1) {
80
+ return null;
81
+ }
82
+
83
+ examples[index] = {
84
+ ...examples[index],
85
+ ...updateData,
86
+ };
87
+
88
+ return examples[index];
89
+ };
90
+
91
+ /**
92
+ * Delete example by ID
93
+ */
94
+ const deleteExample = async (id) => {
95
+ logger.info('Deleting example', { id });
96
+
97
+ const index = examples.findIndex(ex => ex.id === id);
98
+
99
+ if (index === -1) {
100
+ return false;
101
+ }
102
+
103
+ examples.splice(index, 1);
104
+ return true;
105
+ };
106
+
107
+ module.exports = {
108
+ getAllExamples,
109
+ getExampleById,
110
+ createExample,
111
+ updateExample,
112
+ deleteExample,
113
113
  };
@@ -1,34 +1,34 @@
1
- // templates/services/userService.mongo.js.ejs
2
- const User = require('../models/User');
3
-
4
- const findById = async (id) => {
5
- return await User.findById(id).select('-password');
6
- };
7
-
8
- const findByEmail = async (email) => {
9
- return await User.findOne({ email });
10
- };
11
-
12
- const create = async (userData) => {
13
- const user = new User(userData);
14
- return await user.save();
15
- };
16
-
17
- const updateById = async (id, updateData) => {
18
- return await User.findByIdAndUpdate(id, updateData, {
19
- new: true,
20
- select: '-password'
21
- });
22
- };
23
-
24
- const deleteById = async (id) => {
25
- return await User.findByIdAndDelete(id);
26
- };
27
-
28
- module.exports = {
29
- findById,
30
- findByEmail,
31
- create,
32
- updateById,
33
- deleteById,
1
+ // templates/services/userService.mongo.js.ejs
2
+ const User = require('../models/User');
3
+
4
+ const findById = async (id) => {
5
+ return await User.findById(id).select('-password');
6
+ };
7
+
8
+ const findByEmail = async (email) => {
9
+ return await User.findOne({ email });
10
+ };
11
+
12
+ const create = async (userData) => {
13
+ const user = new User(userData);
14
+ return await user.save();
15
+ };
16
+
17
+ const updateById = async (id, updateData) => {
18
+ return await User.findByIdAndUpdate(id, updateData, {
19
+ new: true,
20
+ select: '-password'
21
+ });
22
+ };
23
+
24
+ const deleteById = async (id) => {
25
+ return await User.findByIdAndDelete(id);
26
+ };
27
+
28
+ module.exports = {
29
+ findById,
30
+ findByEmail,
31
+ create,
32
+ updateById,
33
+ deleteById,
34
34
  };
@@ -1,31 +1,31 @@
1
- // templates/services/userService.postgres.js.ejs
2
- const { User } = require('../models');
3
-
4
- const findById = async (id) => {
5
- return await User.findByPk(id, { attributes: { exclude: ['password'] } });
6
- };
7
-
8
- const findByEmail = async (email) => {
9
- return await User.findOne({ where: { email } });
10
- };
11
-
12
- const create = async (userData) => {
13
- return await User.create(userData);
14
- };
15
-
16
- const updateById = async (id, updateData) => {
17
- await User.update(updateData, { where: { id } });
18
- return await User.findByPk(id, { attributes: { exclude: ['password'] } });
19
- };
20
-
21
- const deleteById = async (id) => {
22
- return await User.destroy({ where: { id } });
23
- };
24
-
25
- module.exports = {
26
- findById,
27
- findByEmail,
28
- create,
29
- updateById,
30
- deleteById,
1
+ // templates/services/userService.postgres.js.ejs
2
+ const { User } = require('../models');
3
+
4
+ const findById = async (id) => {
5
+ return await User.findByPk(id, { attributes: { exclude: ['password'] } });
6
+ };
7
+
8
+ const findByEmail = async (email) => {
9
+ return await User.findOne({ where: { email } });
10
+ };
11
+
12
+ const create = async (userData) => {
13
+ return await User.create(userData);
14
+ };
15
+
16
+ const updateById = async (id, updateData) => {
17
+ await User.update(updateData, { where: { id } });
18
+ return await User.findByPk(id, { attributes: { exclude: ['password'] } });
19
+ };
20
+
21
+ const deleteById = async (id) => {
22
+ return await User.destroy({ where: { id } });
23
+ };
24
+
25
+ module.exports = {
26
+ findById,
27
+ findByEmail,
28
+ create,
29
+ updateById,
30
+ deleteById,
31
31
  };
@@ -0,0 +1,36 @@
1
+ const { prisma } = require('../config/database');
2
+
3
+ const findById = async (id) => {
4
+ return await prisma.user.findUnique({
5
+ where: { id },
6
+ select: { id: true, username: true, email: true, createdAt: true, updatedAt: true },
7
+ });
8
+ };
9
+
10
+ const findByEmail = async (email) => {
11
+ return await prisma.user.findUnique({ where: { email } });
12
+ };
13
+
14
+ const create = async (userData) => {
15
+ return await prisma.user.create({ data: userData });
16
+ };
17
+
18
+ const updateById = async (id, updateData) => {
19
+ return await prisma.user.update({
20
+ where: { id },
21
+ data: updateData,
22
+ select: { id: true, username: true, email: true, createdAt: true, updatedAt: true },
23
+ });
24
+ };
25
+
26
+ const deleteById = async (id) => {
27
+ return await prisma.user.delete({ where: { id } });
28
+ };
29
+
30
+ module.exports = {
31
+ findById,
32
+ findByEmail,
33
+ create,
34
+ updateById,
35
+ deleteById,
36
+ };