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.
Files changed (103) hide show
  1. package/README.md +2 -0
  2. package/cli/index.js +2 -0
  3. package/cli.js +206 -0
  4. package/core/detector/detectAxios.js +107 -0
  5. package/core/detector/detectFetch.js +148 -0
  6. package/core/detector/detectForms.js +55 -0
  7. package/core/detector/detectSocket.js +341 -0
  8. package/core/generator/generateControllers.js +17 -0
  9. package/core/generator/generateModels.js +25 -0
  10. package/core/generator/generateRoutes.js +17 -0
  11. package/core/generator/generateServer.js +18 -0
  12. package/core/generator/generateSocket.js +160 -0
  13. package/core/index.js +14 -0
  14. package/core/ir/IRTypes.js +25 -0
  15. package/core/ir/buildIR.js +83 -0
  16. package/core/parser/parseJS.js +26 -0
  17. package/core/parser/parseTS.js +27 -0
  18. package/core/rules/relationRules.js +38 -0
  19. package/core/rules/resourceRules.js +32 -0
  20. package/core/rules/schemaInference.js +26 -0
  21. package/core/scanner/scanProject.js +58 -0
  22. package/deploy/cloudflare.js +41 -0
  23. package/deploy/cloudflareWorker.js +122 -0
  24. package/deploy/connect.js +198 -0
  25. package/deploy/flyio.js +51 -0
  26. package/deploy/index.js +322 -0
  27. package/deploy/netlify.js +29 -0
  28. package/deploy/railway.js +215 -0
  29. package/deploy/render.js +195 -0
  30. package/deploy/utils.js +383 -0
  31. package/deploy/vercel.js +29 -0
  32. package/index.js +18 -0
  33. package/lib/generator/advancedCrudGenerator.js +475 -0
  34. package/lib/generator/crudCodeGenerator.js +486 -0
  35. package/lib/generator/irBasedGenerator.js +360 -0
  36. package/lib/ir-builder/index.js +16 -0
  37. package/lib/ir-builder/irBuilder.js +330 -0
  38. package/lib/ir-builder/rulesEngine.js +353 -0
  39. package/lib/ir-builder/templateEngine.js +193 -0
  40. package/lib/ir-builder/templates/index.js +14 -0
  41. package/lib/ir-builder/templates/model.template.js +47 -0
  42. package/lib/ir-builder/templates/routes-generic.template.js +66 -0
  43. package/lib/ir-builder/templates/routes-user.template.js +105 -0
  44. package/lib/ir-builder/templates/routes.template.js +102 -0
  45. package/lib/ir-builder/templates/validation.template.js +15 -0
  46. package/lib/ir-integration.js +349 -0
  47. package/lib/modes/benchmark.js +162 -0
  48. package/lib/modes/configBasedGenerator.js +2258 -0
  49. package/lib/modes/connect.js +1125 -0
  50. package/lib/modes/doctorAi.js +172 -0
  51. package/lib/modes/generateApi.js +435 -0
  52. package/lib/modes/interactiveSetup.js +548 -0
  53. package/lib/modes/offline.clean.js +14 -0
  54. package/lib/modes/offline.enhanced.js +787 -0
  55. package/lib/modes/offline.js +295 -0
  56. package/lib/modes/offline.v2.js +13 -0
  57. package/lib/modes/sync.js +629 -0
  58. package/lib/scanner/apiEndpointExtractor.js +387 -0
  59. package/lib/scanner/authPatternDetector.js +54 -0
  60. package/lib/scanner/frontendScanner.js +642 -0
  61. package/lib/utils/apiClientGenerator.js +242 -0
  62. package/lib/utils/apiScanner.js +95 -0
  63. package/lib/utils/codeInjector.js +350 -0
  64. package/lib/utils/doctor.js +381 -0
  65. package/lib/utils/envGenerator.js +36 -0
  66. package/lib/utils/loadTester.js +61 -0
  67. package/lib/utils/performanceAnalyzer.js +298 -0
  68. package/lib/utils/resourceDetector.js +281 -0
  69. package/package.json +20 -0
  70. package/templates/.env.template +31 -0
  71. package/templates/advanced.model.template.js +201 -0
  72. package/templates/advanced.route.template.js +341 -0
  73. package/templates/auth.middleware.template.js +87 -0
  74. package/templates/auth.routes.template.js +238 -0
  75. package/templates/auth.user.model.template.js +78 -0
  76. package/templates/cache.middleware.js +34 -0
  77. package/templates/chat.models.template.js +260 -0
  78. package/templates/chat.routes.template.js +478 -0
  79. package/templates/compression.middleware.js +19 -0
  80. package/templates/database.config.js +74 -0
  81. package/templates/errorHandler.middleware.js +54 -0
  82. package/templates/express/controller.ejs +26 -0
  83. package/templates/express/model.ejs +9 -0
  84. package/templates/express/route.ejs +18 -0
  85. package/templates/express/server.ejs +16 -0
  86. package/templates/frontend.env.template +14 -0
  87. package/templates/model.template.js +86 -0
  88. package/templates/package.production.json +51 -0
  89. package/templates/package.template.json +41 -0
  90. package/templates/pagination.utility.js +110 -0
  91. package/templates/production.server.template.js +233 -0
  92. package/templates/rateLimiter.middleware.js +36 -0
  93. package/templates/requestLogger.middleware.js +19 -0
  94. package/templates/response.helper.js +179 -0
  95. package/templates/route.template.js +130 -0
  96. package/templates/security.middleware.js +78 -0
  97. package/templates/server.template.js +91 -0
  98. package/templates/socket.server.template.js +433 -0
  99. package/templates/utils.helper.js +157 -0
  100. package/templates/validation.middleware.js +63 -0
  101. package/templates/validation.schema.js +128 -0
  102. package/utils/fileWriter.js +15 -0
  103. package/utils/logger.js +18 -0
