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,787 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Offline Mode v2.0
|
|
3
|
+
* Production-Ready Backend Generation with:
|
|
4
|
+
* - Advanced CRUD operations
|
|
5
|
+
* - Pagination, filtering, sorting, search
|
|
6
|
+
* - Multiple middleware layers
|
|
7
|
+
* - Proper validation and error handling
|
|
8
|
+
* - Database configuration
|
|
9
|
+
* - Bulk operations
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { exec } from 'child_process';
|
|
16
|
+
import { promisify } from 'util';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import ora from 'ora';
|
|
19
|
+
import { scanFrontendCode, generateRoutesFromAPICalls, buildHybridIR } from '../scanner/frontendScanner.js';
|
|
20
|
+
import { detectAuthPatterns } from '../scanner/authPatternDetector.js';
|
|
21
|
+
import { extractAllApiEndpoints, getDefaultFieldsForResource } from '../scanner/apiEndpointExtractor.js';
|
|
22
|
+
import { generateAdvancedCrudModel, generateAdvancedCrudRoutes } from '../generator/advancedCrudGenerator.js';
|
|
23
|
+
import { generateAuthEnv } from '../utils/envGenerator.js';
|
|
24
|
+
import { generateSocketBackend, generateServerWithSocket, addSocketDependencies } from '../../core/generator/generateSocket.js';
|
|
25
|
+
import { socketServerTemplate } from '../../templates/socket.server.template.js';
|
|
26
|
+
import { messageModelTemplate, conversationModelTemplate } from '../../templates/chat.models.template.js';
|
|
27
|
+
import { chatRoutesTemplate } from '../../templates/chat.routes.template.js';
|
|
28
|
+
|
|
29
|
+
const execAsync = promisify(exec);
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const TEMPLATES_DIR = path.join(__dirname, '../../templates');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Main Enhanced Offline Mode Function
|
|
35
|
+
*/
|
|
36
|
+
export async function enhancedOfflineMode(projectPath) {
|
|
37
|
+
try {
|
|
38
|
+
console.log(chalk.cyan('\n🚀 offbyt - Production-Ready Backend Generation v2.0\n'));
|
|
39
|
+
console.log(chalk.gray('Mode: Enterprise Full Auto-Generation\n'));
|
|
40
|
+
|
|
41
|
+
const backendPath = path.join(projectPath, 'backend');
|
|
42
|
+
|
|
43
|
+
// ========================================================
|
|
44
|
+
// STEP 1: CREATE BACKEND STRUCTURE
|
|
45
|
+
// ========================================================
|
|
46
|
+
const step1 = ora('Step 1/9: Creating production backend structure...').start();
|
|
47
|
+
createAdvancedBackendStructure(backendPath);
|
|
48
|
+
step1.succeed('✅ Backend structure created');
|
|
49
|
+
|
|
50
|
+
// ========================================================
|
|
51
|
+
// STEP 2: SCAN FRONTEND
|
|
52
|
+
// ========================================================
|
|
53
|
+
const step2 = ora('Step 2/9: Scanning frontend for APIs...').start();
|
|
54
|
+
const apiCalls = scanFrontendCode(projectPath);
|
|
55
|
+
step2.succeed(`✅ Found ${apiCalls.length} API calls`);
|
|
56
|
+
|
|
57
|
+
// ========================================================
|
|
58
|
+
// STEP 3: ANALYZE PATTERNS
|
|
59
|
+
// ========================================================
|
|
60
|
+
const step3 = ora('Step 3/9: Analyzing architecture patterns...').start();
|
|
61
|
+
|
|
62
|
+
const srcPath = path.join(projectPath, 'src');
|
|
63
|
+
const scanRoot = fs.existsSync(srcPath) ? srcPath : projectPath;
|
|
64
|
+
const frontendContent = readAllFilesRecursive(scanRoot).join('\n');
|
|
65
|
+
|
|
66
|
+
const authPatterns = detectAuthPatterns(frontendContent, apiCalls);
|
|
67
|
+
const hasAuth = authPatterns.hasSignup || authPatterns.hasLogin || authPatterns.hasProfile;
|
|
68
|
+
|
|
69
|
+
// Build hybrid IR with socket detection
|
|
70
|
+
const ir = await buildHybridIR(projectPath);
|
|
71
|
+
const socketDetection = ir.socketDetection || { hasSocket: false, hasChat: false };
|
|
72
|
+
|
|
73
|
+
const allResources = extractAllApiEndpoints(frontendContent, apiCalls);
|
|
74
|
+
const nonAuthResources = Object.fromEntries(
|
|
75
|
+
Object.entries(allResources).filter(([key]) => key !== 'auth' && key !== 'api' && key !== 'health' && key !== 'status' && key !== 'chat')
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (hasAuth) {
|
|
79
|
+
console.log(chalk.blue(` 🔠Auth: ${authPatterns.hasSignup ? '✓Signup ' : ''}${authPatterns.hasLogin ? '✓Login ' : ''}${authPatterns.hasProfile ? '✓Profile' : ''}`));
|
|
80
|
+
}
|
|
81
|
+
if (socketDetection.hasSocket) {
|
|
82
|
+
console.log(chalk.magenta(` 💬 Real-time: ${socketDetection.socketType || 'Socket.io'}${socketDetection.hasChat ? ' + Chat' : ''}${socketDetection.rooms ? ' + Rooms' : ''}${socketDetection.presence ? ' + Presence' : ''}`));
|
|
83
|
+
}
|
|
84
|
+
if (Object.keys(nonAuthResources).length > 0) {
|
|
85
|
+
console.log(chalk.cyan(` 📊 Resources: ${Object.keys(nonAuthResources).join(', ')}`));
|
|
86
|
+
}
|
|
87
|
+
step3.succeed('✅ Architecture analyzed');
|
|
88
|
+
|
|
89
|
+
// ========================================================
|
|
90
|
+
// STEP 4: CREATE CONFIGURATIONS
|
|
91
|
+
// ========================================================
|
|
92
|
+
const step4 = ora('Step 4/9: Setting up configurations...').start();
|
|
93
|
+
|
|
94
|
+
// Copy config files
|
|
95
|
+
copyConfigFiles(backendPath);
|
|
96
|
+
|
|
97
|
+
// Copy utilities
|
|
98
|
+
copyUtilityFiles(backendPath);
|
|
99
|
+
|
|
100
|
+
step4.succeed('✅ Configuration files created');
|
|
101
|
+
|
|
102
|
+
// ========================================================
|
|
103
|
+
// STEP 5: SETUP MIDDLEWARE
|
|
104
|
+
// ========================================================
|
|
105
|
+
const step5 = ora('Step 5/9: Configuring middleware layers...').start();
|
|
106
|
+
|
|
107
|
+
copyAdvancedMiddleware(backendPath, hasAuth);
|
|
108
|
+
|
|
109
|
+
step5.succeed('✅ Middleware configured');
|
|
110
|
+
|
|
111
|
+
// ========================================================
|
|
112
|
+
// STEP 6: GENERATE MODELS & ROUTES
|
|
113
|
+
// ========================================================
|
|
114
|
+
const step6 = ora('Step 6/9: Generating CRUD models and routes...').start();
|
|
115
|
+
const generatedResources = [];
|
|
116
|
+
|
|
117
|
+
for (const [resourceName, resource] of Object.entries(nonAuthResources)) {
|
|
118
|
+
if (resourceName === 'api') continue;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const fields = resource.fields && resource.fields.length > 0
|
|
122
|
+
? Array.from(resource.fields)
|
|
123
|
+
: getDefaultFieldsForResource(resourceName);
|
|
124
|
+
|
|
125
|
+
// Generate Model
|
|
126
|
+
const modelContent = generateAdvancedCrudModel(
|
|
127
|
+
resourceName,
|
|
128
|
+
fields,
|
|
129
|
+
hasAuth,
|
|
130
|
+
resource.relations || []
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
|
|
134
|
+
const modelPath = path.join(backendPath, 'models', `${modelName}.js`);
|
|
135
|
+
const modelsDir = path.dirname(modelPath);
|
|
136
|
+
if (!fs.existsSync(modelsDir)) fs.mkdirSync(modelsDir, { recursive: true });
|
|
137
|
+
fs.writeFileSync(modelPath, modelContent);
|
|
138
|
+
|
|
139
|
+
// Generate Routes
|
|
140
|
+
const actions = resource.actions || [];
|
|
141
|
+
const routesContent = generateAdvancedCrudRoutes(
|
|
142
|
+
resourceName,
|
|
143
|
+
fields,
|
|
144
|
+
hasAuth,
|
|
145
|
+
resource.relations || [],
|
|
146
|
+
actions
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const routesPath = path.join(backendPath, 'routes', `${resourceName}.routes.js`);
|
|
150
|
+
const routesDir = path.dirname(routesPath);
|
|
151
|
+
if (!fs.existsSync(routesDir)) fs.mkdirSync(routesDir, { recursive: true });
|
|
152
|
+
fs.writeFileSync(routesPath, routesContent);
|
|
153
|
+
|
|
154
|
+
generatedResources.push(resourceName);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.warn(`âš ï¸ Failed to generate ${resourceName}: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
step6.succeed(`✅ Generated ${generatedResources.length} advanced CRUD resources`);
|
|
161
|
+
|
|
162
|
+
// ========================================================
|
|
163
|
+
// STEP 7: GENERATE AUTH SETUP (if needed)
|
|
164
|
+
// ========================================================
|
|
165
|
+
const step7 = ora('Step 7/10: Setting up authentication...').start();
|
|
166
|
+
|
|
167
|
+
if (hasAuth) {
|
|
168
|
+
generateAuthSetup(backendPath);
|
|
169
|
+
step7.succeed('✅ Authentication configured');
|
|
170
|
+
} else {
|
|
171
|
+
step7.succeed('✅ No authentication needed');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//========================================================
|
|
175
|
+
// STEP 7.5: GENERATE SOCKET.IO CHAT (if detected)
|
|
176
|
+
// ========================================================
|
|
177
|
+
let socketGenerated = false;
|
|
178
|
+
|
|
179
|
+
if (socketDetection.hasSocket || socketDetection.hasChat) {
|
|
180
|
+
const step7_5 = ora('Step 7.5/10: Generating Socket.io chat backend...').start();
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Create socket directory
|
|
184
|
+
const socketDir = path.join(backendPath, 'socket');
|
|
185
|
+
if (!fs.existsSync(socketDir)) {
|
|
186
|
+
fs.mkdirSync(socketDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Generate Socket.io server
|
|
190
|
+
fs.writeFileSync(
|
|
191
|
+
path.join(socketDir, 'index.js'),
|
|
192
|
+
socketServerTemplate
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// Generate chat models
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
path.join(backendPath, 'models', 'Message.js'),
|
|
198
|
+
messageModelTemplate
|
|
199
|
+
);
|
|
200
|
+
fs.writeFileSync(
|
|
201
|
+
path.join(backendPath, 'models', 'Conversation.js'),
|
|
202
|
+
conversationModelTemplate
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Generate chat routes
|
|
206
|
+
fs.writeFileSync(
|
|
207
|
+
path.join(backendPath, 'routes', 'chat.routes.js'),
|
|
208
|
+
chatRoutesTemplate
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
socketGenerated = true;
|
|
212
|
+
|
|
213
|
+
const features = [];
|
|
214
|
+
if (socketDetection.hasChat) features.push('Messaging');
|
|
215
|
+
if (socketDetection.rooms) features.push('Rooms');
|
|
216
|
+
if (socketDetection.presence) features.push('Presence');
|
|
217
|
+
features.push('Real-time events');
|
|
218
|
+
|
|
219
|
+
step7_5.succeed(`✅ Socket.io generated (${features.join(', ')})`);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
step7_5.fail(`⌠Failed to generate Socket.io: ${error.message}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ========================================================
|
|
226
|
+
// STEP 8: GENERATE SERVER & CONFIGURATION
|
|
227
|
+
// ========================================================
|
|
228
|
+
const step8 = ora('Step 8/10: Generating production server...').start();
|
|
229
|
+
|
|
230
|
+
generateProductionServer(backendPath, generatedResources, hasAuth, socketGenerated);
|
|
231
|
+
generateEnvFile(backendPath, hasAuth, socketGenerated);
|
|
232
|
+
generatePackageJson(backendPath, socketGenerated);
|
|
233
|
+
generateReadme(backendPath, generatedResources, hasAuth, socketGenerated);
|
|
234
|
+
|
|
235
|
+
step8.succeed('✅ Server and configuration generated');
|
|
236
|
+
|
|
237
|
+
// ========================================================
|
|
238
|
+
// STEP 9: INSTALL DEPENDENCIES
|
|
239
|
+
// ========================================================
|
|
240
|
+
const step9 = ora('Step 9/10: Installing dependencies (this may take a minute)...').start();
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
await installDependencies(backendPath);
|
|
244
|
+
step9.succeed('✅ Dependencies installed successfully');
|
|
245
|
+
} catch (error) {
|
|
246
|
+
step9.warn('âš ï¸ npm install completed with warnings (backend is ready)');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ========================================================
|
|
250
|
+
// SUCCESS
|
|
251
|
+
// ========================================================
|
|
252
|
+
console.log(chalk.green.bold('\n✨ Production Backend Generated Successfully!\n'));
|
|
253
|
+
printCompletionInfo(backendPath, hasAuth, generatedResources, socketGenerated);
|
|
254
|
+
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error(chalk.red('\n⌠Generation Failed:'), error.message);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Create Advanced Backend Directory Structure
|
|
263
|
+
*/
|
|
264
|
+
function createAdvancedBackendStructure(backendPath) {
|
|
265
|
+
const dirs = [
|
|
266
|
+
'models',
|
|
267
|
+
'routes',
|
|
268
|
+
'middleware',
|
|
269
|
+
'config',
|
|
270
|
+
'utils',
|
|
271
|
+
'controllers', // For future use
|
|
272
|
+
'validators', // For future use
|
|
273
|
+
'services' // For future use
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
for (const dir of dirs) {
|
|
277
|
+
const dirPath = path.join(backendPath, dir);
|
|
278
|
+
if (!fs.existsSync(dirPath)) {
|
|
279
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Copy Configuration Files
|
|
286
|
+
*/
|
|
287
|
+
function copyConfigFiles(backendPath) {
|
|
288
|
+
const configDir = path.join(backendPath, 'config');
|
|
289
|
+
|
|
290
|
+
// Database config
|
|
291
|
+
const dbConfig = fs.readFileSync(
|
|
292
|
+
path.join(TEMPLATES_DIR, 'database.config.js'),
|
|
293
|
+
'utf8'
|
|
294
|
+
);
|
|
295
|
+
fs.writeFileSync(path.join(configDir, 'database.js'), dbConfig);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Copy Utility Files
|
|
300
|
+
*/
|
|
301
|
+
function copyUtilityFiles(backendPath) {
|
|
302
|
+
const utilsDir = path.join(backendPath, 'utils');
|
|
303
|
+
|
|
304
|
+
// Helpers
|
|
305
|
+
const helpers = fs.readFileSync(
|
|
306
|
+
path.join(TEMPLATES_DIR, 'utils.helper.js'),
|
|
307
|
+
'utf8'
|
|
308
|
+
);
|
|
309
|
+
fs.writeFileSync(path.join(utilsDir, 'helper.js'), helpers);
|
|
310
|
+
|
|
311
|
+
// Pagination
|
|
312
|
+
const pagination = fs.readFileSync(
|
|
313
|
+
path.join(TEMPLATES_DIR, 'pagination.utility.js'),
|
|
314
|
+
'utf8'
|
|
315
|
+
);
|
|
316
|
+
fs.writeFileSync(path.join(utilsDir, 'pagination.js'), pagination);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Copy Advanced Middleware
|
|
321
|
+
*/
|
|
322
|
+
function copyAdvancedMiddleware(backendPath, hasAuth) {
|
|
323
|
+
const middlewareDir = path.join(backendPath, 'middleware');
|
|
324
|
+
|
|
325
|
+
const middlewares = [
|
|
326
|
+
'errorHandler.middleware.js',
|
|
327
|
+
'requestLogger.middleware.js',
|
|
328
|
+
'security.middleware.js',
|
|
329
|
+
'validation.schema.js',
|
|
330
|
+
'cache.middleware.js',
|
|
331
|
+
'rateLimiter.middleware.js',
|
|
332
|
+
'compression.middleware.js'
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
for (const mw of middlewares) {
|
|
336
|
+
try {
|
|
337
|
+
const source = fs.readFileSync(
|
|
338
|
+
path.join(TEMPLATES_DIR, mw),
|
|
339
|
+
'utf8'
|
|
340
|
+
);
|
|
341
|
+
const target = mw.replace('.middleware', '').replace('.schema', '');
|
|
342
|
+
fs.writeFileSync(path.join(middlewareDir, target), source);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
// File might not exist - that's okay
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Auth middleware
|
|
349
|
+
if (hasAuth) {
|
|
350
|
+
const authMiddleware = fs.readFileSync(
|
|
351
|
+
path.join(TEMPLATES_DIR, 'auth.middleware.template.js'),
|
|
352
|
+
'utf8'
|
|
353
|
+
);
|
|
354
|
+
fs.writeFileSync(path.join(middlewareDir, 'auth.js'), authMiddleware);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Generate Production Server
|
|
360
|
+
*/
|
|
361
|
+
function generateProductionServer(backendPath, resources, hasAuth, hasSocket = false) {
|
|
362
|
+
let template = fs.readFileSync(
|
|
363
|
+
path.join(TEMPLATES_DIR, 'production.server.template.js'),
|
|
364
|
+
'utf8'
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
// Generate route imports and registrations
|
|
368
|
+
let routeCode = '';
|
|
369
|
+
|
|
370
|
+
// Auth routes
|
|
371
|
+
if (hasAuth) {
|
|
372
|
+
routeCode += `import authRoutes from './routes/auth.routes.js';\n`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Chat routes (Socket.io imports will be added by generateServerWithSocket)
|
|
376
|
+
if (hasSocket) {
|
|
377
|
+
routeCode += `import chatRoutes from './routes/chat.routes.js';\n`;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Resource routes
|
|
381
|
+
for (const resource of resources) {
|
|
382
|
+
routeCode += `import ${resource}Routes from './routes/${resource}.routes.js';\n`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
routeCode += '\n// Register routes\n';
|
|
386
|
+
|
|
387
|
+
if (hasAuth) {
|
|
388
|
+
routeCode += `app.use('/api/auth', authRoutes);\n`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (hasSocket) {
|
|
392
|
+
routeCode += `app.use('/api/chat', chatRoutes);\n`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
for (const resource of resources) {
|
|
396
|
+
routeCode += `app.use('/api/${resource}', ${resource}Routes);\n`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
template = template.replace(/\/\/\s*__ROUTES__|<ROUTES>/, routeCode);
|
|
400
|
+
|
|
401
|
+
// If socket is enabled, wrap server with HTTP and Socket.io
|
|
402
|
+
if (hasSocket) {
|
|
403
|
+
template = generateServerWithSocket(template, true);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fs.writeFileSync(path.join(backendPath, 'server.js'), template);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Generate Environment File
|
|
411
|
+
*/
|
|
412
|
+
function generateEnvFile(backendPath, hasAuth, hasSocket = false) {
|
|
413
|
+
const envContent = `# ========== SERVER ==========
|
|
414
|
+
NODE_ENV=development
|
|
415
|
+
PORT=5000
|
|
416
|
+
|
|
417
|
+
# ========== DATABASE ==========
|
|
418
|
+
MONGODB_URI=mongodb://localhost:27017/offbyt_app
|
|
419
|
+
DB_NAME=offbyt_app
|
|
420
|
+
|
|
421
|
+
# ========== SECURITY ==========
|
|
422
|
+
${hasAuth || hasSocket ? `JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
|
|
423
|
+
JWT_EXPIRE=7d
|
|
424
|
+
JWT_EXPIRES_IN=7d
|
|
425
|
+
PASSWORD_SALT_ROUNDS=10` : `# JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
|
|
426
|
+
# JWT_EXPIRE=7d
|
|
427
|
+
# JWT_EXPIRES_IN=7d
|
|
428
|
+
# PASSWORD_SALT_ROUNDS=10`}
|
|
429
|
+
|
|
430
|
+
# ========== CORS ==========
|
|
431
|
+
CORS_ORIGIN=http://localhost:3000,http://localhost:5173
|
|
432
|
+
${hasSocket ? `CLIENT_URL=http://localhost:3000` : `# CLIENT_URL=http://localhost:3000`}
|
|
433
|
+
|
|
434
|
+
# ========== API ==========
|
|
435
|
+
API_VERSION=v1
|
|
436
|
+
API_RATE_LIMIT=100
|
|
437
|
+
|
|
438
|
+
# ========== LOGGING ==========
|
|
439
|
+
LOG_LEVEL=info
|
|
440
|
+
ENABLE_LOGGING=true
|
|
441
|
+
ENABLE_METRICS=false
|
|
442
|
+
|
|
443
|
+
# ========== CACHE ==========
|
|
444
|
+
CACHE_TTL=300
|
|
445
|
+
|
|
446
|
+
${hasSocket ? `# ========== SOCKET.IO ==========
|
|
447
|
+
SOCKET_PING_TIMEOUT=60000
|
|
448
|
+
SOCKET_PING_INTERVAL=25000
|
|
449
|
+
` : ''}
|
|
450
|
+
# ========== API KEYS (Optional) ==========
|
|
451
|
+
# API_KEY=your_api_key_here
|
|
452
|
+
`;
|
|
453
|
+
|
|
454
|
+
fs.writeFileSync(path.join(backendPath, '.env'), envContent);
|
|
455
|
+
fs.writeFileSync(path.join(backendPath, '.env.example'), envContent);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Generate Package.json
|
|
460
|
+
*/
|
|
461
|
+
function generatePackageJson(backendPath, hasSocket = false) {
|
|
462
|
+
const templatePath = path.join(TEMPLATES_DIR, 'package.production.json');
|
|
463
|
+
let template = fs.readFileSync(templatePath, 'utf8');
|
|
464
|
+
|
|
465
|
+
// Parse and add socket dependencies if needed
|
|
466
|
+
if (hasSocket) {
|
|
467
|
+
const packageJson = JSON.parse(template);
|
|
468
|
+
addSocketDependencies(packageJson);
|
|
469
|
+
template = JSON.stringify(packageJson, null, 2);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
fs.writeFileSync(path.join(backendPath, 'package.json'), template);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Generate README
|
|
477
|
+
*/
|
|
478
|
+
function generateReadme(backendPath, resources, hasAuth, hasSocket = false) {
|
|
479
|
+
const readmeContent = `# Backend API - Generated by offbyt v2.0
|
|
480
|
+
|
|
481
|
+
## Quick Start
|
|
482
|
+
|
|
483
|
+
\`\`\`bash
|
|
484
|
+
# Install dependencies
|
|
485
|
+
npm install
|
|
486
|
+
|
|
487
|
+
# Start development server
|
|
488
|
+
npm run dev
|
|
489
|
+
|
|
490
|
+
# Start production server
|
|
491
|
+
npm start
|
|
492
|
+
\`\`\`
|
|
493
|
+
|
|
494
|
+
## API Endpoints
|
|
495
|
+
|
|
496
|
+
### Base URL
|
|
497
|
+
\`http://localhost:5000/api\`
|
|
498
|
+
|
|
499
|
+
### Health Check
|
|
500
|
+
- \`GET /health\` - Server health status
|
|
501
|
+
|
|
502
|
+
### Resources
|
|
503
|
+
${resources.map(r => `- \`/api/${r}\` - ${r.toUpperCase()} resource endpoints`).join('\n')}
|
|
504
|
+
|
|
505
|
+
${hasAuth ? `### Authentication
|
|
506
|
+
- \`POST /api/auth/signup\` - Register new user
|
|
507
|
+
- \`POST /api/auth/login\` - Login user
|
|
508
|
+
- \`GET /api/auth/profile\` - Get user profile
|
|
509
|
+
` : ''}
|
|
510
|
+
|
|
511
|
+
${hasSocket ? `### Chat (Real-time)
|
|
512
|
+
- \`GET /api/chat/conversations\` - Get all conversations
|
|
513
|
+
- \`POST /api/chat/conversations\` - Create conversation
|
|
514
|
+
- \`GET /api/chat/conversations/:id/messages\` - Get messages
|
|
515
|
+
- \`POST /api/chat/conversations/:id/messages\` - Send message (REST fallback)
|
|
516
|
+
|
|
517
|
+
### Socket.io Events
|
|
518
|
+
**Client → Server:**
|
|
519
|
+
- \`conversation:join\` - Join a conversation room
|
|
520
|
+
- \`message:send\` - Send a message
|
|
521
|
+
- \`typing:start\` - Start typing indicator
|
|
522
|
+
- \`typing:stop\` - Stop typing indicator
|
|
523
|
+
- \`message:read\` - Mark message as read
|
|
524
|
+
|
|
525
|
+
**Server → Client:**
|
|
526
|
+
- \`message:new\` - New message received
|
|
527
|
+
- \`typing:update\` - Someone is typing
|
|
528
|
+
- \`user:online\` - User came online
|
|
529
|
+
- \`user:offline\` - User went offline
|
|
530
|
+
- \`message:sent\` - Message sent confirmation
|
|
531
|
+
|
|
532
|
+
**Socket.io Connection:**
|
|
533
|
+
\`\`\`javascript
|
|
534
|
+
import io from 'socket.io-client';
|
|
535
|
+
const socket = io(process.env.REACT_APP_API_URL, {
|
|
536
|
+
auth: { token: 'YOUR_JWT_TOKEN' }
|
|
537
|
+
});
|
|
538
|
+
\`\`\`
|
|
539
|
+
` : ''}
|
|
540
|
+
|
|
541
|
+
## Features
|
|
542
|
+
|
|
543
|
+
✅ Production-Ready Architecture
|
|
544
|
+
✅ Advanced Pagination & Filtering
|
|
545
|
+
✅ Comprehensive Error Handling
|
|
546
|
+
✅ Security Headers & Rate Limiting
|
|
547
|
+
✅ MongoDB Integration
|
|
548
|
+
✅ Mongoose Validation
|
|
549
|
+
✅ Soft Delete Support
|
|
550
|
+
✅ Activity Logging
|
|
551
|
+
✅ CORS Support
|
|
552
|
+
${hasSocket ? '✅ Real-time Socket.io Chat\n✅ Typing Indicators\n✅ Online Presence\n✅ Read Receipts\n✅ Room/Channel Support' : ''}
|
|
553
|
+
|
|
554
|
+
## Environment Variables
|
|
555
|
+
|
|
556
|
+
Copy \`.env.example\` to \`.env\` and update values:
|
|
557
|
+
|
|
558
|
+
\`\`\`
|
|
559
|
+
DATABASE_URL=mongodb://localhost:27017/app
|
|
560
|
+
PORT=5000
|
|
561
|
+
JWT_SECRET=your_secret_key
|
|
562
|
+
NODE_ENV=development
|
|
563
|
+
${hasSocket ? 'CLIENT_URL=http://localhost:3000' : ''}
|
|
564
|
+
\`\`\`
|
|
565
|
+
|
|
566
|
+
## Project Structure
|
|
567
|
+
|
|
568
|
+
\`\`\`
|
|
569
|
+
backend/
|
|
570
|
+
├── config/ # Database & app config
|
|
571
|
+
├── middleware/ # Express middleware
|
|
572
|
+
├── models/ # Mongoose models
|
|
573
|
+
├── routes/ # API routes
|
|
574
|
+
${hasSocket ? '├── socket/ # Socket.io handlers\n' : ''}├── utils/ # Helper utilities
|
|
575
|
+
├── server.js # Main server file
|
|
576
|
+
├── package.json
|
|
577
|
+
├── .env
|
|
578
|
+
└── .gitignore
|
|
579
|
+
\`\`\`
|
|
580
|
+
|
|
581
|
+
## API Response Format
|
|
582
|
+
|
|
583
|
+
All endpoints return JSON in this format:
|
|
584
|
+
|
|
585
|
+
\`\`\`json
|
|
586
|
+
{
|
|
587
|
+
"success": true,
|
|
588
|
+
"message": "Request successful",
|
|
589
|
+
"data": {},
|
|
590
|
+
"timestamp": "2024-01-01T00:00:00.000Z"
|
|
591
|
+
}
|
|
592
|
+
\`\`\`
|
|
593
|
+
|
|
594
|
+
## Database Models
|
|
595
|
+
|
|
596
|
+
${resources.map(r => {
|
|
597
|
+
const Model = r.charAt(0).toUpperCase() + r.slice(1).replace(/s$/, '');
|
|
598
|
+
return `- \`${Model}\` - Stores ${r} data`;
|
|
599
|
+
}).join('\n')}
|
|
600
|
+
|
|
601
|
+
## Security Features
|
|
602
|
+
|
|
603
|
+
- Helmet.js for security headers
|
|
604
|
+
- Rate limiting on all endpoints
|
|
605
|
+
- MongoDB injection prevention
|
|
606
|
+
- CORS protection
|
|
607
|
+
- Request validation
|
|
608
|
+
|
|
609
|
+
## Performance
|
|
610
|
+
|
|
611
|
+
- Compression middleware
|
|
612
|
+
- Database indexes
|
|
613
|
+
- Caching headers
|
|
614
|
+
- Query optimization
|
|
615
|
+
- Bulk operations support
|
|
616
|
+
|
|
617
|
+
## Error Handling
|
|
618
|
+
|
|
619
|
+
All errors are returned with appropriate HTTP status codes:
|
|
620
|
+
|
|
621
|
+
- \`400\` - Bad Request
|
|
622
|
+
- \`401\` - Unauthorized
|
|
623
|
+
- \`403\` - Forbidden
|
|
624
|
+
- \`404\` - Not Found
|
|
625
|
+
- \`409\` - Conflict (Duplicate)
|
|
626
|
+
- \`500\` - Server Error
|
|
627
|
+
|
|
628
|
+
## Development
|
|
629
|
+
|
|
630
|
+
\`\`\`bash
|
|
631
|
+
# Start with file watching
|
|
632
|
+
npm run dev
|
|
633
|
+
|
|
634
|
+
# Run tests
|
|
635
|
+
npm test
|
|
636
|
+
|
|
637
|
+
# Check linting
|
|
638
|
+
npm run lint
|
|
639
|
+
\`\`\`
|
|
640
|
+
|
|
641
|
+
## Production Deployment
|
|
642
|
+
|
|
643
|
+
1. Set \`NODE_ENV=production\`
|
|
644
|
+
2. Update database URL to production MongoDB
|
|
645
|
+
3. Set strong JWT_SECRET
|
|
646
|
+
4. Enable HTTPS in reverse proxy
|
|
647
|
+
5. Use process manager (PM2)
|
|
648
|
+
6. Set up monitoring
|
|
649
|
+
|
|
650
|
+
## Support
|
|
651
|
+
|
|
652
|
+
Generated by offbyt v2.0
|
|
653
|
+
Full-stack backend generation for modern web applications
|
|
654
|
+
`;
|
|
655
|
+
|
|
656
|
+
fs.writeFileSync(path.join(backendPath, 'README.md'), readmeContent);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Helper: Read all files recursively
|
|
661
|
+
*/
|
|
662
|
+
function readAllFilesRecursive(dirPath) {
|
|
663
|
+
const files = [];
|
|
664
|
+
|
|
665
|
+
if (!fs.existsSync(dirPath)) {
|
|
666
|
+
return files;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const items = fs.readdirSync(dirPath);
|
|
670
|
+
|
|
671
|
+
for (const item of items) {
|
|
672
|
+
const fullPath = path.join(dirPath, item);
|
|
673
|
+
const stat = fs.statSync(fullPath);
|
|
674
|
+
|
|
675
|
+
if (stat.isDirectory()) {
|
|
676
|
+
if (!item.startsWith('.') && item !== 'node_modules') {
|
|
677
|
+
files.push(...readAllFilesRecursive(fullPath));
|
|
678
|
+
}
|
|
679
|
+
} else if (
|
|
680
|
+
item.endsWith('.html') ||
|
|
681
|
+
item.endsWith('.js') ||
|
|
682
|
+
item.endsWith('.jsx') ||
|
|
683
|
+
item.endsWith('.ts') ||
|
|
684
|
+
item.endsWith('.tsx')
|
|
685
|
+
) {
|
|
686
|
+
try {
|
|
687
|
+
files.push(fs.readFileSync(fullPath, 'utf8'));
|
|
688
|
+
} catch {
|
|
689
|
+
// Ignore read errors
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return files;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Generate Auth Setup
|
|
699
|
+
*/
|
|
700
|
+
function generateAuthSetup(backendPath) {
|
|
701
|
+
const middlewareDir = path.join(backendPath, 'middleware');
|
|
702
|
+
const modelsDir = path.join(backendPath, 'models');
|
|
703
|
+
const routesDir = path.join(backendPath, 'routes');
|
|
704
|
+
|
|
705
|
+
// Auth middleware
|
|
706
|
+
try {
|
|
707
|
+
const authMiddleware = fs.readFileSync(
|
|
708
|
+
path.join(TEMPLATES_DIR, 'auth.middleware.template.js'),
|
|
709
|
+
'utf8'
|
|
710
|
+
);
|
|
711
|
+
fs.writeFileSync(path.join(middlewareDir, 'auth.js'), authMiddleware);
|
|
712
|
+
} catch (error) {
|
|
713
|
+
console.warn('âš ï¸ Could not copy auth middleware');
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// User model
|
|
717
|
+
try {
|
|
718
|
+
const userModel = fs.readFileSync(
|
|
719
|
+
path.join(TEMPLATES_DIR, 'auth.user.model.template.js'),
|
|
720
|
+
'utf8'
|
|
721
|
+
);
|
|
722
|
+
fs.writeFileSync(path.join(modelsDir, 'User.js'), userModel);
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.warn('âš ï¸ Could not copy user model');
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Auth routes
|
|
728
|
+
try {
|
|
729
|
+
const authRoutes = fs.readFileSync(
|
|
730
|
+
path.join(TEMPLATES_DIR, 'auth.routes.template.js'),
|
|
731
|
+
'utf8'
|
|
732
|
+
);
|
|
733
|
+
fs.writeFileSync(path.join(routesDir, 'auth.routes.js'), authRoutes);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.warn('âš ï¸ Could not copy auth routes');
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Install Dependencies
|
|
741
|
+
*/
|
|
742
|
+
async function installDependencies(backendPath) {
|
|
743
|
+
try {
|
|
744
|
+
await execAsync('npm install', { cwd: backendPath });
|
|
745
|
+
} catch (error) {
|
|
746
|
+
console.warn('âš ï¸ npm install had some warnings');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Print Completion Info
|
|
752
|
+
*/
|
|
753
|
+
function printCompletionInfo(backendPath, hasAuth, resources, hasSocket = false) {
|
|
754
|
+
console.log(chalk.cyan('📦 Backend Package:'));
|
|
755
|
+
console.log(chalk.gray(` Path: ${backendPath}`));
|
|
756
|
+
console.log(chalk.gray(` Resources: ${resources.length}`));
|
|
757
|
+
console.log(chalk.gray(` Auth: ${hasAuth ? '✓ Enabled' : '✗ Disabled'}`));
|
|
758
|
+
console.log(chalk.gray(` Socket.io: ${hasSocket ? '✓ Enabled (Chat)' : '✗ Disabled'}\n`));
|
|
759
|
+
|
|
760
|
+
console.log(chalk.cyan('🚀 Next Steps:'));
|
|
761
|
+
console.log(chalk.white(` 1. cd backend`));
|
|
762
|
+
console.log(chalk.white(` 2. npm run dev # Start development server`));
|
|
763
|
+
console.log(chalk.white(` 3. Open http://localhost:5000/health\n`));
|
|
764
|
+
|
|
765
|
+
if (hasSocket) {
|
|
766
|
+
console.log(chalk.magenta('💬 Socket.io Endpoints:'));
|
|
767
|
+
console.log(chalk.gray(` • ws://localhost:5000 # Socket.io connection`));
|
|
768
|
+
console.log(chalk.gray(` • GET /api/chat/conversations`));
|
|
769
|
+
console.log(chalk.gray(` • GET /api/chat/conversations/:id/messages`));
|
|
770
|
+
console.log(chalk.gray(` • POST /api/chat/conversations\n`));
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
console.log(chalk.cyan('📚 API Resources:'));
|
|
774
|
+
resources.forEach(r => {
|
|
775
|
+
console.log(chalk.gray(` • GET /api/${r} # List all`));
|
|
776
|
+
console.log(chalk.gray(` • GET /api/${r}/:id # Get by ID`));
|
|
777
|
+
console.log(chalk.gray(` • POST /api/${r} # Create`));
|
|
778
|
+
console.log(chalk.gray(` • PUT /api/${r}/:id # Update`));
|
|
779
|
+
console.log(chalk.gray(` • DELETE /api/${r}/:id # Delete`));
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
console.log(chalk.green('\n✨ Your production-ready backend is ready!\n'));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
export { enhancedOfflineMode as offlineMode };
|
|
786
|
+
export default enhancedOfflineMode;
|
|
787
|
+
|