express-genix 1.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 (47) hide show
  1. package/README.md +110 -0
  2. package/index.js +114 -0
  3. package/lib/cleanup.js +129 -0
  4. package/lib/generator.js +205 -0
  5. package/lib/utils.js +92 -0
  6. package/package.json +49 -0
  7. package/templates/config/database.mongo.js.ejs +36 -0
  8. package/templates/config/database.postgres.js.ejs +41 -0
  9. package/templates/config/swagger.json.ejs +194 -0
  10. package/templates/controllers/authController.js.ejs +129 -0
  11. package/templates/controllers/exampleController.js.ejs +152 -0
  12. package/templates/controllers/userController.js.ejs +60 -0
  13. package/templates/core/Dockerfile.ejs +32 -0
  14. package/templates/core/README.md.ejs +179 -0
  15. package/templates/core/app.js.ejs +65 -0
  16. package/templates/core/docker-compose.yml.ejs +48 -0
  17. package/templates/core/env.ejs +20 -0
  18. package/templates/core/eslintrc.json.ejs +20 -0
  19. package/templates/core/gitignore.ejs +51 -0
  20. package/templates/core/healthcheck.js.ejs +25 -0
  21. package/templates/core/index.js.ejs +24 -0
  22. package/templates/core/jest.config.js.ejs +23 -0
  23. package/templates/core/package.json.ejs +33 -0
  24. package/templates/core/prettierrc.json.ejs +12 -0
  25. package/templates/core/server.js.ejs +44 -0
  26. package/templates/middleware/auth.js.ejs +66 -0
  27. package/templates/middleware/errorHandler.js.ejs +47 -0
  28. package/templates/middleware/validation.js.ejs +48 -0
  29. package/templates/models/User.mongo.js.ejs +33 -0
  30. package/templates/models/User.postgres.js.ejs +41 -0
  31. package/templates/models/index.mongo.js.ejs +8 -0
  32. package/templates/models/index.postgres.js.ejs +12 -0
  33. package/templates/routes/authRoutes.js.ejs +14 -0
  34. package/templates/routes/exampleRoutes.js.ejs +13 -0
  35. package/templates/routes/index.js.ejs +24 -0
  36. package/templates/routes/userRoutes.js.ejs +16 -0
  37. package/templates/services/authService.js.ejs +36 -0
  38. package/templates/services/exampleService.js.ejs +113 -0
  39. package/templates/services/userService.mongo.js.ejs +34 -0
  40. package/templates/services/userService.postgres.js.ejs +31 -0
  41. package/templates/tests/auth.test.js.ejs +67 -0
  42. package/templates/tests/example.test.js.ejs +113 -0
  43. package/templates/tests/setup.js.ejs +12 -0
  44. package/templates/tests/users.test.js.ejs +43 -0
  45. package/templates/utils/errors.js.ejs +13 -0
  46. package/templates/utils/logger.js.ejs +28 -0
  47. package/templates/utils/validators.js.ejs +35 -0
