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,179 @@
1
+ # <%= projectName %>
2
+
3
+ A production-grade Express.js boilerplate<% if (hasDatabase) { %> with JWT authentication, <%= db %> support<% } %>, rate-limiting, security middleware, logging, API documentation, testing, and more.
4
+
5
+ ## Features
6
+
7
+ <% if (hasDatabase) { %>- **Authentication**: JWT-based authentication with refresh tokens
8
+ - **Database**: <%= db === 'mongodb' ? 'MongoDB with Mongoose' : 'PostgreSQL with Sequelize' %>
9
+ - **User Management**: Complete user registration, login, profile management<% } else { %>- **No Database**: Perfect for microservices, proxy servers, or computational APIs
10
+ - **Example API**: CRUD operations with in-memory storage (easily replaceable)<% } %>
11
+ - **Security**: Helmet, CORS, rate limiting
12
+ - **Logging**: Morgan HTTP request logger with custom logging utilities
13
+ - **API Documentation**: Swagger UI
14
+ - **Testing**: Jest and Supertest
15
+ - **Code Quality**: ESLint with Airbnb configuration, Prettier formatting
16
+ - **Deployment**: Docker support with clustering
17
+ - **Project Structure**: Clean, modular Express.js architecture
18
+
19
+ ## Project Structure
20
+
21
+ ```
22
+ ├── src/
23
+ │ ├── config/ # Configuration files (swagger<% if (hasDatabase) { %>, database<% } %>)
24
+ │ ├── controllers/ # Request handlers and business logic orchestration<% if (hasDatabase) { %>
25
+ │ ├── middleware/ # Custom middleware (auth, validation, errors)<% } else { %>
26
+ │ ├── middleware/ # Custom middleware (errors)<% } %>
27
+ <% if (hasDatabase) { %>│ ├── models/ # Database models<% } %>
28
+ │ ├── routes/ # API endpoint definitions
29
+ │ ├── services/ # Business logic layer
30
+ │ ├── utils/ # Utility functions and helpers
31
+ │ ├── app.js # Express app configuration
32
+ │ └── server.js # Server setup and clustering
33
+ ├── tests/ # Test files
34
+ ├── .env # Environment variables
35
+ ├── .gitignore # Git ignore rules
36
+ ├── package.json # Dependencies and scripts
37
+ └── README.md # Project documentation
38
+ ```
39
+
40
+ ## Setup
41
+
42
+ 1. **Install Dependencies**
43
+ ```bash
44
+ npm install
45
+ ```
46
+
47
+ 2. **Environment Configuration**
48
+ - Copy `.env` and configure your environment variables<% if (hasDatabase) { %>
49
+ - Update database connection settings
50
+ - Change JWT secrets for production<% } %>
51
+
52
+ <% if (hasDatabase) { %>3. **Database Setup**<% if (db === 'mongodb') { %>
53
+ - Ensure MongoDB is running locally or update MONGO_URI
54
+ - Database will be created automatically<% } else if (db === 'postgresql') { %>
55
+ - Ensure PostgreSQL is running locally
56
+ - Create a database matching your DATABASE_URL
57
+ - Run migrations if needed<% } %>
58
+
59
+ 4.<% } else { %>3.<% } %> **Start Development Server**
60
+ ```bash
61
+ npm run dev
62
+ ```
63
+
64
+ <% if (hasDatabase) { %>5.<% } else { %>4.<% } %> **Production**
65
+ ```bash
66
+ npm start
67
+ ```
68
+
69
+ ## Available Scripts
70
+
71
+ - `npm run dev` - Start development server with nodemon
72
+ - `npm start` - Start production server with clustering
73
+ - `npm test` - Run tests with coverage
74
+ - `npm run lint` - Run ESLint
75
+ - `npm run lint:fix` - Fix ESLint issues automatically
76
+ - `npm run format` - Format code with Prettier
77
+ - `npm run format:check` - Check code formatting
78
+
79
+ ## API Documentation
80
+
81
+ Visit `http://localhost:3000/api-docs` for interactive Swagger documentation.
82
+
83
+ ## API Endpoints
84
+
85
+ <% if (hasDatabase) { %>### Authentication
86
+ - `POST /api/auth/register` - Register new user
87
+ - `POST /api/auth/login` - User login
88
+ - `POST /api/auth/refresh` - Refresh access token
89
+ - `POST /api/auth/logout` - User logout
90
+
91
+ ### Users
92
+ - `GET /api/users/profile` - Get user profile (protected)
93
+ - `PUT /api/users/profile` - Update user profile (protected)
94
+ - `DELETE /api/users/profile` - Delete user profile (protected)<% } else { %>### Examples
95
+ - `GET /api/examples` - Get all examples (with pagination)
96
+ - `GET /api/examples/:id` - Get example by ID
97
+ - `POST /api/examples` - Create new example
98
+ - `PUT /api/examples/:id` - Update example
99
+ - `DELETE /api/examples/:id` - Delete example<% } %>
100
+
101
+ ### Health Check
102
+ - `GET /health` - Application health status
103
+
104
+ ## Environment Variables
105
+
106
+ | Variable | Description | Default |
107
+ |----------|-------------|---------|
108
+ | NODE_ENV | Environment mode | development |
109
+ | PORT | Server port | 3000 |<% if (hasDatabase) { %>
110
+ | JWT_SECRET | JWT signing secret | - |
111
+ | JWT_REFRESH_SECRET | Refresh token secret | - |
112
+ | JWT_EXPIRE | Access token expiry | 15m |
113
+ | JWT_REFRESH_EXPIRE | Refresh token expiry | 7d |
114
+ | <%= db === 'mongodb' ? 'MONGO_URI' : 'DATABASE_URL' %> | Database connection | - |<% } %>
115
+ | RATE_LIMIT_WINDOW_MS | Rate limit window | 900000 |
116
+ | RATE_LIMIT_MAX | Max requests per window | 100 |
117
+
118
+ ## Testing
119
+
120
+ ```bash
121
+ # Run all tests
122
+ npm test
123
+
124
+ # Run tests with coverage
125
+ npm run test
126
+
127
+ # Run tests in watch mode
128
+ npm run test:watch
129
+ ```
130
+
131
+ ## Deployment
132
+
133
+ ### Docker
134
+
135
+ 1. **Build Image**
136
+ ```bash
137
+ docker build -t <%= projectName %> .
138
+ ```
139
+
140
+ 2. **Run Container**
141
+ ```bash
142
+ docker run -p 3000:3000 --env-file .env <%= projectName %>
143
+ ```
144
+
145
+ 3. **Docker Compose**
146
+ ```bash
147
+ docker-compose up
148
+ ```
149
+
150
+ ## Security Features
151
+
152
+ - **Helmet**: Sets various HTTP headers for security
153
+ - **CORS**: Configurable cross-origin resource sharing
154
+ - **Rate Limiting**: Prevents abuse and DDoS attacks<% if (hasDatabase) { %>
155
+ - **JWT**: Stateless authentication with refresh tokens
156
+ - **Password Hashing**: bcrypt for secure password storage
157
+ - **Input Validation**: Request validation middleware<% } %>
158
+
159
+ <% if (!hasDatabase) { %>## Extending the Application
160
+
161
+ This boilerplate is designed for APIs that don't require database persistence. You can easily:
162
+
163
+ - Add database support by integrating MongoDB or PostgreSQL
164
+ - Replace in-memory storage with external services (Redis, etc.)
165
+ - Add authentication by integrating with OAuth providers
166
+ - Build microservices by adding service-to-service communication
167
+
168
+ <% } %>## Contributing
169
+
170
+ 1. Fork the repository
171
+ 2. Create a feature branch
172
+ 3. Make your changes
173
+ 4. Add tests for new functionality
174
+ 5. Ensure all tests pass
175
+ 6. Submit a pull request
176
+
177
+ ## License
178
+
179
+ MIT License - see LICENSE file for details
@@ -0,0 +1,65 @@
1
+ const express = require('express');
2
+ const cors = require('cors');
3
+ const helmet = require('helmet');
4
+ const morgan = require('morgan');
5
+ const rateLimit = require('express-rate-limit');
6
+ const swaggerUi = require('swagger-ui-express');
7
+ const dotenv = require('dotenv');
8
+
9
+ const routes = require('./routes');
10
+ const errorHandler = require('./middleware/errorHandler');
11
+ const swaggerConfig = require('./config/swagger.json');
12
+
13
+ // Load environment variables
14
+ dotenv.config();
15
+
16
+ const app = express();
17
+
18
+ // Security middleware
19
+ app.use(helmet());
20
+ app.use(cors());
21
+
22
+ // Logging
23
+ app.use(morgan('dev'));
24
+
25
+ // Body parsing middleware
26
+ app.use(express.json({ limit: '10mb' }));
27
+ app.use(express.urlencoded({ extended: true }));
28
+
29
+ // Rate limiting
30
+ const limiter = rateLimit({
31
+ windowMs: 15 * 60 * 1000, // 15 minutes
32
+ max: 100, // Limit each IP to 100 requests per windowMs
33
+ message: 'Too many requests from this IP, please try again later.',
34
+ });
35
+ app.use(limiter);
36
+
37
+ // Health check endpoint
38
+ app.get('/health', (req, res) => {
39
+ res.status(200).json({
40
+ status: 'OK',
41
+ timestamp: new Date().toISOString(),
42
+ uptime: process.uptime(),<% if (hasDatabase) { %>
43
+ database: '<%= db %>'<% } else { %>
44
+ database: 'none'<% } %>,
45
+ });
46
+ });
47
+
48
+ // API documentation
49
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerConfig));
50
+
51
+ // API routes
52
+ app.use('/api', routes);
53
+
54
+ // 404 handler
55
+ app.use('*', (req, res) => {
56
+ res.status(404).json({
57
+ error: 'Route not found',
58
+ path: req.originalUrl,
59
+ });
60
+ });
61
+
62
+ // Error handling middleware (should be last)
63
+ app.use(errorHandler);
64
+
65
+ module.exports = app;
@@ -0,0 +1,48 @@
1
+ # templates/core/docker-compose.yml.ejs
2
+ version: '3.8'
3
+
4
+ services:
5
+ app:
6
+ build: .
7
+ ports:
8
+ - "3000:3000"
9
+ environment:
10
+ - NODE_ENV=production
11
+ env_file:
12
+ - .env<% if (hasDatabase) { %>
13
+ depends_on:
14
+ - <%= db === 'mongodb' ? 'mongodb' : 'postgres' %><% } %>
15
+ restart: unless-stopped
16
+ networks:
17
+ - app-network
18
+
19
+ <% if (db === 'mongodb') { %> mongodb:
20
+ image: mongo:7-jammy
21
+ ports:
22
+ - "27017:27017"
23
+ environment:
24
+ - MONGO_INITDB_DATABASE=<%= projectName %>
25
+ volumes:
26
+ - mongodb_data:/data/db
27
+ restart: unless-stopped
28
+ networks:
29
+ - app-network<% } else if (db === 'postgresql') { %> postgres:
30
+ image: postgres:15-alpine
31
+ ports:
32
+ - "5432:5432"
33
+ environment:
34
+ - POSTGRES_DB=<%= projectName %>
35
+ - POSTGRES_USER=user
36
+ - POSTGRES_PASSWORD=password
37
+ volumes:
38
+ - postgres_data:/var/lib/postgresql/data
39
+ restart: unless-stopped
40
+ networks:
41
+ - app-network<% } %>
42
+
43
+ <% if (hasDatabase) { %>volumes:
44
+ <%= db === 'mongodb' ? 'mongodb_data:' : 'postgres_data:' %><% } %>
45
+
46
+ networks:
47
+ app-network:
48
+ driver: bridge
@@ -0,0 +1,20 @@
1
+ # Server Configuration
2
+ NODE_ENV=development
3
+ PORT=3000<% if (hasDatabase) { %>
4
+
5
+ # JWT Configuration
6
+ JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
7
+ JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-this-in-production
8
+ JWT_EXPIRE=15m
9
+ JWT_REFRESH_EXPIRE=7d
10
+
11
+ # Database Configuration<% if (db === 'mongodb') { %>
12
+ MONGO_URI=mongodb://localhost:27017/<%= projectName %><% } else if (db === 'postgresql') { %>
13
+ DATABASE_URL=postgresql://user:password@localhost:5432/<%= projectName %><% } %><% } %>
14
+
15
+ # Rate Limiting
16
+ RATE_LIMIT_WINDOW_MS=900000
17
+ RATE_LIMIT_MAX=100
18
+
19
+ # Logging
20
+ LOG_LEVEL=info
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": ["airbnb-base"],
3
+ "env": {
4
+ "node": true,
5
+ "jest": true
6
+ },
7
+ "rules": {
8
+ "no-console": "off",
9
+ "consistent-return": "off",
10
+ "func-names": "off",
11
+ "object-shorthand": "off",
12
+ "no-process-exit": "off",
13
+ "no-param-reassign": "off",
14
+ "no-return-await": "off",
15
+ "no-underscore-dangle": "off",
16
+ "class-methods-use-this": "off",
17
+ "prefer-destructuring": ["error", { "object": true, "array": false }],
18
+ "no-unused-vars": ["error", { "argsIgnorePattern": "req|res|next|val" }]
19
+ }
20
+ }
@@ -0,0 +1,51 @@
1
+ // templates/core/gitignore.ejs
2
+ # Dependencies
3
+ node_modules/
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+
8
+ # Environment variables
9
+ .env
10
+ .env.local
11
+ .env.development.local
12
+ .env.test.local
13
+ .env.production.local
14
+
15
+ # Build outputs
16
+ dist/
17
+ build/
18
+
19
+ # Logs
20
+ logs/
21
+ *.log
22
+
23
+ # Runtime data
24
+ pids/
25
+ *.pid
26
+ *.seed
27
+ *.pid.lock
28
+
29
+ # Coverage directory used by tools like istanbul
30
+ coverage/
31
+ *.lcov
32
+
33
+ # OS generated files
34
+ .DS_Store
35
+ .DS_Store?
36
+ ._*
37
+ .Spotlight-V100
38
+ .Trashes
39
+ ehthumbs.db
40
+ Thumbs.db
41
+
42
+ # IDE files
43
+ .vscode/
44
+ .idea/
45
+ *.swp
46
+ *.swo
47
+ *~
48
+
49
+ # Temporary files
50
+ tmp/
51
+ temp/
@@ -0,0 +1,25 @@
1
+ // templates/core/healthcheck.js.ejs
2
+ const http = require('http');
3
+
4
+ const options = {
5
+ host: 'localhost',
6
+ port: process.env.PORT || 3000,
7
+ path: '/health',
8
+ timeout: 2000,
9
+ };
10
+
11
+ const request = http.request(options, (res) => {
12
+ console.log(`STATUS: ${res.statusCode}`);
13
+ if (res.statusCode === 200) {
14
+ process.exit(0);
15
+ } else {
16
+ process.exit(1);
17
+ }
18
+ });
19
+
20
+ request.on('error', (err) => {
21
+ console.log('ERROR:', err);
22
+ process.exit(1);
23
+ });
24
+
25
+ request.end();
@@ -0,0 +1,24 @@
1
+ const express = require('express');<% if (hasDatabase) { %>
2
+ const authRoutes = require('./authRoutes');
3
+ const userRoutes = require('./userRoutes');<% } else { %>
4
+ const exampleRoutes = require('./exampleRoutes');<% } %>
5
+
6
+ const router = express.Router();
7
+
8
+ // Mount route modules<% if (hasDatabase) { %>
9
+ router.use('/auth', authRoutes);
10
+ router.use('/users', userRoutes);<% } else { %>
11
+ router.use('/examples', exampleRoutes);<% } %>
12
+
13
+ // API root endpoint
14
+ router.get('/', (req, res) => {
15
+ res.json({
16
+ message: 'Welcome to the API',
17
+ version: '1.0.0',
18
+ documentation: '/api-docs',<% if (hasDatabase) { %>
19
+ features: ['authentication', 'user-management', '<%= db %>-database']<% } else { %>
20
+ features: ['rate-limiting', 'logging', 'swagger-docs']<% } %>,
21
+ });
22
+ });
23
+
24
+ module.exports = router;
@@ -0,0 +1,23 @@
1
+
2
+ // templates/core/jest.config.js.ejs
3
+ module.exports = {
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/src', '<rootDir>/tests'],
6
+ testMatch: [
7
+ '**/__tests__/**/*.js',
8
+ '**/?(*.)+(spec|test).js'
9
+ ],
10
+ collectCoverageFrom: [
11
+ 'src/**/*.js',
12
+ '!src/server.js',
13
+ '!src/config/**',
14
+ ],
15
+ coverageDirectory: 'coverage',
16
+ coverageReporters: [
17
+ 'text',
18
+ 'lcov',
19
+ 'html'
20
+ ],
21
+ setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
22
+ testTimeout: 10000,
23
+ };
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "<%= projectName %>",
3
+ "version": "1.0.0",
4
+ "main": "src/server.js",
5
+ "scripts": {
6
+ "start": "node src/server.js",
7
+ "dev": "nodemon src/server.js",
8
+ "test": "jest --coverage",
9
+ "lint": "eslint ."
10
+ },
11
+ "dependencies": {
12
+ "express": "^4.18.2",
13
+ "express-rate-limit": "^7.1.5",
14
+ "dotenv": "^16.3.1",
15
+ "cors": "^2.8.5",
16
+ "helmet": "^7.1.0",
17
+ "morgan": "^1.10.0",
18
+ "swagger-ui-express": "^5.0.1"<% if (hasDatabase) { %>,
19
+ "jsonwebtoken": "^9.0.2",
20
+ "bcryptjs": "^2.4.3"<% if (db === 'mongodb') { %>,
21
+ "mongoose": "^8.0.3"<% } else if (db === 'postgresql') { %>,
22
+ "pg": "^8.11.3",
23
+ "sequelize": "^6.35.1"<% } %><% } %>
24
+ },
25
+ "devDependencies": {
26
+ "nodemon": "^3.0.3",
27
+ "jest": "^29.7.0",
28
+ "supertest": "^6.3.4",
29
+ "eslint": "^8.56.0",
30
+ "eslint-config-airbnb-base": "^15.0.0",
31
+ "eslint-plugin-import": "^2.29.1"
32
+ }
33
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 80,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "bracketSpacing": true,
9
+ "bracketSameLine": false,
10
+ "arrowParens": "always",
11
+ "endOfLine": "lf"
12
+ }
@@ -0,0 +1,44 @@
1
+ const cluster = require('cluster');
2
+ const os = require('os');
3
+ const app = require('./app');<% if (hasDatabase) { %>
4
+ const db = require('./config/database');<% } %>
5
+
6
+ const port = process.env.PORT || 3000;
7
+
8
+ const startServer = async () => {<% if (hasDatabase) { %>
9
+ await db.connect();<% } %>
10
+
11
+ if (cluster.isMaster) {
12
+ const numCPUs = os.cpus().length;
13
+ console.log(`Master ${process.pid} is running`);
14
+
15
+ // Fork workers
16
+ for (let i = 0; i < numCPUs; i++) {
17
+ cluster.fork();
18
+ }
19
+
20
+ cluster.on('exit', (worker, code, signal) => {
21
+ console.log(`Worker ${worker.process.pid} died with code ${code} and signal ${signal}`);
22
+ cluster.fork();
23
+ });
24
+ } else {
25
+ app.listen(port, () => {
26
+ console.log(`Worker ${process.pid} running on http://localhost:${port}`);
27
+ });
28
+ }
29
+ };
30
+
31
+ startServer().catch(console.error);
32
+
33
+ // Graceful Shutdown
34
+ process.on('SIGTERM', async () => {
35
+ console.log('SIGTERM signal received: closing HTTP server');<% if (hasDatabase) { %>
36
+ await db.disconnect();<% } %>
37
+ process.exit(0);
38
+ });
39
+
40
+ process.on('SIGINT', async () => {
41
+ console.log('SIGINT signal received: closing HTTP server');<% if (hasDatabase) { %>
42
+ await db.disconnect();<% } %>
43
+ process.exit(0);
44
+ });
@@ -0,0 +1,66 @@
1
+ const jwt = require('jsonwebtoken');
2
+ const { AppError } = require('../utils/errors');
3
+
4
+ const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
5
+
6
+ /**
7
+ * Middleware to authenticate JWT tokens
8
+ */
9
+ const authenticateToken = (req, res, next) => {
10
+ const authHeader = req.headers['authorization'];
11
+ const token = authHeader && authHeader.split(' ')[1];
12
+
13
+ if (!token) {
14
+ return next(new AppError('Access token is required', 401));
15
+ }
16
+
17
+ jwt.verify(token, jwtSecret, (err, decoded) => {
18
+ if (err) {
19
+ return next(new AppError('Invalid or expired token', 403));
20
+ }
21
+
22
+ req.user = decoded;
23
+ next();
24
+ });
25
+ };
26
+
27
+ /**
28
+ * Optional authentication middleware - doesn't require token
29
+ */
30
+ const optionalAuth = (req, res, next) => {
31
+ const authHeader = req.headers['authorization'];
32
+ const token = authHeader && authHeader.split(' ')[1];
33
+
34
+ if (token) {
35
+ jwt.verify(token, jwtSecret, (err, decoded) => {
36
+ if (!err) {
37
+ req.user = decoded;
38
+ }
39
+ });
40
+ }
41
+
42
+ next();
43
+ };
44
+
45
+ /**
46
+ * Middleware to check if user has specific role
47
+ */
48
+ const requireRole = (role) => {
49
+ return (req, res, next) => {
50
+ if (!req.user) {
51
+ return next(new AppError('Authentication required', 401));
52
+ }
53
+
54
+ if (req.user.role !== role) {
55
+ return next(new AppError('Insufficient permissions', 403));
56
+ }
57
+
58
+ next();
59
+ };
60
+ };
61
+
62
+ module.exports = {
63
+ authenticateToken,
64
+ optionalAuth,
65
+ requireRole,
66
+ };
@@ -0,0 +1,47 @@
1
+
2
+ // templates/middleware/errorHandler.js.ejs
3
+ const { AppError } = require('../utils/errors');
4
+
5
+ const errorHandler = (err, req, res, next) => {
6
+ let error = { ...err };
7
+ error.message = err.message;
8
+
9
+ console.error(err);
10
+
11
+ <% if (db === 'mongodb') { %> // Mongoose bad ObjectId
12
+ if (err.name === 'CastError') {
13
+ const message = 'Resource not found';
14
+ error = new AppError(message, 404);
15
+ }
16
+
17
+ // Mongoose duplicate key
18
+ if (err.code === 11000) {
19
+ const message = 'Duplicate field value entered';
20
+ error = new AppError(message, 400);
21
+ }
22
+
23
+ // Mongoose validation error
24
+ if (err.name === 'ValidationError') {
25
+ const message = Object.values(err.errors).map(val => val.message).join(', ');
26
+ error = new AppError(message, 400);
27
+ }<% } %>
28
+
29
+ <% if (hasDatabase) { %> // JWT errors
30
+ if (err.name === 'JsonWebTokenError') {
31
+ const message = 'Invalid token';
32
+ error = new AppError(message, 401);
33
+ }
34
+
35
+ if (err.name === 'TokenExpiredError') {
36
+ const message = 'Token expired';
37
+ error = new AppError(message, 401);
38
+ }<% } %>
39
+
40
+ res.status(error.statusCode || 500).json({
41
+ success: false,
42
+ error: error.message || 'Server Error',
43
+ ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
44
+ });
45
+ };
46
+
47
+ module.exports = errorHandler;