nexpgen 1.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.
- package/.gitattributes +2 -0
- package/README.md +517 -0
- package/bin/cli.js +58 -0
- package/package.json +44 -0
- package/src/commands/generate.js +205 -0
- package/src/commands/init.js +191 -0
- package/src/templates/controller.template +127 -0
- package/src/templates/error.template +24 -0
- package/src/templates/helpers/imageUpload.template +191 -0
- package/src/templates/helpers/timeConverter.template +171 -0
- package/src/templates/middleware.template +302 -0
- package/src/templates/route.template +56 -0
- package/src/templates/servers/clean-arrow.template +42 -0
- package/src/templates/servers/clean-class.template +46 -0
- package/src/templates/servers/layered-arrow.template +42 -0
- package/src/templates/servers/layered-class.template +46 -0
- package/src/templates/servers/microservices-arrow.template +41 -0
- package/src/templates/servers/microservices-class.template +45 -0
- package/src/templates/servers/mvc-arrow.template +44 -0
- package/src/templates/servers/mvc-class.template +48 -0
- package/src/templates/servers/simple-arrow.template +42 -0
- package/src/templates/servers/simple-class.template +46 -0
- package/src/templates/service.template +121 -0
- package/src/templates/util.template +39 -0
- package/src/utils/architectureGenerator.js +925 -0
- package/src/utils/fileGenerator.js +617 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
{{#if isClass}}
|
|
2
|
+
const multer = require('multer');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
class ImageUploadHelper {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.uploadDir = path.join(process.cwd(), 'config', 'uploads', 'images');
|
|
9
|
+
this.publicDir = path.join(process.cwd(), 'public', 'images');
|
|
10
|
+
this.storage = this.setupStorage();
|
|
11
|
+
this.upload = this.setupMulter();
|
|
12
|
+
this.ensureDirectories();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
ensureDirectories() {
|
|
16
|
+
// Ensure upload directory exists
|
|
17
|
+
if (!fs.existsSync(this.uploadDir)) {
|
|
18
|
+
fs.mkdirSync(this.uploadDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
// Ensure public images directory exists
|
|
21
|
+
if (!fs.existsSync(this.publicDir)) {
|
|
22
|
+
fs.mkdirSync(this.publicDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setupStorage() {
|
|
27
|
+
return multer.diskStorage({
|
|
28
|
+
destination: (req, file, cb) => {
|
|
29
|
+
cb(null, this.uploadDir);
|
|
30
|
+
},
|
|
31
|
+
filename: (req, file, cb) => {
|
|
32
|
+
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
|
33
|
+
const ext = path.extname(file.originalname);
|
|
34
|
+
cb(null, file.fieldname + '-' + uniqueSuffix + ext);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setupMulter() {
|
|
40
|
+
const fileFilter = (req, file, cb) => {
|
|
41
|
+
const allowedTypes = /jpeg|jpg|png|gif|webp/;
|
|
42
|
+
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
|
|
43
|
+
const mimetype = allowedTypes.test(file.mimetype);
|
|
44
|
+
|
|
45
|
+
if (mimetype && extname) {
|
|
46
|
+
return cb(null, true);
|
|
47
|
+
} else {
|
|
48
|
+
cb(new Error('Only image files are allowed!'));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return multer({
|
|
53
|
+
storage: this.storage,
|
|
54
|
+
limits: {
|
|
55
|
+
fileSize: 5 * 1024 * 1024 // 5MB limit
|
|
56
|
+
},
|
|
57
|
+
fileFilter: fileFilter
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getSingleUpload(fieldName) {
|
|
62
|
+
return this.upload.single(fieldName);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getMultipleUpload(fieldName, maxCount = 5) {
|
|
66
|
+
return this.upload.array(fieldName, maxCount);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
deleteFile(filePath) {
|
|
70
|
+
try {
|
|
71
|
+
const fullPath = path.isAbsolute(filePath)
|
|
72
|
+
? filePath
|
|
73
|
+
: path.join(this.uploadDir, filePath);
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(fullPath)) {
|
|
76
|
+
fs.unlinkSync(fullPath);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(`Error deleting file: ${error.message}`);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getUploadPath() {
|
|
87
|
+
return this.uploadDir;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getPublicPath() {
|
|
91
|
+
return this.publicDir;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = new ImageUploadHelper();
|
|
96
|
+
{{else}}
|
|
97
|
+
const multer = require('multer');
|
|
98
|
+
const path = require('path');
|
|
99
|
+
const fs = require('fs');
|
|
100
|
+
|
|
101
|
+
const uploadDir = path.join(process.cwd(), 'config', 'uploads', 'images');
|
|
102
|
+
const publicDir = path.join(process.cwd(), 'public', 'images');
|
|
103
|
+
|
|
104
|
+
const ensureDirectories = () => {
|
|
105
|
+
// Ensure upload directory exists
|
|
106
|
+
if (!fs.existsSync(uploadDir)) {
|
|
107
|
+
fs.mkdirSync(uploadDir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
// Ensure public images directory exists
|
|
110
|
+
if (!fs.existsSync(publicDir)) {
|
|
111
|
+
fs.mkdirSync(publicDir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
ensureDirectories();
|
|
116
|
+
|
|
117
|
+
const setupStorage = () => {
|
|
118
|
+
return multer.diskStorage({
|
|
119
|
+
destination: (req, file, cb) => {
|
|
120
|
+
cb(null, uploadDir);
|
|
121
|
+
},
|
|
122
|
+
filename: (req, file, cb) => {
|
|
123
|
+
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
|
124
|
+
const ext = path.extname(file.originalname);
|
|
125
|
+
cb(null, file.fieldname + '-' + uniqueSuffix + ext);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const fileFilter = (req, file, cb) => {
|
|
131
|
+
const allowedTypes = /jpeg|jpg|png|gif|webp/;
|
|
132
|
+
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
|
|
133
|
+
const mimetype = allowedTypes.test(file.mimetype);
|
|
134
|
+
|
|
135
|
+
if (mimetype && extname) {
|
|
136
|
+
return cb(null, true);
|
|
137
|
+
} else {
|
|
138
|
+
cb(new Error('Only image files are allowed!'));
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const upload = multer({
|
|
143
|
+
storage: setupStorage(),
|
|
144
|
+
limits: {
|
|
145
|
+
fileSize: 5 * 1024 * 1024 // 5MB limit
|
|
146
|
+
},
|
|
147
|
+
fileFilter: fileFilter
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const getSingleUpload = (fieldName) => {
|
|
151
|
+
return upload.single(fieldName);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const getMultipleUpload = (fieldName, maxCount = 5) => {
|
|
155
|
+
return upload.array(fieldName, maxCount);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const deleteFile = (filePath) => {
|
|
159
|
+
try {
|
|
160
|
+
const fullPath = path.isAbsolute(filePath)
|
|
161
|
+
? filePath
|
|
162
|
+
: path.join(uploadDir, filePath);
|
|
163
|
+
|
|
164
|
+
if (fs.existsSync(fullPath)) {
|
|
165
|
+
fs.unlinkSync(fullPath);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error(`Error deleting file: ${error.message}`);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const getUploadPath = () => {
|
|
176
|
+
return uploadDir;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const getPublicPath = () => {
|
|
180
|
+
return publicDir;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
getSingleUpload,
|
|
185
|
+
getMultipleUpload,
|
|
186
|
+
deleteFile,
|
|
187
|
+
getUploadPath,
|
|
188
|
+
getPublicPath
|
|
189
|
+
};
|
|
190
|
+
{{/if}}
|
|
191
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
{{#if isClass}}
|
|
2
|
+
class TimeConverterHelper {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.timeZones = {
|
|
5
|
+
UTC: 'UTC',
|
|
6
|
+
EST: 'America/New_York',
|
|
7
|
+
PST: 'America/Los_Angeles',
|
|
8
|
+
GMT: 'Europe/London',
|
|
9
|
+
IST: 'Asia/Kolkata'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
toUTC(date) {
|
|
14
|
+
return new Date(date.toUTCString());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toLocal(date, timeZone = 'UTC') {
|
|
18
|
+
return new Date(date.toLocaleString('en-US', { timeZone }));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
format(date, format = 'YYYY-MM-DD HH:mm:ss') {
|
|
22
|
+
const year = date.getFullYear();
|
|
23
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
24
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
25
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
26
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
27
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
28
|
+
|
|
29
|
+
return format
|
|
30
|
+
.replace('YYYY', year)
|
|
31
|
+
.replace('MM', month)
|
|
32
|
+
.replace('DD', day)
|
|
33
|
+
.replace('HH', hours)
|
|
34
|
+
.replace('mm', minutes)
|
|
35
|
+
.replace('ss', seconds);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addDays(date, days) {
|
|
39
|
+
const result = new Date(date);
|
|
40
|
+
result.setDate(result.getDate() + days);
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addHours(date, hours) {
|
|
45
|
+
const result = new Date(date);
|
|
46
|
+
result.setHours(result.getHours() + hours);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addMinutes(date, minutes) {
|
|
51
|
+
const result = new Date(date);
|
|
52
|
+
result.setMinutes(result.getMinutes() + minutes);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
differenceInDays(date1, date2) {
|
|
57
|
+
const diffTime = Math.abs(date2 - date1);
|
|
58
|
+
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
differenceInHours(date1, date2) {
|
|
62
|
+
const diffTime = Math.abs(date2 - date1);
|
|
63
|
+
return Math.ceil(diffTime / (1000 * 60 * 60));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
differenceInMinutes(date1, date2) {
|
|
67
|
+
const diffTime = Math.abs(date2 - date1);
|
|
68
|
+
return Math.ceil(diffTime / (1000 * 60));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
isBefore(date1, date2) {
|
|
72
|
+
return date1 < date2;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
isAfter(date1, date2) {
|
|
76
|
+
return date1 > date2;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
isSameDay(date1, date2) {
|
|
80
|
+
return date1.toDateString() === date2.toDateString();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = new TimeConverterHelper();
|
|
85
|
+
{{else}}
|
|
86
|
+
const toUTC = (date) => {
|
|
87
|
+
return new Date(date.toUTCString());
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const toLocal = (date, timeZone = 'UTC') => {
|
|
91
|
+
return new Date(date.toLocaleString('en-US', { timeZone }));
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const format = (date, formatStr = 'YYYY-MM-DD HH:mm:ss') => {
|
|
95
|
+
const year = date.getFullYear();
|
|
96
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
97
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
98
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
99
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
100
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
101
|
+
|
|
102
|
+
return formatStr
|
|
103
|
+
.replace('YYYY', year)
|
|
104
|
+
.replace('MM', month)
|
|
105
|
+
.replace('DD', day)
|
|
106
|
+
.replace('HH', hours)
|
|
107
|
+
.replace('mm', minutes)
|
|
108
|
+
.replace('ss', seconds);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const addDays = (date, days) => {
|
|
112
|
+
const result = new Date(date);
|
|
113
|
+
result.setDate(result.getDate() + days);
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const addHours = (date, hours) => {
|
|
118
|
+
const result = new Date(date);
|
|
119
|
+
result.setHours(result.getHours() + hours);
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const addMinutes = (date, minutes) => {
|
|
124
|
+
const result = new Date(date);
|
|
125
|
+
result.setMinutes(result.getMinutes() + minutes);
|
|
126
|
+
return result;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const differenceInDays = (date1, date2) => {
|
|
130
|
+
const diffTime = Math.abs(date2 - date1);
|
|
131
|
+
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const differenceInHours = (date1, date2) => {
|
|
135
|
+
const diffTime = Math.abs(date2 - date1);
|
|
136
|
+
return Math.ceil(diffTime / (1000 * 60 * 60));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const differenceInMinutes = (date1, date2) => {
|
|
140
|
+
const diffTime = Math.abs(date2 - date1);
|
|
141
|
+
return Math.ceil(diffTime / (1000 * 60));
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const isBefore = (date1, date2) => {
|
|
145
|
+
return date1 < date2;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const isAfter = (date1, date2) => {
|
|
149
|
+
return date1 > date2;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const isSameDay = (date1, date2) => {
|
|
153
|
+
return date1.toDateString() === date2.toDateString();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
toUTC,
|
|
158
|
+
toLocal,
|
|
159
|
+
format,
|
|
160
|
+
addDays,
|
|
161
|
+
addHours,
|
|
162
|
+
addMinutes,
|
|
163
|
+
differenceInDays,
|
|
164
|
+
differenceInHours,
|
|
165
|
+
differenceInMinutes,
|
|
166
|
+
isBefore,
|
|
167
|
+
isAfter,
|
|
168
|
+
isSameDay
|
|
169
|
+
};
|
|
170
|
+
{{/if}}
|
|
171
|
+
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
{{#if isClass}}
|
|
2
|
+
class {{className}}Middleware {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.name = '{{name}}';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
{{#if isAuth}}
|
|
8
|
+
authenticate(req, res, next) {
|
|
9
|
+
try {
|
|
10
|
+
// TODO: Implement authentication logic
|
|
11
|
+
const token = req.headers.authorization?.split(' ')[1];
|
|
12
|
+
|
|
13
|
+
if (!token) {
|
|
14
|
+
return res.status(401).json({
|
|
15
|
+
success: false,
|
|
16
|
+
message: 'Authentication token required'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// TODO: Verify token
|
|
21
|
+
// const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
22
|
+
// req.user = decoded;
|
|
23
|
+
|
|
24
|
+
next();
|
|
25
|
+
} catch (error) {
|
|
26
|
+
res.status(401).json({
|
|
27
|
+
success: false,
|
|
28
|
+
message: 'Invalid or expired token'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
authorize(...roles) {
|
|
34
|
+
return (req, res, next) => {
|
|
35
|
+
try {
|
|
36
|
+
// TODO: Check if user has required role
|
|
37
|
+
// if (!roles.includes(req.user.role)) {
|
|
38
|
+
// return res.status(403).json({
|
|
39
|
+
// success: false,
|
|
40
|
+
// message: 'Insufficient permissions'
|
|
41
|
+
// });
|
|
42
|
+
// }
|
|
43
|
+
next();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
res.status(403).json({
|
|
46
|
+
success: false,
|
|
47
|
+
message: 'Authorization failed'
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
54
|
+
{{#if isValidation}}
|
|
55
|
+
validate(validationSchema) {
|
|
56
|
+
return (req, res, next) => {
|
|
57
|
+
try {
|
|
58
|
+
// TODO: Implement validation using Joi/Yup
|
|
59
|
+
// const { error, value } = validationSchema.validate(req.body);
|
|
60
|
+
// if (error) {
|
|
61
|
+
// return res.status(400).json({
|
|
62
|
+
// success: false,
|
|
63
|
+
// message: 'Validation error',
|
|
64
|
+
// errors: error.details.map(detail => detail.message)
|
|
65
|
+
// });
|
|
66
|
+
// }
|
|
67
|
+
// req.validatedData = value;
|
|
68
|
+
next();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
res.status(400).json({
|
|
71
|
+
success: false,
|
|
72
|
+
message: 'Validation failed',
|
|
73
|
+
error: error.message
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
{{/if}}
|
|
79
|
+
|
|
80
|
+
{{#if isErrorHandler}}
|
|
81
|
+
errorHandler(err, req, res, next) {
|
|
82
|
+
console.error('Error:', err);
|
|
83
|
+
|
|
84
|
+
// Default error
|
|
85
|
+
let statusCode = err.statusCode || 500;
|
|
86
|
+
let message = err.message || 'Internal server error';
|
|
87
|
+
|
|
88
|
+
// Handle specific error types
|
|
89
|
+
if (err.name === 'ValidationError') {
|
|
90
|
+
statusCode = 400;
|
|
91
|
+
message = 'Validation error';
|
|
92
|
+
} else if (err.name === 'UnauthorizedError') {
|
|
93
|
+
statusCode = 401;
|
|
94
|
+
message = 'Unauthorized';
|
|
95
|
+
} else if (err.name === 'CastError') {
|
|
96
|
+
statusCode = 400;
|
|
97
|
+
message = 'Invalid ID format';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
res.status(statusCode).json({
|
|
101
|
+
success: false,
|
|
102
|
+
message: message,
|
|
103
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
notFound(req, res, next) {
|
|
108
|
+
res.status(404).json({
|
|
109
|
+
success: false,
|
|
110
|
+
message: `Route ${req.originalUrl} not found`
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
{{/if}}
|
|
114
|
+
|
|
115
|
+
{{#if isRateLimit}}
|
|
116
|
+
rateLimiter(windowMs = 15 * 60 * 1000, max = 100) {
|
|
117
|
+
const requests = new Map();
|
|
118
|
+
|
|
119
|
+
return (req, res, next) => {
|
|
120
|
+
const ip = req.ip || req.connection.remoteAddress;
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
|
|
123
|
+
if (!requests.has(ip)) {
|
|
124
|
+
requests.set(ip, { count: 1, resetTime: now + windowMs });
|
|
125
|
+
return next();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const record = requests.get(ip);
|
|
129
|
+
|
|
130
|
+
if (now > record.resetTime) {
|
|
131
|
+
record.count = 1;
|
|
132
|
+
record.resetTime = now + windowMs;
|
|
133
|
+
return next();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (record.count >= max) {
|
|
137
|
+
return res.status(429).json({
|
|
138
|
+
success: false,
|
|
139
|
+
message: 'Too many requests, please try again later'
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
record.count++;
|
|
144
|
+
next();
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
{{/if}}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = new {{className}}Middleware();
|
|
151
|
+
{{else}}
|
|
152
|
+
{{#if isAuth}}
|
|
153
|
+
const authenticate = (req, res, next) => {
|
|
154
|
+
try {
|
|
155
|
+
// TODO: Implement authentication logic
|
|
156
|
+
const token = req.headers.authorization?.split(' ')[1];
|
|
157
|
+
|
|
158
|
+
if (!token) {
|
|
159
|
+
return res.status(401).json({
|
|
160
|
+
success: false,
|
|
161
|
+
message: 'Authentication token required'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// TODO: Verify token
|
|
166
|
+
// const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
167
|
+
// req.user = decoded;
|
|
168
|
+
|
|
169
|
+
next();
|
|
170
|
+
} catch (error) {
|
|
171
|
+
res.status(401).json({
|
|
172
|
+
success: false,
|
|
173
|
+
message: 'Invalid or expired token'
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const authorize = (...roles) => {
|
|
179
|
+
return (req, res, next) => {
|
|
180
|
+
try {
|
|
181
|
+
// TODO: Check if user has required role
|
|
182
|
+
// if (!roles.includes(req.user.role)) {
|
|
183
|
+
// return res.status(403).json({
|
|
184
|
+
// success: false,
|
|
185
|
+
// message: 'Insufficient permissions'
|
|
186
|
+
// });
|
|
187
|
+
// }
|
|
188
|
+
next();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
res.status(403).json({
|
|
191
|
+
success: false,
|
|
192
|
+
message: 'Authorization failed'
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
module.exports = { authenticate, authorize };
|
|
199
|
+
{{/if}}
|
|
200
|
+
|
|
201
|
+
{{#if isValidation}}
|
|
202
|
+
const validate = (validationSchema) => {
|
|
203
|
+
return (req, res, next) => {
|
|
204
|
+
try {
|
|
205
|
+
// TODO: Implement validation using Joi/Yup
|
|
206
|
+
// const { error, value } = validationSchema.validate(req.body);
|
|
207
|
+
// if (error) {
|
|
208
|
+
// return res.status(400).json({
|
|
209
|
+
// success: false,
|
|
210
|
+
// message: 'Validation error',
|
|
211
|
+
// errors: error.details.map(detail => detail.message)
|
|
212
|
+
// });
|
|
213
|
+
// }
|
|
214
|
+
// req.validatedData = value;
|
|
215
|
+
next();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
res.status(400).json({
|
|
218
|
+
success: false,
|
|
219
|
+
message: 'Validation failed',
|
|
220
|
+
error: error.message
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
module.exports = { validate };
|
|
227
|
+
{{/if}}
|
|
228
|
+
|
|
229
|
+
{{#if isErrorHandler}}
|
|
230
|
+
const errorHandler = (err, req, res, next) => {
|
|
231
|
+
console.error('Error:', err);
|
|
232
|
+
|
|
233
|
+
// Default error
|
|
234
|
+
let statusCode = err.statusCode || 500;
|
|
235
|
+
let message = err.message || 'Internal server error';
|
|
236
|
+
|
|
237
|
+
// Handle specific error types
|
|
238
|
+
if (err.name === 'ValidationError') {
|
|
239
|
+
statusCode = 400;
|
|
240
|
+
message = 'Validation error';
|
|
241
|
+
} else if (err.name === 'UnauthorizedError') {
|
|
242
|
+
statusCode = 401;
|
|
243
|
+
message = 'Unauthorized';
|
|
244
|
+
} else if (err.name === 'CastError') {
|
|
245
|
+
statusCode = 400;
|
|
246
|
+
message = 'Invalid ID format';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
res.status(statusCode).json({
|
|
250
|
+
success: false,
|
|
251
|
+
message: message,
|
|
252
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const notFound = (req, res, next) => {
|
|
257
|
+
res.status(404).json({
|
|
258
|
+
success: false,
|
|
259
|
+
message: `Route ${req.originalUrl} not found`
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
module.exports = { errorHandler, notFound };
|
|
264
|
+
{{/if}}
|
|
265
|
+
|
|
266
|
+
{{#if isRateLimit}}
|
|
267
|
+
const rateLimiter = (windowMs = 15 * 60 * 1000, max = 100) => {
|
|
268
|
+
const requests = new Map();
|
|
269
|
+
|
|
270
|
+
return (req, res, next) => {
|
|
271
|
+
const ip = req.ip || req.connection.remoteAddress;
|
|
272
|
+
const now = Date.now();
|
|
273
|
+
|
|
274
|
+
if (!requests.has(ip)) {
|
|
275
|
+
requests.set(ip, { count: 1, resetTime: now + windowMs });
|
|
276
|
+
return next();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const record = requests.get(ip);
|
|
280
|
+
|
|
281
|
+
if (now > record.resetTime) {
|
|
282
|
+
record.count = 1;
|
|
283
|
+
record.resetTime = now + windowMs;
|
|
284
|
+
return next();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (record.count >= max) {
|
|
288
|
+
return res.status(429).json({
|
|
289
|
+
success: false,
|
|
290
|
+
message: 'Too many requests, please try again later'
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
record.count++;
|
|
295
|
+
next();
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
module.exports = { rateLimiter };
|
|
300
|
+
{{/if}}
|
|
301
|
+
{{/if}}
|
|
302
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{{#if isClass}}
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const {{camelCaseName}}Controller = require('../controllers/{{name}}.controller');
|
|
4
|
+
|
|
5
|
+
class {{className}}Routes {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.router = express.Router();
|
|
8
|
+
this.setupRoutes();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
setupRoutes() {
|
|
12
|
+
// GET /api/{{name}}s - Get all {{name}}s
|
|
13
|
+
this.router.get('/', {{camelCaseName}}Controller.getAll);
|
|
14
|
+
|
|
15
|
+
// GET /api/{{name}}s/:id - Get {{name}} by ID
|
|
16
|
+
this.router.get('/:id', {{camelCaseName}}Controller.getById);
|
|
17
|
+
|
|
18
|
+
// POST /api/{{name}}s - Create new {{name}}
|
|
19
|
+
this.router.post('/', {{camelCaseName}}Controller.create);
|
|
20
|
+
|
|
21
|
+
// PUT /api/{{name}}s/:id - Update {{name}}
|
|
22
|
+
this.router.put('/:id', {{camelCaseName}}Controller.update);
|
|
23
|
+
|
|
24
|
+
// DELETE /api/{{name}}s/:id - Delete {{name}}
|
|
25
|
+
this.router.delete('/:id', {{camelCaseName}}Controller.remove);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getRouter() {
|
|
29
|
+
return this.router;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = new {{className}}Routes().getRouter();
|
|
34
|
+
{{else}}
|
|
35
|
+
const express = require('express');
|
|
36
|
+
const router = express.Router();
|
|
37
|
+
const {{camelCaseName}}Controller = require('../controllers/{{name}}.controller');
|
|
38
|
+
|
|
39
|
+
// GET /api/{{name}}s - Get all {{name}}s
|
|
40
|
+
router.get('/', {{camelCaseName}}Controller.getAll);
|
|
41
|
+
|
|
42
|
+
// GET /api/{{name}}s/:id - Get {{name}} by ID
|
|
43
|
+
router.get('/:id', {{camelCaseName}}Controller.getById);
|
|
44
|
+
|
|
45
|
+
// POST /api/{{name}}s - Create new {{name}}
|
|
46
|
+
router.post('/', {{camelCaseName}}Controller.create);
|
|
47
|
+
|
|
48
|
+
// PUT /api/{{name}}s/:id - Update {{name}}
|
|
49
|
+
router.put('/:id', {{camelCaseName}}Controller.update);
|
|
50
|
+
|
|
51
|
+
// DELETE /api/{{name}}s/:id - Delete {{name}}
|
|
52
|
+
router.delete('/:id', {{camelCaseName}}Controller.remove);
|
|
53
|
+
|
|
54
|
+
module.exports = router;
|
|
55
|
+
{{/if}}
|
|
56
|
+
|