@@ -0,0 +1,36 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const mongoUri = process.env.MONGO_URI || 'mongodb://localhost:27017/<%= projectName %>';
4
+
5
+ const connect = async () => {
6
+ try {
7
+ await mongoose.connect(mongoUri, {
8
+ useNewUrlParser: true,
9
+ useUnifiedTopology: true,
10
+ });
11
+ console.log('MongoDB connected successfully');
12
+ } catch (error) {
13
+ console.error('MongoDB connection error:', error);
14
+ process.exit(1);
15
+ }
16
+ };
17
+
18
+ const disconnect = async () => {
19
+ try {
20
+ await mongoose.disconnect();
21
+ console.log('MongoDB disconnected');
22
+ } catch (error) {
23
+ console.error('MongoDB disconnection error:', error);
24
+ }
25
+ };
26
+
27
+ // Handle connection events
28
+ mongoose.connection.on('error', (error) => {
29
+ console.error('MongoDB connection error:', error);
30
+ });
31
+
32
+ mongoose.connection.on('disconnected', () => {
33
+ console.log('MongoDB disconnected');
34
+ });
35
+
36
+ module.exports = { connect, disconnect };
@@ -0,0 +1,41 @@
1
+ const { Sequelize } = require('sequelize');
2
+
3
+ const databaseUrl = process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/<%= projectName %>';
4
+
5
+ const sequelize = new Sequelize(databaseUrl, {
6
+ dialect: 'postgres',
7
+ logging: process.env.NODE_ENV === 'development' ? console.log : false,
8
+ pool: {
9
+ max: 5,
10
+ min: 0,
11
+ acquire: 30000,
12
+ idle: 10000,
13
+ },
14
+ });
15
+
16
+ const connect = async () => {
17
+ try {
18
+ await sequelize.authenticate();
19
+ console.log('PostgreSQL connected successfully');
20
+
21
+ // Sync database in development
22
+ if (process.env.NODE_ENV === 'development') {
23
+ await sequelize.sync({ alter: true });
24
+ console.log('Database synced');
25
+ }
26
+ } catch (error) {
27
+ console.error('PostgreSQL connection error:', error);
28
+ process.exit(1);
29
+ }
30
+ };
31
+
32
+ const disconnect = async () => {
33
+ try {
34
+ await sequelize.close();
35
+ console.log('PostgreSQL disconnected');
36
+ } catch (error) {
37
+ console.error('PostgreSQL disconnection error:', error);
38
+ }
39
+ };
40
+
41
+ module.exports = { connect, disconnect, sequelize };
@@ -0,0 +1,194 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "title": "<%= projectName %> API",
5
+ "version": "1.0.0",
6
+ "description": "A production-grade Express boilerplate<% if (hasDatabase) { %> with JWT authentication<% } %>"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "http://localhost:3000/api",
11
+ "description": "Development server"
12
+ }
13
+ ],<% if (hasDatabase) { %>
14
+ "components": {
15
+ "securitySchemes": {
16
+ "bearerAuth": {
17
+ "type": "http",
18
+ "scheme": "bearer",
19
+ "bearerFormat": "JWT"
20
+ }
21
+ }
22
+ },<% } %>
23
+ "paths": {<% if (hasDatabase) { %>
24
+ "/auth/register": {
25
+ "post": {
26
+ "summary": "Register a new user",
27
+ "tags": ["Authentication"],
28
+ "requestBody": {
29
+ "required": true,
30
+ "content": {
31
+ "application/json": {
32
+ "schema": {
33
+ "type": "object",
34
+ "required": ["username", "email", "password"],
35
+ "properties": {
36
+ "username": { "type": "string" },
37
+ "email": { "type": "string", "format": "email" },
38
+ "password": { "type": "string", "minLength": 6 }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ },
44
+ "responses": {
45
+ "201": { "description": "User registered successfully" },
46
+ "409": { "description": "User already exists" }
47
+ }
48
+ }
49
+ },
50
+ "/auth/login": {
51
+ "post": {
52
+ "summary": "Login user",
53
+ "tags": ["Authentication"],
54
+ "requestBody": {
55
+ "required": true,
56
+ "content": {
57
+ "application/json": {
58
+ "schema": {
59
+ "type": "object",
60
+ "required": ["email", "password"],
61
+ "properties": {
62
+ "email": { "type": "string", "format": "email" },
63
+ "password": { "type": "string" }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ },
69
+ "responses": {
70
+ "200": { "description": "Login successful" },
71
+ "401": { "description": "Invalid credentials" }
72
+ }
73
+ }
74
+ },
75
+ "/users/profile": {
76
+ "get": {
77
+ "summary": "Get user profile",
78
+ "tags": ["Users"],
79
+ "security": [{ "bearerAuth": [] }],
80
+ "responses": {
81
+ "200": { "description": "User profile retrieved" },
82
+ "401": { "description": "Unauthorized" }
83
+ }
84
+ }
85
+ }<% } else { %>
86
+ "/examples": {
87
+ "get": {
88
+ "summary": "Get all examples",
89
+ "tags": ["Examples"],
90
+ "parameters": [
91
+ {
92
+ "name": "page",
93
+ "in": "query",
94
+ "schema": { "type": "integer", "default": 1 }
95
+ },
96
+ {
97
+ "name": "limit",
98
+ "in": "query",
99
+ "schema": { "type": "integer", "default": 10 }
100
+ }
101
+ ],
102
+ "responses": {
103
+ "200": { "description": "Examples retrieved successfully" }
104
+ }
105
+ },
106
+ "post": {
107
+ "summary": "Create new example",
108
+ "tags": ["Examples"],
109
+ "requestBody": {
110
+ "required": true,
111
+ "content": {
112
+ "application/json": {
113
+ "schema": {
114
+ "type": "object",
115
+ "required": ["title", "description"],
116
+ "properties": {
117
+ "title": { "type": "string" },
118
+ "description": { "type": "string" }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ },
124
+ "responses": {
125
+ "201": { "description": "Example created successfully" },
126
+ "400": { "description": "Invalid input" }
127
+ }
128
+ }
129
+ },
130
+ "/examples/{id}": {
131
+ "get": {
132
+ "summary": "Get example by ID",
133
+ "tags": ["Examples"],
134
+ "parameters": [
135
+ {
136
+ "name": "id",
137
+ "in": "path",
138
+ "required": true,
139
+ "schema": { "type": "string" }
140
+ }
141
+ ],
142
+ "responses": {
143
+ "200": { "description": "Example retrieved successfully" },
144
+ "404": { "description": "Example not found" }
145
+ }
146
+ },
147
+ "put": {
148
+ "summary": "Update example",
149
+ "tags": ["Examples"],
150
+ "parameters": [
151
+ {
152
+ "name": "id",
153
+ "in": "path",
154
+ "required": true,
155
+ "schema": { "type": "string" }
156
+ }
157
+ ],
158
+ "requestBody": {
159
+ "content": {
160
+ "application/json": {
161
+ "schema": {
162
+ "type": "object",
163
+ "properties": {
164
+ "title": { "type": "string" },
165
+ "description": { "type": "string" }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ },
171
+ "responses": {
172
+ "200": { "description": "Example updated successfully" },
173
+ "404": { "description": "Example not found" }
174
+ }
175
+ },
176
+ "delete": {
177
+ "summary": "Delete example",
178
+ "tags": ["Examples"],
179
+ "parameters": [
180
+ {
181
+ "name": "id",
182
+ "in": "path",
183
+ "required": true,
184
+ "schema": { "type": "string" }
185
+ }
186
+ ],
187
+ "responses": {
188
+ "200": { "description": "Example deleted successfully" },
189
+ "404": { "description": "Example not found" }
190
+ }
191
+ }
192
+ }<% } %>
193
+ }
194
+ }
@@ -0,0 +1,129 @@
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
+ };
@@ -0,0 +1,152 @@
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
+ };
@@ -0,0 +1,60 @@
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
+ };
@@ -0,0 +1,32 @@
1
+ # templates/core/Dockerfile.ejs
2
+ FROM node:20-alpine
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Copy package files
8
+ COPY package*.json ./
9
+
10
+ # Install dependencies
11
+ RUN npm ci --only=production && npm cache clean --force
12
+
13
+ # Copy application code
14
+ COPY . .
15
+
16
+ # Create non-root user
17
+ RUN addgroup -g 1001 -S nodejs
18
+ RUN adduser -S nodeuser -u 1001
19
+
20
+ # Change ownership of app directory
21
+ RUN chown -R nodeuser:nodejs /app
22
+ USER nodeuser
23
+
24
+ # Expose port
25
+ EXPOSE 3000
26
+
27
+ # Health check
28
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
29
+ CMD node healthcheck.js
30
+
31
+ # Start application
32
+ CMD ["npm", "start"]