offbyt 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/README.md +2 -0
- package/cli/index.js +2 -0
- package/cli.js +206 -0
- package/core/detector/detectAxios.js +107 -0
- package/core/detector/detectFetch.js +148 -0
- package/core/detector/detectForms.js +55 -0
- package/core/detector/detectSocket.js +341 -0
- package/core/generator/generateControllers.js +17 -0
- package/core/generator/generateModels.js +25 -0
- package/core/generator/generateRoutes.js +17 -0
- package/core/generator/generateServer.js +18 -0
- package/core/generator/generateSocket.js +160 -0
- package/core/index.js +14 -0
- package/core/ir/IRTypes.js +25 -0
- package/core/ir/buildIR.js +83 -0
- package/core/parser/parseJS.js +26 -0
- package/core/parser/parseTS.js +27 -0
- package/core/rules/relationRules.js +38 -0
- package/core/rules/resourceRules.js +32 -0
- package/core/rules/schemaInference.js +26 -0
- package/core/scanner/scanProject.js +58 -0
- package/deploy/cloudflare.js +41 -0
- package/deploy/cloudflareWorker.js +122 -0
- package/deploy/connect.js +198 -0
- package/deploy/flyio.js +51 -0
- package/deploy/index.js +322 -0
- package/deploy/netlify.js +29 -0
- package/deploy/railway.js +215 -0
- package/deploy/render.js +195 -0
- package/deploy/utils.js +383 -0
- package/deploy/vercel.js +29 -0
- package/index.js +18 -0
- package/lib/generator/advancedCrudGenerator.js +475 -0
- package/lib/generator/crudCodeGenerator.js +486 -0
- package/lib/generator/irBasedGenerator.js +360 -0
- package/lib/ir-builder/index.js +16 -0
- package/lib/ir-builder/irBuilder.js +330 -0
- package/lib/ir-builder/rulesEngine.js +353 -0
- package/lib/ir-builder/templateEngine.js +193 -0
- package/lib/ir-builder/templates/index.js +14 -0
- package/lib/ir-builder/templates/model.template.js +47 -0
- package/lib/ir-builder/templates/routes-generic.template.js +66 -0
- package/lib/ir-builder/templates/routes-user.template.js +105 -0
- package/lib/ir-builder/templates/routes.template.js +102 -0
- package/lib/ir-builder/templates/validation.template.js +15 -0
- package/lib/ir-integration.js +349 -0
- package/lib/modes/benchmark.js +162 -0
- package/lib/modes/configBasedGenerator.js +2258 -0
- package/lib/modes/connect.js +1125 -0
- package/lib/modes/doctorAi.js +172 -0
- package/lib/modes/generateApi.js +435 -0
- package/lib/modes/interactiveSetup.js +548 -0
- package/lib/modes/offline.clean.js +14 -0
- package/lib/modes/offline.enhanced.js +787 -0
- package/lib/modes/offline.js +295 -0
- package/lib/modes/offline.v2.js +13 -0
- package/lib/modes/sync.js +629 -0
- package/lib/scanner/apiEndpointExtractor.js +387 -0
- package/lib/scanner/authPatternDetector.js +54 -0
- package/lib/scanner/frontendScanner.js +642 -0
- package/lib/utils/apiClientGenerator.js +242 -0
- package/lib/utils/apiScanner.js +95 -0
- package/lib/utils/codeInjector.js +350 -0
- package/lib/utils/doctor.js +381 -0
- package/lib/utils/envGenerator.js +36 -0
- package/lib/utils/loadTester.js +61 -0
- package/lib/utils/performanceAnalyzer.js +298 -0
- package/lib/utils/resourceDetector.js +281 -0
- package/package.json +20 -0
- package/templates/.env.template +31 -0
- package/templates/advanced.model.template.js +201 -0
- package/templates/advanced.route.template.js +341 -0
- package/templates/auth.middleware.template.js +87 -0
- package/templates/auth.routes.template.js +238 -0
- package/templates/auth.user.model.template.js +78 -0
- package/templates/cache.middleware.js +34 -0
- package/templates/chat.models.template.js +260 -0
- package/templates/chat.routes.template.js +478 -0
- package/templates/compression.middleware.js +19 -0
- package/templates/database.config.js +74 -0
- package/templates/errorHandler.middleware.js +54 -0
- package/templates/express/controller.ejs +26 -0
- package/templates/express/model.ejs +9 -0
- package/templates/express/route.ejs +18 -0
- package/templates/express/server.ejs +16 -0
- package/templates/frontend.env.template +14 -0
- package/templates/model.template.js +86 -0
- package/templates/package.production.json +51 -0
- package/templates/package.template.json +41 -0
- package/templates/pagination.utility.js +110 -0
- package/templates/production.server.template.js +233 -0
- package/templates/rateLimiter.middleware.js +36 -0
- package/templates/requestLogger.middleware.js +19 -0
- package/templates/response.helper.js +179 -0
- package/templates/route.template.js +130 -0
- package/templates/security.middleware.js +78 -0
- package/templates/server.template.js +91 -0
- package/templates/socket.server.template.js +433 -0
- package/templates/utils.helper.js +157 -0
- package/templates/validation.middleware.js +63 -0
- package/templates/validation.schema.js +128 -0
- package/utils/fileWriter.js +15 -0
- package/utils/logger.js +18 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRUD Code Generator
|
|
3
|
+
* Generates complete CRUD model and routes for any resource
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function generateCrudModel(resourceName, fields = [], hasAuth = false) {
|
|
7
|
+
const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
|
|
8
|
+
const collectionName = resourceName.toLowerCase();
|
|
9
|
+
|
|
10
|
+
const fieldDefinitions = generateFieldDefinitions(fields);
|
|
11
|
+
|
|
12
|
+
return `import mongoose from 'mongoose';
|
|
13
|
+
|
|
14
|
+
const ${modelName}Schema = new mongoose.Schema(
|
|
15
|
+
{
|
|
16
|
+
${fieldDefinitions}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
timestamps: true,
|
|
20
|
+
collection: '${collectionName}'
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
${hasAuth ? `
|
|
25
|
+
// Add userId reference if authenticated resources
|
|
26
|
+
${modelName}Schema.add({
|
|
27
|
+
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
|
|
28
|
+
});
|
|
29
|
+
` : ''}
|
|
30
|
+
|
|
31
|
+
${modelName}Schema.index({ createdAt: -1 });
|
|
32
|
+
|
|
33
|
+
const ${modelName} = mongoose.model('${modelName}', ${modelName}Schema);
|
|
34
|
+
|
|
35
|
+
export default ${modelName};
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate Mongoose field definitions from field names
|
|
41
|
+
*/
|
|
42
|
+
function generateFieldDefinitions(fields) {
|
|
43
|
+
const fieldDefs = [];
|
|
44
|
+
|
|
45
|
+
for (const field of fields) {
|
|
46
|
+
// Skip MongoDB's automatic _id field and id variations
|
|
47
|
+
if (field === '_id' || field === 'id' || field.toLowerCase() === 'id') {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const normalized = field.toLowerCase();
|
|
52
|
+
|
|
53
|
+
// Determine field type based on name
|
|
54
|
+
let fieldDef = '';
|
|
55
|
+
if (normalized === 'email') {
|
|
56
|
+
fieldDef = ` ${field}: { type: String, lowercase: true, match: /.+\\@.+\\..+/ }`;
|
|
57
|
+
} else if (normalized === 'price' || normalized === 'cost' || normalized === 'rating') {
|
|
58
|
+
fieldDef = ` ${field}: { type: Number, default: 0 }`;
|
|
59
|
+
} else if (normalized === 'status') {
|
|
60
|
+
fieldDef = ` ${field}: { type: String, enum: ['active', 'inactive', 'pending'], default: 'active' }`;
|
|
61
|
+
} else if (normalized === 'views' || normalized === 'likes' || normalized === 'count') {
|
|
62
|
+
fieldDef = ` ${field}: { type: Number, default: 0 }`;
|
|
63
|
+
} else if (normalized === 'userid' || normalized === 'authorid' || normalized === 'postid') {
|
|
64
|
+
fieldDef = ` ${field}: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }`;
|
|
65
|
+
} else if (normalized === 'duedate' || normalized === 'deadline' || normalized === 'startdate') {
|
|
66
|
+
fieldDef = ` ${field}: { type: Date }`;
|
|
67
|
+
} else if (normalized === 'completed' || normalized === 'active' || normalized === 'published') {
|
|
68
|
+
fieldDef = ` ${field}: { type: Boolean, default: false }`;
|
|
69
|
+
} else if (normalized === 'tags') {
|
|
70
|
+
fieldDef = ` ${field}: [{ type: String }]`;
|
|
71
|
+
} else if (normalized === 'items' || normalized === 'comments' || normalized === 'reviews') {
|
|
72
|
+
fieldDef = ` ${field}: [{ type: mongoose.Schema.Types.ObjectId }]`;
|
|
73
|
+
} else {
|
|
74
|
+
// Default to string
|
|
75
|
+
fieldDef = ` ${field}: { type: String }`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fieldDefs.push(fieldDef);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return fieldDefs.length > 0 ? fieldDefs.join(',\n') : ' _id: mongoose.Schema.Types.ObjectId';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate action-specific routes
|
|
86
|
+
* Creates routes for actions like /dashboard, /users, /analytics, /usage, etc.
|
|
87
|
+
*/
|
|
88
|
+
function generateActionRoutes(resourceName, modelName, actions = [], endpoints = [], hasAuth = false) {
|
|
89
|
+
if (!actions || actions.length === 0) return '';
|
|
90
|
+
|
|
91
|
+
const authMiddleware = hasAuth ? ', authenticateToken' : '';
|
|
92
|
+
let routesCode = `// ===== ACTION ROUTES =====\n`;
|
|
93
|
+
|
|
94
|
+
for (const action of actions) {
|
|
95
|
+
const endpoint = endpoints.find(ep => ep.action === action);
|
|
96
|
+
const method = endpoint ? endpoint.method : 'GET';
|
|
97
|
+
const requiresAuth = endpoint ? endpoint.requiresAuth : hasAuth;
|
|
98
|
+
const auth = requiresAuth ? ', authenticateToken' : '';
|
|
99
|
+
|
|
100
|
+
// Generate response based on action type
|
|
101
|
+
let responseData = '';
|
|
102
|
+
let responseKey = action.toLowerCase();
|
|
103
|
+
|
|
104
|
+
if (action === 'dashboard') {
|
|
105
|
+
responseData = `
|
|
106
|
+
// TODO: Implement dashboard stats logic
|
|
107
|
+
const stats = {
|
|
108
|
+
totalUsers: await ${modelName}.countDocuments({}),
|
|
109
|
+
activeSubscriptions: 0,
|
|
110
|
+
revenue: 0,
|
|
111
|
+
newUsers: 0
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
res.status(200).json({
|
|
115
|
+
success: true,
|
|
116
|
+
stats: stats
|
|
117
|
+
});`;
|
|
118
|
+
} else if (action === 'users') {
|
|
119
|
+
responseData = `
|
|
120
|
+
// Fetch all users
|
|
121
|
+
const users = await ${modelName}.find({}).sort({ createdAt: -1 });
|
|
122
|
+
|
|
123
|
+
res.status(200).json({
|
|
124
|
+
success: true,
|
|
125
|
+
count: users.length,
|
|
126
|
+
users: users
|
|
127
|
+
});`;
|
|
128
|
+
} else if (action === 'subscriptions') {
|
|
129
|
+
responseData = `
|
|
130
|
+
// TODO: Implement subscriptions logic
|
|
131
|
+
const subscriptions = [];
|
|
132
|
+
|
|
133
|
+
res.status(200).json({
|
|
134
|
+
success: true,
|
|
135
|
+
count: subscriptions.length,
|
|
136
|
+
subscriptions: subscriptions
|
|
137
|
+
});`;
|
|
138
|
+
} else if (action === 'analytics') {
|
|
139
|
+
responseData = `
|
|
140
|
+
// TODO: Implement analytics logic
|
|
141
|
+
const analytics = {
|
|
142
|
+
dailySignups: [],
|
|
143
|
+
revenue: []
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
res.status(200).json({
|
|
147
|
+
success: true,
|
|
148
|
+
analytics: analytics
|
|
149
|
+
});`;
|
|
150
|
+
} else if (action === 'usage') {
|
|
151
|
+
responseData = `
|
|
152
|
+
// Fetch user usage data
|
|
153
|
+
const usage = {
|
|
154
|
+
apiCalls: 0,
|
|
155
|
+
storage: 0,
|
|
156
|
+
users: 0
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
res.status(200).json({
|
|
160
|
+
success: true,
|
|
161
|
+
usage: usage
|
|
162
|
+
});`;
|
|
163
|
+
} else if (action === 'subscription') {
|
|
164
|
+
responseData = `
|
|
165
|
+
// Fetch user subscription
|
|
166
|
+
// TODO: Implement subscription logic with actual Subscription model
|
|
167
|
+
const subscription = {
|
|
168
|
+
plan: 'free',
|
|
169
|
+
status: 'active',
|
|
170
|
+
renewalDate: new Date()
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
res.status(200).json({
|
|
174
|
+
success: true,
|
|
175
|
+
subscription: subscription
|
|
176
|
+
});`;
|
|
177
|
+
} else if (action === 'invoices') {
|
|
178
|
+
responseData = `
|
|
179
|
+
// Fetch user invoices
|
|
180
|
+
// TODO: Implement invoices logic
|
|
181
|
+
const invoices = [];
|
|
182
|
+
|
|
183
|
+
res.status(200).json({
|
|
184
|
+
success: true,
|
|
185
|
+
count: invoices.length,
|
|
186
|
+
invoices: invoices
|
|
187
|
+
});`;
|
|
188
|
+
} else if (action === 'settings') {
|
|
189
|
+
responseData = `
|
|
190
|
+
// Fetch or update user settings
|
|
191
|
+
${method === 'PUT' ? `
|
|
192
|
+
const updatedSettings = req.body;
|
|
193
|
+
// TODO: Save settings to database
|
|
194
|
+
|
|
195
|
+
res.status(200).json({
|
|
196
|
+
success: true,
|
|
197
|
+
message: 'Settings updated successfully',
|
|
198
|
+
settings: updatedSettings
|
|
199
|
+
});` : `
|
|
200
|
+
// TODO: Fetch settings from database
|
|
201
|
+
const settings = {};
|
|
202
|
+
|
|
203
|
+
res.status(200).json({
|
|
204
|
+
success: true,
|
|
205
|
+
settings: settings
|
|
206
|
+
});`}`;
|
|
207
|
+
} else if (action === 'status' && method === 'PUT') {
|
|
208
|
+
// Special handling for /users/:id/status
|
|
209
|
+
responseData = `
|
|
210
|
+
const { status } = req.body;
|
|
211
|
+
const item = await ${modelName}.findByIdAndUpdate(
|
|
212
|
+
req.params.id,
|
|
213
|
+
{ status },
|
|
214
|
+
{ new: true }
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
res.status(200).json({
|
|
218
|
+
success: true,
|
|
219
|
+
message: 'Status updated successfully',
|
|
220
|
+
data: item
|
|
221
|
+
});`;
|
|
222
|
+
} else {
|
|
223
|
+
// Generic action
|
|
224
|
+
responseData = `
|
|
225
|
+
// TODO: Implement ${action} logic
|
|
226
|
+
res.status(200).json({
|
|
227
|
+
success: true,
|
|
228
|
+
message: '${action} endpoint',
|
|
229
|
+
${responseKey}: []
|
|
230
|
+
});`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Determine route path
|
|
234
|
+
let routePath = `/${action}`;
|
|
235
|
+
if (action === 'status') {
|
|
236
|
+
routePath = '/:id/status';
|
|
237
|
+
} else if (action === 'upgrade') {
|
|
238
|
+
routePath = '/subscription/upgrade';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const httpMethod = method.toLowerCase();
|
|
242
|
+
|
|
243
|
+
routesCode += `
|
|
244
|
+
router.${httpMethod}('${routePath}'${auth}, async (req, res) => {
|
|
245
|
+
try {${responseData}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
res.status(500).json({
|
|
248
|
+
success: false,
|
|
249
|
+
message: 'Error processing ${action}',
|
|
250
|
+
error: error.message
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return routesCode;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate CRUD routes for a resource
|
|
262
|
+
*/
|
|
263
|
+
export function generateCrudRoutes(resourceName, fields = [], hasAuth = false, actions = [], endpoints = []) {
|
|
264
|
+
const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
|
|
265
|
+
const routePath = `/${resourceName.toLowerCase()}`;
|
|
266
|
+
|
|
267
|
+
// Determine if this resource is user-specific (needs auth for ALL operations)
|
|
268
|
+
const userSpecificResources = ['cart', 'orders', 'order'];
|
|
269
|
+
const isUserSpecific = userSpecificResources.includes(resourceName.toLowerCase());
|
|
270
|
+
|
|
271
|
+
// For user-specific resources, all routes need auth
|
|
272
|
+
// For public resources (products, reviews, categories), only write operations need auth
|
|
273
|
+
const authMiddlewareWrite = hasAuth ? ', authenticateToken' : '';
|
|
274
|
+
const authMiddlewareRead = (hasAuth && isUserSpecific) ? ', authenticateToken' : '';
|
|
275
|
+
|
|
276
|
+
const userIdAssignment = hasAuth ? `
|
|
277
|
+
if (req.user) {
|
|
278
|
+
data.userId = req.user.id;
|
|
279
|
+
}` : '';
|
|
280
|
+
|
|
281
|
+
// Determine response key for list endpoint
|
|
282
|
+
// Cart -> "items", Dashboard -> "stats", Others -> resource name
|
|
283
|
+
let responseKey = resourceName.toLowerCase();
|
|
284
|
+
if (responseKey === 'cart') responseKey = 'items';
|
|
285
|
+
else if (responseKey === 'dashboard') responseKey = 'stats';
|
|
286
|
+
|
|
287
|
+
return `import express from 'express';
|
|
288
|
+
import ${modelName} from '../models/${modelName}.js';
|
|
289
|
+
${hasAuth ? "import { authenticateToken } from '../middleware/auth.js';" : ''}
|
|
290
|
+
|
|
291
|
+
const router = express.Router();
|
|
292
|
+
|
|
293
|
+
${generateActionRoutes(resourceName, modelName, actions, endpoints, hasAuth)}
|
|
294
|
+
|
|
295
|
+
// ===== CREATE =====
|
|
296
|
+
router.post('/'${authMiddlewareWrite}, async (req, res) => {
|
|
297
|
+
try {
|
|
298
|
+
const data = req.body;
|
|
299
|
+
${userIdAssignment}
|
|
300
|
+
|
|
301
|
+
const item = new ${modelName}(data);
|
|
302
|
+
await item.save();
|
|
303
|
+
|
|
304
|
+
res.status(201).json({
|
|
305
|
+
success: true,
|
|
306
|
+
message: '${modelName} created successfully',
|
|
307
|
+
data: item
|
|
308
|
+
});
|
|
309
|
+
} catch (error) {
|
|
310
|
+
res.status(400).json({
|
|
311
|
+
success: false,
|
|
312
|
+
message: 'Error creating ${modelName}',
|
|
313
|
+
error: error.message
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// ===== READ ALL (LIST) =====
|
|
319
|
+
router.get('/'${authMiddlewareRead}, async (req, res) => {
|
|
320
|
+
try {
|
|
321
|
+
const query = ${isUserSpecific && hasAuth ? "req.user ? { userId: req.user.id } : {}" : "{}"};
|
|
322
|
+
const items = await ${modelName}.find(query).sort({ createdAt: -1 });
|
|
323
|
+
|
|
324
|
+
res.status(200).json({
|
|
325
|
+
success: true,
|
|
326
|
+
count: items.length,
|
|
327
|
+
${responseKey}: items
|
|
328
|
+
});
|
|
329
|
+
} catch (error) {
|
|
330
|
+
res.status(500).json({
|
|
331
|
+
success: false,
|
|
332
|
+
message: 'Error fetching ${modelName}s',
|
|
333
|
+
error: error.message
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ===== READ SINGLE =====
|
|
339
|
+
router.get('/:id'${authMiddlewareRead}, async (req, res) => {
|
|
340
|
+
try {
|
|
341
|
+
const item = await ${modelName}.findById(req.params.id);
|
|
342
|
+
|
|
343
|
+
if (!item) {
|
|
344
|
+
return res.status(404).json({
|
|
345
|
+
success: false,
|
|
346
|
+
message: '${modelName} not found'
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
res.status(200).json({
|
|
351
|
+
success: true,
|
|
352
|
+
data: item
|
|
353
|
+
});
|
|
354
|
+
} catch (error) {
|
|
355
|
+
res.status(500).json({
|
|
356
|
+
success: false,
|
|
357
|
+
message: 'Error fetching ${modelName}',
|
|
358
|
+
error: error.message
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// ===== UPDATE =====
|
|
364
|
+
router.put('/:id'${authMiddlewareWrite}, async (req, res) => {
|
|
365
|
+
try {
|
|
366
|
+
const item = await ${modelName}.findByIdAndUpdate(
|
|
367
|
+
req.params.id,
|
|
368
|
+
req.body,
|
|
369
|
+
{ new: true, runValidators: true }
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (!item) {
|
|
373
|
+
return res.status(404).json({
|
|
374
|
+
success: false,
|
|
375
|
+
message: '${modelName} not found'
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
res.status(200).json({
|
|
380
|
+
success: true,
|
|
381
|
+
message: '${modelName} updated successfully',
|
|
382
|
+
data: item
|
|
383
|
+
});
|
|
384
|
+
} catch (error) {
|
|
385
|
+
res.status(400).json({
|
|
386
|
+
success: false,
|
|
387
|
+
message: 'Error updating ${modelName}',
|
|
388
|
+
error: error.message
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// ===== DELETE =====
|
|
394
|
+
router.delete('/:id'${authMiddlewareWrite}, async (req, res) => {
|
|
395
|
+
try {
|
|
396
|
+
const item = await ${modelName}.findByIdAndDelete(req.params.id);
|
|
397
|
+
|
|
398
|
+
if (!item) {
|
|
399
|
+
return res.status(404).json({
|
|
400
|
+
success: false,
|
|
401
|
+
message: '${modelName} not found'
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
res.status(200).json({
|
|
406
|
+
success: true,
|
|
407
|
+
message: '${modelName} deleted successfully'
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
res.status(500).json({
|
|
411
|
+
success: false,
|
|
412
|
+
message: 'Error deleting ${modelName}',
|
|
413
|
+
error: error.message
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
export default router;
|
|
419
|
+
`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Generate server imports and route registrations
|
|
424
|
+
*/
|
|
425
|
+
export function generateServerRouteRegistration(resources, hasAuth = false) {
|
|
426
|
+
const imports = [];
|
|
427
|
+
const registrations = [];
|
|
428
|
+
|
|
429
|
+
for (const [resourceName, resource] of Object.entries(resources)) {
|
|
430
|
+
if (resourceName === 'api') continue; // Skip generic api resource
|
|
431
|
+
|
|
432
|
+
const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
|
|
433
|
+
imports.push(`import ${resourceName}Routes from './routes/${resourceName}.routes.js';`);
|
|
434
|
+
registrations.push(`app.use('/api/${resourceName}', ${resourceName}Routes);`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let code = '';
|
|
438
|
+
|
|
439
|
+
if (imports.length > 0) {
|
|
440
|
+
code += '// Resource Routes\\n' + imports.join('\\n') + '\\n\\n';
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (registrations.length > 0) {
|
|
444
|
+
code += '// Register Routes\\n' + registrations.join('\\n');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return code;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Generate updated package.json dependencies for CRUD
|
|
452
|
+
*/
|
|
453
|
+
export function getRequiredCrudDependencies() {
|
|
454
|
+
return {
|
|
455
|
+
'express': '^4.18.2',
|
|
456
|
+
'mongoose': '^8.0.0',
|
|
457
|
+
'bcryptjs': '^2.4.3',
|
|
458
|
+
'jsonwebtoken': '^9.1.0',
|
|
459
|
+
'cors': '^2.8.5',
|
|
460
|
+
'dotenv': '^16.3.1'
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Validate if resource has proper CRUD structure
|
|
466
|
+
*/
|
|
467
|
+
export function validateCrudResource(resource) {
|
|
468
|
+
const errors = [];
|
|
469
|
+
|
|
470
|
+
if (!resource.name) {
|
|
471
|
+
errors.push('Resource must have a name');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (resource.methods.length === 0) {
|
|
475
|
+
errors.push('Resource must have at least one HTTP method');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (resource.fields.length === 0) {
|
|
479
|
+
errors.push('Resource should have at least one field defined');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
isValid: errors.length === 0,
|
|
484
|
+
errors
|
|
485
|
+
};
|
|
486
|
+
}
|