@@ -0,0 +1,478 @@
1
+ /**
2
+ * Chat Routes Template
3
+ * REST API endpoints for chat functionality
4
+ */
5
+
6
+ export const chatRoutesTemplate = `import express from 'express';
7
+ import Message from '../models/Message.js';
8
+ import Conversation from '../models/Conversation.js';
9
+ import { authenticateToken } from '../middleware/auth.js';
10
+
11
+ const router = express.Router();
12
+
13
+ // All routes require authentication
14
+ router.use(authenticateToken);
15
+
16
+ // ============ CONVERSATIONS ============
17
+
18
+ /**
19
+ * GET /api/chat/conversations
20
+ * Get all conversations for current user
21
+ */
22
+ router.get('/conversations', async (req, res) => {
23
+ try {
24
+ const conversations = await Conversation.findByUser(req.user.id)
25
+ .populate('participants', 'name email avatar')
26
+ .populate({
27
+ path: 'lastMessage',
28
+ populate: { path: 'sender', select: 'name email' }
29
+ })
30
+ .lean();
31
+
32
+ res.json({
33
+ success: true,
34
+ count: conversations.length,
35
+ data: conversations
36
+ });
37
+ } catch (error) {
38
+ console.error('Get conversations error:', error);
39
+ res.status(500).json({
40
+ success: false,
41
+ message: 'Failed to fetch conversations'
42
+ });
43
+ }
44
+ });
45
+
46
+ /**
47
+ * GET /api/chat/conversations/:id
48
+ * Get single conversation details
49
+ */
50
+ router.get('/conversations/:id', async (req, res) => {
51
+ try {
52
+ const conversation = await Conversation.findOne({
53
+ _id: req.params.id,
54
+ participants: req.user.id
55
+ })
56
+ .populate('participants', 'name email avatar')
57
+ .populate('admins', 'name email')
58
+ .lean();
59
+
60
+ if (!conversation) {
61
+ return res.status(404).json({
62
+ success: false,
63
+ message: 'Conversation not found'
64
+ });
65
+ }
66
+
67
+ res.json({
68
+ success: true,
69
+ data: conversation
70
+ });
71
+ } catch (error) {
72
+ console.error('Get conversation error:', error);
73
+ res.status(500).json({
74
+ success: false,
75
+ message: 'Failed to fetch conversation'
76
+ });
77
+ }
78
+ });
79
+
80
+ /**
81
+ * POST /api/chat/conversations
82
+ * Create new conversation
83
+ */
84
+ router.post('/conversations', async (req, res) => {
85
+ try {
86
+ const { participantIds, name, type = 'direct' } = req.body;
87
+
88
+ if (!participantIds || !Array.isArray(participantIds)) {
89
+ return res.status(400).json({
90
+ success: false,
91
+ message: 'participantIds array is required'
92
+ });
93
+ }
94
+
95
+ // Include current user
96
+ const participants = [req.user.id, ...participantIds];
97
+
98
+ // For direct chats, check if exists
99
+ if (type === 'direct' && participants.length === 2) {
100
+ const existing = await Conversation.findDirectConversation(
101
+ participants[0],
102
+ participants[1]
103
+ );
104
+
105
+ if (existing) {
106
+ return res.json({
107
+ success: true,
108
+ data: existing,
109
+ isExisting: true
110
+ });
111
+ }
112
+ }
113
+
114
+ // Create new conversation
115
+ const conversation = await Conversation.create({
116
+ name,
117
+ type,
118
+ participants,
119
+ createdBy: req.user.id,
120
+ admins: type === 'group' ? [req.user.id] : []
121
+ });
122
+
123
+ await conversation.populate('participants', 'name email avatar');
124
+
125
+ res.status(201).json({
126
+ success: true,
127
+ data: conversation
128
+ });
129
+ } catch (error) {
130
+ console.error('Create conversation error:', error);
131
+ res.status(500).json({
132
+ success: false,
133
+ message: 'Failed to create conversation'
134
+ });
135
+ }
136
+ });
137
+
138
+ /**
139
+ * PUT /api/chat/conversations/:id
140
+ * Update conversation (name, settings, etc.)
141
+ */
142
+ router.put('/conversations/:id', async (req, res) => {
143
+ try {
144
+ const conversation = await Conversation.findOne({
145
+ _id: req.params.id,
146
+ participants: req.user.id
147
+ });
148
+
149
+ if (!conversation) {
150
+ return res.status(404).json({
151
+ success: false,
152
+ message: 'Conversation not found'
153
+ });
154
+ }
155
+
156
+ // Only admins can update group conversations
157
+ if (conversation.type === 'group' && !conversation.isAdmin(req.user.id)) {
158
+ return res.status(403).json({
159
+ success: false,
160
+ message: 'Only admins can update group conversations'
161
+ });
162
+ }
163
+
164
+ const { name, description, avatar, settings } = req.body;
165
+
166
+ if (name) conversation.name = name;
167
+ if (description) conversation.description = description;
168
+ if (avatar) conversation.avatar = avatar;
169
+ if (settings) conversation.settings = { ...conversation.settings, ...settings };
170
+
171
+ await conversation.save();
172
+
173
+ res.json({
174
+ success: true,
175
+ data: conversation
176
+ });
177
+ } catch (error) {
178
+ console.error('Update conversation error:', error);
179
+ res.status(500).json({
180
+ success: false,
181
+ message: 'Failed to update conversation'
182
+ });
183
+ }
184
+ });
185
+
186
+ /**
187
+ * DELETE /api/chat/conversations/:id
188
+ * Delete/leave conversation
189
+ */
190
+ router.delete('/conversations/:id', async (req, res) => {
191
+ try {
192
+ const conversation = await Conversation.findOne({
193
+ _id: req.params.id,
194
+ participants: req.user.id
195
+ });
196
+
197
+ if (!conversation) {
198
+ return res.status(404).json({
199
+ success: false,
200
+ message: 'Conversation not found'
201
+ });
202
+ }
203
+
204
+ // For direct chats, just remove the user
205
+ if (conversation.type === 'direct') {
206
+ await conversation.removeParticipant(req.user.id);
207
+ } else {
208
+ // For groups, if creator, delete. Otherwise, just leave.
209
+ if (conversation.createdBy.toString() === req.user.id) {
210
+ conversation.isActive = false;
211
+ await conversation.save();
212
+ } else {
213
+ await conversation.removeParticipant(req.user.id);
214
+ }
215
+ }
216
+
217
+ res.json({
218
+ success: true,
219
+ message: 'Left conversation successfully'
220
+ });
221
+ } catch (error) {
222
+ console.error('Delete conversation error:', error);
223
+ res.status(500).json({
224
+ success: false,
225
+ message: 'Failed to delete conversation'
226
+ });
227
+ }
228
+ });
229
+
230
+ // ============ MESSAGES ============
231
+
232
+ /**
233
+ * GET /api/chat/conversations/:id/messages
234
+ * Get messages for a conversation
235
+ */
236
+ router.get('/conversations/:id/messages', async (req, res) => {
237
+ try {
238
+ const { limit = 50, before, after } = req.query;
239
+
240
+ // Verify access
241
+ const conversation = await Conversation.findOne({
242
+ _id: req.params.id,
243
+ participants: req.user.id
244
+ });
245
+
246
+ if (!conversation) {
247
+ return res.status(404).json({
248
+ success: false,
249
+ message: 'Conversation not found'
250
+ });
251
+ }
252
+
253
+ const query = {
254
+ conversation: req.params.id,
255
+ isDeleted: false
256
+ };
257
+
258
+ if (before) {
259
+ query.timestamp = { $lt: new Date(before) };
260
+ }
261
+
262
+ if (after) {
263
+ query.timestamp = { $gt: new Date(after) };
264
+ }
265
+
266
+ const messages = await Message.find(query)
267
+ .sort({ timestamp: -1 })
268
+ .limit(parseInt(limit))
269
+ .populate('sender', 'name email avatar')
270
+ .populate('replyTo', 'content sender')
271
+ .lean();
272
+
273
+ res.json({
274
+ success: true,
275
+ count: messages.length,
276
+ data: messages.reverse(),
277
+ hasMore: messages.length === parseInt(limit)
278
+ });
279
+ } catch (error) {
280
+ console.error('Get messages error:', error);
281
+ res.status(500).json({
282
+ success: false,
283
+ message: 'Failed to fetch messages'
284
+ });
285
+ }
286
+ });
287
+
288
+ /**
289
+ * POST /api/chat/conversations/:id/messages
290
+ * Send message (REST fallback - prefer Socket.io)
291
+ */
292
+ router.post('/conversations/:id/messages', async (req, res) => {
293
+ try {
294
+ const { content, type = 'text', metadata } = req.body;
295
+
296
+ if (!content) {
297
+ return res.status(400).json({
298
+ success: false,
299
+ message: 'Content is required'
300
+ });
301
+ }
302
+
303
+ // Verify access
304
+ const conversation = await Conversation.findOne({
305
+ _id: req.params.id,
306
+ participants: req.user.id
307
+ });
308
+
309
+ if (!conversation) {
310
+ return res.status(404).json({
311
+ success: false,
312
+ message: 'Conversation not found'
313
+ });
314
+ }
315
+
316
+ const message = await Message.create({
317
+ conversation: req.params.id,
318
+ sender: req.user.id,
319
+ content,
320
+ type,
321
+ metadata
322
+ });
323
+
324
+ await message.populate('sender', 'name email avatar');
325
+
326
+ // Update conversation
327
+ conversation.lastMessage = message._id;
328
+ conversation.lastMessageAt = new Date();
329
+ await conversation.save();
330
+
331
+ res.status(201).json({
332
+ success: true,
333
+ data: message
334
+ });
335
+ } catch (error) {
336
+ console.error('Send message error:', error);
337
+ res.status(500).json({
338
+ success: false,
339
+ message: 'Failed to send message'
340
+ });
341
+ }
342
+ });
343
+
344
+ /**
345
+ * PUT /api/chat/messages/:id
346
+ * Edit message
347
+ */
348
+ router.put('/messages/:id', async (req, res) => {
349
+ try {
350
+ const { content } = req.body;
351
+
352
+ const message = await Message.findOne({
353
+ _id: req.params.id,
354
+ sender: req.user.id
355
+ });
356
+
357
+ if (!message) {
358
+ return res.status(404).json({
359
+ success: false,
360
+ message: 'Message not found or not authorized'
361
+ });
362
+ }
363
+
364
+ message.content = content;
365
+ message.isEdited = true;
366
+ message.editedAt = new Date();
367
+ await message.save();
368
+
369
+ res.json({
370
+ success: true,
371
+ data: message
372
+ });
373
+ } catch (error) {
374
+ console.error('Edit message error:', error);
375
+ res.status(500).json({
376
+ success: false,
377
+ message: 'Failed to edit message'
378
+ });
379
+ }
380
+ });
381
+
382
+ /**
383
+ * DELETE /api/chat/messages/:id
384
+ * Delete message
385
+ */
386
+ router.delete('/messages/:id', async (req, res) => {
387
+ try {
388
+ const message = await Message.findOne({
389
+ _id: req.params.id,
390
+ sender: req.user.id
391
+ });
392
+
393
+ if (!message) {
394
+ return res.status(404).json({
395
+ success: false,
396
+ message: 'Message not found or not authorized'
397
+ });
398
+ }
399
+
400
+ message.isDeleted = true;
401
+ message.deletedAt = new Date();
402
+ message.content = '[Message deleted]';
403
+ await message.save();
404
+
405
+ res.json({
406
+ success: true,
407
+ message: 'Message deleted successfully'
408
+ });
409
+ } catch (error) {
410
+ console.error('Delete message error:', error);
411
+ res.status(500).json({
412
+ success: false,
413
+ message: 'Failed to delete message'
414
+ });
415
+ }
416
+ });
417
+
418
+ /**
419
+ * POST /api/chat/messages/:id/reactions
420
+ * Add reaction to message
421
+ */
422
+ router.post('/messages/:id/reactions', async (req, res) => {
423
+ try {
424
+ const { emoji } = req.body;
425
+
426
+ if (!emoji) {
427
+ return res.status(400).json({
428
+ success: false,
429
+ message: 'Emoji is required'
430
+ });
431
+ }
432
+
433
+ const message = await Message.findById(req.params.id);
434
+
435
+ if (!message) {
436
+ return res.status(404).json({
437
+ success: false,
438
+ message: 'Message not found'
439
+ });
440
+ }
441
+
442
+ // Check if user already reacted with this emoji
443
+ const existingReaction = message.reactions.find(
444
+ r => r.user.toString() === req.user.id && r.emoji === emoji
445
+ );
446
+
447
+ if (existingReaction) {
448
+ return res.status(400).json({
449
+ success: false,
450
+ message: 'Already reacted with this emoji'
451
+ });
452
+ }
453
+
454
+ message.reactions.push({
455
+ user: req.user.id,
456
+ emoji,
457
+ createdAt: new Date()
458
+ });
459
+
460
+ await message.save();
461
+
462
+ res.json({
463
+ success: true,
464
+ data: message
465
+ });
466
+ } catch (error) {
467
+ console.error('Add reaction error:', error);
468
+ res.status(500).json({
469
+ success: false,
470
+ message: 'Failed to add reaction'
471
+ });
472
+ }
473
+ });
474
+
475
+ export default router;
476
+ `;
477
+
478
+ export default chatRoutesTemplate;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Compression Middleware
3
+ * Compresses response bodies for all requests that match the filter function
4
+ */
5
+
6
+ import compression from 'compression';
7
+
8
+ export const compressionMiddleware = compression({
9
+ filter: (req, res) => {
10
+ if (req.headers['x-no-compression']) {
11
+ return false;
12
+ }
13
+ return compression.filter(req, res);
14
+ },
15
+ level: process.env.NODE_ENV === 'production' ? 6 : 3,
16
+ threshold: 1024 // Only compress responses larger than 1KB
17
+ });
18
+
19
+ export default compressionMiddleware;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Database Configuration
3
+ * Centralized database connection and configuration
4
+ */
5
+
6
+ import mongoose from 'mongoose';
7
+ import chalk from 'chalk';
8
+
9
+ const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/offbyt_app';
10
+
11
+ export const dbConfig = {
12
+ uri: MONGODB_URI,
13
+ options: {
14
+ useNewUrlParser: true,
15
+ useUnifiedTopology: true,
16
+ maxPoolSize: 10,
17
+ minPoolSize: 5,
18
+ serverSelectionTimeoutMS: 5000,
19
+ socketTimeoutMS: 45000,
20
+ retryWrites: true,
21
+ retryReads: true
22
+ }
23
+ };
24
+
25
+ // Connection flags
26
+ let isConnected = false;
27
+
28
+ export async function connectDatabase() {
29
+ if (isConnected) {
30
+ return;
31
+ }
32
+
33
+ try {
34
+ await mongoose.connect(dbConfig.uri, dbConfig.options);
35
+ isConnected = true;
36
+
37
+ mongoose.connection.on('error', (error) => {
38
+ console.error(chalk.red('❌ MongoDB Error:'), error.message);
39
+ isConnected = false;
40
+ });
41
+
42
+ mongoose.connection.on('disconnected', () => {
43
+ console.warn(chalk.yellow('⚠️ MongoDB Disconnected'));
44
+ isConnected = false;
45
+ });
46
+
47
+ console.log(chalk.green('✅ MongoDB Connected Successfully'));
48
+ return mongoose.connection;
49
+ } catch (error) {
50
+ console.error(chalk.red('❌ MongoDB Connection Failed:'), error.message);
51
+ throw error;
52
+ }
53
+ }
54
+
55
+ export async function disconnectDatabase() {
56
+ if (!isConnected) {
57
+ return;
58
+ }
59
+
60
+ try {
61
+ await mongoose.disconnect();
62
+ isConnected = false;
63
+ console.log(chalk.yellow('🔌 MongoDB Disconnected'));
64
+ } catch (error) {
65
+ console.error(chalk.red('❌ MongoDB Disconnection Error:'), error.message);
66
+ }
67
+ }
68
+
69
+ export function isDatabaseConnected() {
70
+ return isConnected && mongoose.connection.readyState === 1;
71
+ }
72
+
73
+ export default { connectDatabase, disconnectDatabase, isDatabaseConnected, dbConfig };
74
+
@@ -0,0 +1,54 @@
1
+ // Error Handler Middleware
2
+ const errorHandler = (err, req, res, next) => {
3
+ console.error('❌ Error:', err.message);
4
+
5
+ // Mongoose Validation Error
6
+ if (err.name === 'ValidationError') {
7
+ return res.status(400).json({
8
+ success: false,
9
+ error: 'Validation Error',
10
+ details: Object.values(err.errors).map(e => e.message)
11
+ });
12
+ }
13
+
14
+ // Mongoose Cast Error
15
+ if (err.name === 'CastError') {
16
+ return res.status(400).json({
17
+ success: false,
18
+ error: 'Invalid ID format'
19
+ });
20
+ }
21
+
22
+ // Duplicate Key Error
23
+ if (err.code === 11000) {
24
+ const field = Object.keys(err.keyPattern)[0];
25
+ return res.status(409).json({
26
+ success: false,
27
+ error: `${field} already exists`
28
+ });
29
+ }
30
+
31
+ // JWT Errors
32
+ if (err.name === 'JsonWebTokenError') {
33
+ return res.status(401).json({
34
+ success: false,
35
+ error: 'Invalid token'
36
+ });
37
+ }
38
+
39
+ if (err.name === 'TokenExpiredError') {
40
+ return res.status(401).json({
41
+ success: false,
42
+ error: 'Token expired'
43
+ });
44
+ }
45
+
46
+ // Default Error
47
+ res.status(err.statusCode || 500).json({
48
+ success: false,
49
+ error: err.message || 'Internal Server Error',
50
+ ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
51
+ });
52
+ };
53
+
54
+ export default errorHandler;
@@ -0,0 +1,26 @@
1
+ import <%= modelName %> from '../models/<%= modelName %>.js';
2
+
3
+ export const list<%= modelNamePlural %> = async (req, res) => {
4
+ const data = await <%= modelName %>.find();
5
+ res.json({ success: true, data });
6
+ };
7
+
8
+ export const get<%= modelName %> = async (req, res) => {
9
+ const data = await <%= modelName %>.findById(req.params.id);
10
+ res.json({ success: true, data });
11
+ };
12
+
13
+ export const create<%= modelName %> = async (req, res) => {
14
+ const data = await <%= modelName %>.create(req.body);
15
+ res.status(201).json({ success: true, data });
16
+ };
17
+
18
+ export const update<%= modelName %> = async (req, res) => {
19
+ const data = await <%= modelName %>.findByIdAndUpdate(req.params.id, req.body, { new: true });
20
+ res.json({ success: true, data });
21
+ };
22
+
23
+ export const delete<%= modelName %> = async (req, res) => {
24
+ await <%= modelName %>.findByIdAndDelete(req.params.id);
25
+ res.json({ success: true });
26
+ };
@@ -0,0 +1,9 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const <%= modelVar %>Schema = new mongoose.Schema({
4
+ <% fields.forEach(function(field) { %>
5
+ <%= field.name %>: { type: <%= field.type %><%= field.required ? ', required: true' : '' %> },
6
+ <% }); %>
7
+ }, { timestamps: true });
8
+
9
+ export default mongoose.model('<%= modelName %>', <%= modelVar %>Schema);
@@ -0,0 +1,18 @@
1
+ import express from 'express';
2
+ import {
3
+ list<%= modelNamePlural %>,
4
+ get<%= modelName %>,
5
+ create<%= modelName %>,
6
+ update<%= modelName %>,
7
+ delete<%= modelName %>
8
+ } from '../controllers/<%= resourceName %>Controller.js';
9
+
10
+ const router = express.Router();
11
+
12
+ router.get('/', list<%= modelNamePlural %>);
13
+ router.get('/:id', get<%= modelName %>);
14
+ router.post('/', create<%= modelName %>);
15
+ router.put('/:id', update<%= modelName %>);
16
+ router.delete('/:id', delete<%= modelName %>);
17
+
18
+ export default router;
@@ -0,0 +1,16 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ <% resources.forEach(function(r) { %>import <%= r.name %>Routes from './src/routes/<%= r.name %>.routes.js';
4
+ <% }); %>
5
+
6
+ const app = express();
7
+ app.use(cors());
8
+ app.use(express.json());
9
+
10
+ <% resources.forEach(function(r) { %>app.use('/api/<%= r.name %>s', <%= r.name %>Routes);
11
+ <% }); %>
12
+
13
+ app.get('/health', (req, res) => res.json({ status: 'ok' }));
14
+
15
+ const PORT = process.env.PORT || 3000;
16
+ app.listen(PORT, () => console.log(`Server running on ${PORT}`));
@@ -0,0 +1,14 @@
1
+ # ========== FRONTEND ENVIRONMENT VARIABLES ==========
2
+ # Copy this file as .env in your frontend root directory
3
+
4
+ # API Configuration
5
+ # NOTE: Should NOT include /api path - backend will handle route prefixing
6
+ VITE_API_URL=http://localhost:5000
7
+
8
+ # App Configuration
9
+ VITE_APP_NAME=YourAppName
10
+ VITE_APP_VERSION=1.0.0
11
+
12
+ # Optional: Features
13
+ VITE_ENABLE_ANALYTICS=false
14
+ VITE_ENABLE_LOGGING=true