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,129 +1,152 @@
1
- const jwt = require('jsonwebtoken');
2
- const bcrypt = require('bcryptjs');
3
- const authService = require('../services/authService');
4
- const userService = require('../services/userService');
5
- const { AppError } = require('../utils/errors');
6
-
7
- const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
8
- const jwtRefreshSecret = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret-key';
9
-
10
- const register = async (req, res, next) => {
11
- try {
12
- const { username, email, password } = req.body;
13
-
14
- // Check if user already exists
15
- const existingUser = await userService.findByEmail(email);
16
- if (existingUser) {
17
- throw new AppError('User already exists with this email', 409);
18
- }
19
-
20
- // Hash password
21
- const hashedPassword = await bcrypt.hash(password, 12);
22
-
23
- // Create user
24
- const user = await userService.create({
25
- username,
26
- email,
27
- password: hashedPassword,
28
- });
29
-
30
- // Generate tokens
31
- const { accessToken, refreshToken } = authService.generateTokens(user);
32
-
33
- res.status(201).json({
34
- message: 'User registered successfully',
35
- user: { id: user.id, username: user.username, email: user.email },
36
- accessToken,
37
- refreshToken,
38
- });
39
- } catch (error) {
40
- next(error);
41
- }
42
- };
43
-
44
- const login = async (req, res, next) => {
45
- try {
46
- const { email, password } = req.body;
47
-
48
- // Find user
49
- const user = await userService.findByEmail(email);
50
- if (!user) {
51
- throw new AppError('Invalid credentials', 401);
52
- }
53
-
54
- // Check password
55
- const isValidPassword = await bcrypt.compare(password, user.password);
56
- if (!isValidPassword) {
57
- throw new AppError('Invalid credentials', 401);
58
- }
59
-
60
- // Generate tokens
61
- const { accessToken, refreshToken } = authService.generateTokens(user);
62
-
63
- res.json({
64
- message: 'Login successful',
65
- user: { id: user.id, username: user.username, email: user.email },
66
- accessToken,
67
- refreshToken,
68
- });
69
- } catch (error) {
70
- next(error);
71
- }
72
- };
73
-
74
- const refreshToken = async (req, res, next) => {
75
- try {
76
- const { refreshToken } = req.body;
77
-
78
- if (!refreshToken) {
79
- throw new AppError('Refresh token is required', 400);
80
- }
81
-
82
- const decoded = jwt.verify(refreshToken, jwtRefreshSecret);
83
- const user = await userService.findById(decoded.userId);
84
-
85
- if (!user) {
86
- throw new AppError('Invalid refresh token', 401);
87
- }
88
-
89
- const tokens = authService.generateTokens(user);
90
-
91
- res.json({
92
- accessToken: tokens.accessToken,
93
- refreshToken: tokens.refreshToken,
94
- });
95
- } catch (error) {
96
- next(error);
97
- }
98
- };
99
-
100
- const logout = (req, res) => {
101
- // In a real app, you might want to blacklist the token
102
- res.json({ message: 'Logout successful' });
103
- };
104
-
105
- const authenticateToken = (req, res, next) => {
106
- const authHeader = req.headers['authorization'];
107
- const token = authHeader && authHeader.split(' ')[1];
108
-
109
- if (!token) {
110
- return res.status(401).json({ error: 'Access token is required' });
111
- }
112
-
113
- jwt.verify(token, jwtSecret, (err, decoded) => {
114
- if (err) {
115
- return res.status(403).json({ error: 'Invalid or expired token' });
116
- }
117
-
118
- req.user = decoded;
119
- next();
120
- });
121
- };
122
-
123
- module.exports = {
124
- register,
125
- login,
126
- refreshToken,
127
- logout,
128
- authenticateToken,
129
- };
1
+ const bcrypt = require('bcryptjs');
2
+ const authService = require('../services/authService');
3
+ const userService = require('../services/userService');
4
+ const { AppError } = require('../utils/errors');
5
+ const { success, created } = require('../utils/response');
6
+
7
+ const register = async (req, res, next) => {
8
+ try {
9
+ const { username, email, password } = req.body;
10
+
11
+ const existingUser = await userService.findByEmail(email);
12
+ if (existingUser) {
13
+ throw new AppError('User already exists with this email', 409);
14
+ }
15
+
16
+ const hashedPassword = await bcrypt.hash(password, 12);
17
+ const user = await userService.create({ username, email, password: hashedPassword });
18
+ const tokens = authService.generateTokens(user);
19
+
20
+ return created(res, {
21
+ message: 'User registered successfully',
22
+ user: { id: user.id, username: user.username, email: user.email },
23
+ ...tokens,
24
+ });
25
+ } catch (error) {
26
+ next(error);
27
+ }
28
+ };
29
+
30
+ const login = async (req, res, next) => {
31
+ try {
32
+ const { email, password } = req.body;
33
+
34
+ const user = await userService.findByEmail(email);
35
+ if (!user) {
36
+ throw new AppError('Invalid credentials', 401);
37
+ }
38
+
39
+ const isValidPassword = await bcrypt.compare(password, user.password);
40
+ if (!isValidPassword) {
41
+ throw new AppError('Invalid credentials', 401);
42
+ }
43
+
44
+ const tokens = authService.generateTokens(user);
45
+
46
+ return success(res, {
47
+ message: 'Login successful',
48
+ user: { id: user.id, username: user.username, email: user.email },
49
+ ...tokens,
50
+ });
51
+ } catch (error) {
52
+ next(error);
53
+ }
54
+ };
55
+
56
+ const refreshToken = async (req, res, next) => {
57
+ try {
58
+ const { refreshToken: token } = req.body;
59
+
60
+ if (!token) {
61
+ throw new AppError('Refresh token is required', 400);
62
+ }
63
+
64
+ if (authService.isTokenBlacklisted(token)) {
65
+ throw new AppError('Token has been revoked', 401);
66
+ }
67
+
68
+ const decoded = authService.verifyRefreshToken(token);
69
+ const user = await userService.findById(decoded.userId);
70
+
71
+ if (!user) {
72
+ throw new AppError('Invalid refresh token', 401);
73
+ }
74
+
75
+ // Blacklist old refresh token (rotation)
76
+ authService.blacklistToken(token);
77
+
78
+ const tokens = authService.generateTokens(user);
79
+ return success(res, tokens);
80
+ } catch (error) {
81
+ next(error);
82
+ }
83
+ };
84
+
85
+ const logout = async (req, res, next) => {
86
+ try {
87
+ const authHeader = req.headers['authorization'];
88
+ const accessToken = authHeader && authHeader.split(' ')[1];
89
+
90
+ if (accessToken) {
91
+ authService.blacklistToken(accessToken);
92
+ }
93
+
94
+ const { refreshToken: token } = req.body;
95
+ if (token) {
96
+ authService.blacklistToken(token);
97
+ }
98
+
99
+ return success(res, { message: 'Logout successful' });
100
+ } catch (error) {
101
+ next(error);
102
+ }
103
+ };
104
+
105
+ const forgotPassword = async (req, res, next) => {
106
+ try {
107
+ const { email } = req.body;
108
+ const user = await userService.findByEmail(email);
109
+
110
+ // Always return success to prevent email enumeration
111
+ if (!user) {
112
+ return success(res, { message: 'If an account with that email exists, a reset link has been sent.' });
113
+ }
114
+
115
+ const resetToken = await authService.generateResetToken(email);
116
+
117
+ // TODO: Send email with reset link
118
+ // await emailService.sendResetEmail(email, resetToken);
119
+ //
120
+ // For development, log the token:
121
+ console.log(`Password reset token for ${email}: ${resetToken}`);
122
+
123
+ return success(res, { message: 'If an account with that email exists, a reset link has been sent.' });
124
+ } catch (error) {
125
+ next(error);
126
+ }
127
+ };
128
+
129
+ const resetPassword = async (req, res, next) => {
130
+ try {
131
+ const { token, password } = req.body;
132
+
133
+ const email = await authService.consumeResetToken(token);
134
+ if (!email) {
135
+ throw new AppError('Invalid or expired reset token', 400);
136
+ }
137
+
138
+ const user = await userService.findByEmail(email);
139
+ if (!user) {
140
+ throw new AppError('User not found', 404);
141
+ }
142
+
143
+ const hashedPassword = await bcrypt.hash(password, 12);
144
+ await userService.updateById(user.id, { password: hashedPassword });
145
+
146
+ return success(res, { message: 'Password has been reset successfully.' });
147
+ } catch (error) {
148
+ next(error);
149
+ }
150
+ };
151
+
152
+ module.exports = { register, login, refreshToken, logout, forgotPassword, resetPassword };
@@ -1,152 +1,92 @@
1
- const exampleService = require('../services/exampleService');
2
- const { AppError } = require('../utils/errors');
3
- const { createLogger } = require('../utils/logger');
4
-
5
- const logger = createLogger('ExampleController');
6
-
7
- /**
8
- * Get all examples
9
- */
10
- const getAllExamples = async (req, res, next) => {
11
- try {
12
- const { page = 1, limit = 10 } = req.query;
13
-
14
- logger.info('Fetching examples', { page, limit });
15
-
16
- const examples = await exampleService.getAllExamples(
17
- parseInt(page),
18
- parseInt(limit)
19
- );
20
-
21
- res.json({
22
- success: true,
23
- data: examples,
24
- pagination: {
25
- page: parseInt(page),
26
- limit: parseInt(limit),
27
- },
28
- });
29
- } catch (error) {
30
- logger.error('Error fetching examples', { error: error.message });
31
- next(error);
32
- }
33
- };
34
-
35
- /**
36
- * Get example by ID
37
- */
38
- const getExampleById = async (req, res, next) => {
39
- try {
40
- const { id } = req.params;
41
-
42
- logger.info('Fetching example by ID', { id });
43
-
44
- const example = await exampleService.getExampleById(id);
45
-
46
- if (!example) {
47
- throw new AppError('Example not found', 404);
48
- }
49
-
50
- res.json({
51
- success: true,
52
- data: example,
53
- });
54
- } catch (error) {
55
- logger.error('Error fetching example', { id: req.params.id, error: error.message });
56
- next(error);
57
- }
58
- };
59
-
60
- /**
61
- * Create new example
62
- */
63
- const createExample = async (req, res, next) => {
64
- try {
65
- const { title, description } = req.body;
66
-
67
- if (!title || !description) {
68
- throw new AppError('Title and description are required', 400);
69
- }
70
-
71
- logger.info('Creating new example', { title });
72
-
73
- const example = await exampleService.createExample({
74
- title,
75
- description,
76
- createdAt: new Date(),
77
- });
78
-
79
- res.status(201).json({
80
- success: true,
81
- message: 'Example created successfully',
82
- data: example,
83
- });
84
- } catch (error) {
85
- logger.error('Error creating example', { error: error.message });
86
- next(error);
87
- }
88
- };
89
-
90
- /**
91
- * Update example
92
- */
93
- const updateExample = async (req, res, next) => {
94
- try {
95
- const { id } = req.params;
96
- const { title, description } = req.body;
97
-
98
- logger.info('Updating example', { id });
99
-
100
- const example = await exampleService.updateExample(id, {
101
- title,
102
- description,
103
- updatedAt: new Date(),
104
- });
105
-
106
- if (!example) {
107
- throw new AppError('Example not found', 404);
108
- }
109
-
110
- res.json({
111
- success: true,
112
- message: 'Example updated successfully',
113
- data: example,
114
- });
115
- } catch (error) {
116
- logger.error('Error updating example', { id: req.params.id, error: error.message });
117
- next(error);
118
- }
119
- };
120
-
121
- /**
122
- * Delete example
123
- */
124
- const deleteExample = async (req, res, next) => {
125
- try {
126
- const { id } = req.params;
127
-
128
- logger.info('Deleting example', { id });
129
-
130
- const deleted = await exampleService.deleteExample(id);
131
-
132
- if (!deleted) {
133
- throw new AppError('Example not found', 404);
134
- }
135
-
136
- res.json({
137
- success: true,
138
- message: 'Example deleted successfully',
139
- });
140
- } catch (error) {
141
- logger.error('Error deleting example', { id: req.params.id, error: error.message });
142
- next(error);
143
- }
144
- };
145
-
146
- module.exports = {
147
- getAllExamples,
148
- getExampleById,
149
- createExample,
150
- updateExample,
151
- deleteExample,
152
- };
1
+ const exampleService = require('../services/exampleService');
2
+ const { AppError } = require('../utils/errors');
3
+ const { success, created, paginated } = require('../utils/response');
4
+ const { createLogger } = require('../utils/logger');
5
+
6
+ const logger = createLogger('ExampleController');
7
+
8
+ const getAllExamples = async (req, res, next) => {
9
+ try {
10
+ const page = Math.max(1, parseInt(req.query.page, 10) || 1);
11
+ const limit = Math.min(100, Math.max(1, parseInt(req.query.limit, 10) || 10));
12
+
13
+ logger.info('Fetching examples', { page, limit });
14
+ const result = await exampleService.getAllExamples(page, limit);
15
+
16
+ return paginated(res, result.examples, {
17
+ page,
18
+ limit,
19
+ total: result.total,
20
+ totalPages: result.totalPages,
21
+ });
22
+ } catch (error) {
23
+ logger.error('Error fetching examples', { error: error.message });
24
+ next(error);
25
+ }
26
+ };
27
+
28
+ const getExampleById = async (req, res, next) => {
29
+ try {
30
+ const { id } = req.params;
31
+ const example = await exampleService.getExampleById(id);
32
+
33
+ if (!example) {
34
+ throw new AppError('Example not found', 404);
35
+ }
36
+
37
+ return success(res, example);
38
+ } catch (error) {
39
+ next(error);
40
+ }
41
+ };
42
+
43
+ const createExample = async (req, res, next) => {
44
+ try {
45
+ const { title, description } = req.body;
46
+
47
+ if (!title || !description) {
48
+ throw new AppError('Title and description are required', 400);
49
+ }
50
+
51
+ logger.info('Creating example', { title });
52
+ const example = await exampleService.createExample({ title, description });
53
+
54
+ return created(res, example);
55
+ } catch (error) {
56
+ next(error);
57
+ }
58
+ };
59
+
60
+ const updateExample = async (req, res, next) => {
61
+ try {
62
+ const { id } = req.params;
63
+ const { title, description } = req.body;
64
+
65
+ const example = await exampleService.updateExample(id, { title, description });
66
+
67
+ if (!example) {
68
+ throw new AppError('Example not found', 404);
69
+ }
70
+
71
+ return success(res, example);
72
+ } catch (error) {
73
+ next(error);
74
+ }
75
+ };
76
+
77
+ const deleteExample = async (req, res, next) => {
78
+ try {
79
+ const { id } = req.params;
80
+ const deleted = await exampleService.deleteExample(id);
81
+
82
+ if (!deleted) {
83
+ throw new AppError('Example not found', 404);
84
+ }
85
+
86
+ return success(res, { message: 'Example deleted successfully' });
87
+ } catch (error) {
88
+ next(error);
89
+ }
90
+ };
91
+
92
+ module.exports = { getAllExamples, getExampleById, createExample, updateExample, deleteExample };
@@ -1,60 +1,52 @@
1
- const userService = require('../services/userService');
2
- const { AppError } = require('../utils/errors');
3
-
4
- const getProfile = async (req, res, next) => {
5
- try {
6
- const user = await userService.findById(req.user.userId);
7
- if (!user) {
8
- throw new AppError('User not found', 404);
9
- }
10
-
11
- res.json({
12
- user: {
13
- id: user.id,
14
- username: user.username,
15
- email: user.email,
16
- createdAt: user.createdAt,
17
- },
18
- });
19
- } catch (error) {
20
- next(error);
21
- }
22
- };
23
-
24
- const updateProfile = async (req, res, next) => {
25
- try {
26
- const { username } = req.body;
27
- const userId = req.user.userId;
28
-
29
- const updatedUser = await userService.updateById(userId, { username });
30
-
31
- res.json({
32
- message: 'Profile updated successfully',
33
- user: {
34
- id: updatedUser.id,
35
- username: updatedUser.username,
36
- email: updatedUser.email,
37
- },
38
- });
39
- } catch (error) {
40
- next(error);
41
- }
42
- };
43
-
44
- const deleteProfile = async (req, res, next) => {
45
- try {
46
- const userId = req.user.userId;
47
-
48
- await userService.deleteById(userId);
49
-
50
- res.json({ message: 'Profile deleted successfully' });
51
- } catch (error) {
52
- next(error);
53
- }
54
- };
55
-
56
- module.exports = {
57
- getProfile,
58
- updateProfile,
59
- deleteProfile,
60
- };
1
+ const userService = require('../services/userService');
2
+ const { AppError } = require('../utils/errors');
3
+ const { success } = require('../utils/response');
4
+
5
+ const getProfile = async (req, res, next) => {
6
+ try {
7
+ const user = await userService.findById(req.user.userId);
8
+ if (!user) {
9
+ throw new AppError('User not found', 404);
10
+ }
11
+
12
+ return success(res, {
13
+ user: {
14
+ id: user.id,
15
+ username: user.username,
16
+ email: user.email,
17
+ createdAt: user.createdAt,
18
+ },
19
+ });
20
+ } catch (error) {
21
+ next(error);
22
+ }
23
+ };
24
+
25
+ const updateProfile = async (req, res, next) => {
26
+ try {
27
+ const { username } = req.body;
28
+ const updatedUser = await userService.updateById(req.user.userId, { username });
29
+
30
+ return success(res, {
31
+ message: 'Profile updated successfully',
32
+ user: {
33
+ id: updatedUser.id,
34
+ username: updatedUser.username,
35
+ email: updatedUser.email,
36
+ },
37
+ });
38
+ } catch (error) {
39
+ next(error);
40
+ }
41
+ };
42
+
43
+ const deleteProfile = async (req, res, next) => {
44
+ try {
45
+ await userService.deleteById(req.user.userId);
46
+ return success(res, { message: 'Profile deleted successfully' });
47
+ } catch (error) {
48
+ next(error);
49
+ }
50
+ };
51
+
52
+ module.exports = { getProfile, updateProfile, deleteProfile };