offbyt 1.0.0 → 1.0.2
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/cli.js +117 -2
- package/industry-mode.json +22 -0
- package/lib/ir-integration.js +4 -4
- package/lib/modes/configBasedGenerator.js +27 -27
- package/lib/modes/connect.js +26 -25
- package/lib/modes/generateApi.js +217 -76
- package/lib/modes/interactiveSetup.js +28 -29
- package/lib/utils/cliFormatter.js +131 -0
- package/lib/utils/codeInjector.js +144 -26
- package/lib/utils/industryMode.js +419 -0
- package/lib/utils/postGenerationVerifier.js +256 -0
- package/lib/utils/resourceDetector.js +234 -169
- package/package.json +1 -1
package/lib/modes/generateApi.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* Smart API Generation Mode
|
|
3
3
|
* Detects resources from frontend patterns and generates full-stack APIs
|
|
4
4
|
*/
|
|
@@ -13,6 +13,7 @@ import { generateApiClients } from '../utils/apiClientGenerator.js';
|
|
|
13
13
|
import { injectApiCalls } from '../utils/codeInjector.js';
|
|
14
14
|
import { generateAdvancedCrudModel, generateAdvancedCrudRoutes } from '../generator/advancedCrudGenerator.js';
|
|
15
15
|
import { generateSQLFiles } from './configBasedGenerator.js';
|
|
16
|
+
import { printSection, printStep, printSuccess, printBox, printSummary, printFooter } from '../utils/cliFormatter.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Main Smart API Generation Function
|
|
@@ -22,10 +23,10 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
22
23
|
const config = options.config || { database: 'mongodb' };
|
|
23
24
|
const isSQL = ['postgresql', 'mysql', 'sqlite'].includes(config.database);
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
console.log(chalk.gray('
|
|
26
|
+
printSection('Smart API Generation Engine');
|
|
27
|
+
console.log(chalk.gray('Analyzing frontend & generating full-stack APIs...\n'));
|
|
27
28
|
if (isSQL) {
|
|
28
|
-
|
|
29
|
+
printSuccess(`Using ${config.database.toUpperCase()} database`);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const backendPath = path.join(projectPath, 'backend');
|
|
@@ -33,7 +34,8 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
33
34
|
// ========================================================
|
|
34
35
|
// STEP 1: DETECT RESOURCES FROM FRONTEND
|
|
35
36
|
// ========================================================
|
|
36
|
-
|
|
37
|
+
printStep(1, 6, 'Scanning Frontend for Data Patterns');
|
|
38
|
+
const step1 = ora('Analyzing React state and components...').start();
|
|
37
39
|
const resources = detectResourcesFromFrontend(projectPath);
|
|
38
40
|
|
|
39
41
|
if (resources.length === 0) {
|
|
@@ -44,35 +46,31 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
44
46
|
return;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
step1.succeed(chalk.green(
|
|
49
|
+
step1.succeed(chalk.green('Found resources to generate APIs for'));
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log(chalk.white(` • ${resource.name}`) + chalk.gray(` (${resource.singular})`));
|
|
52
|
-
if (resource.fields.length > 0) {
|
|
53
|
-
console.log(chalk.gray(` Fields: ${resource.fields.join(', ')}`));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
console.log('');
|
|
51
|
+
const resourcesList = resources.map(r => `${r.name} (${r.fields.join(', ')})`);
|
|
52
|
+
printBox('Detected Resources', resourcesList);
|
|
57
53
|
|
|
58
54
|
// ========================================================
|
|
59
55
|
// STEP 2: CREATE BACKEND STRUCTURE
|
|
60
56
|
// ========================================================
|
|
61
|
-
|
|
57
|
+
printStep(2, 6, 'Creating Backend Structure');
|
|
58
|
+
const step2 = ora('Generating folder structure and middleware...').start();
|
|
62
59
|
|
|
63
60
|
const backendExists = fs.existsSync(backendPath);
|
|
64
61
|
createBackendStructure(backendPath, projectPath);
|
|
65
62
|
ensureValidationMiddleware(backendPath);
|
|
66
63
|
if (backendExists) {
|
|
67
|
-
step2.succeed('
|
|
64
|
+
step2.succeed('Backend structure verified and utility files ensured');
|
|
68
65
|
} else {
|
|
69
|
-
step2.succeed('
|
|
66
|
+
step2.succeed('Backend structure created successfully');
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
// ========================================================
|
|
73
70
|
// STEP 3: GENERATE BACKEND MODELS
|
|
74
71
|
// ========================================================
|
|
75
|
-
|
|
72
|
+
printStep(3, 6, 'Generating Backend Models');
|
|
73
|
+
const step3 = ora('Creating model files for each resource...').start();
|
|
76
74
|
const modelsDir = path.join(backendPath, 'models');
|
|
77
75
|
let modelCount = 0;
|
|
78
76
|
|
|
@@ -82,23 +80,26 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
82
80
|
|
|
83
81
|
// Only create if doesn't exist
|
|
84
82
|
if (!fs.existsSync(modelFile)) {
|
|
85
|
-
const modelCode =
|
|
86
|
-
resource
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
const modelCode = isSQL
|
|
84
|
+
? generateSqlCrudModel(resource)
|
|
85
|
+
: generateAdvancedCrudModel(
|
|
86
|
+
resource.name,
|
|
87
|
+
resource.fields,
|
|
88
|
+
false,
|
|
89
|
+
[]
|
|
90
|
+
);
|
|
91
91
|
fs.writeFileSync(modelFile, modelCode, 'utf8');
|
|
92
92
|
modelCount++;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
step3.succeed(
|
|
96
|
+
step3.succeed(`Generated ${modelCount} models`);
|
|
97
97
|
|
|
98
98
|
// ========================================================
|
|
99
99
|
// STEP 4: GENERATE BACKEND ROUTES
|
|
100
100
|
// ========================================================
|
|
101
|
-
|
|
101
|
+
printStep(4, 6, 'Generating Backend Routes');
|
|
102
|
+
const step4 = ora('Creating route files for each resource...').start();
|
|
102
103
|
const routesDir = path.join(backendPath, 'routes');
|
|
103
104
|
let routeCount = 0;
|
|
104
105
|
|
|
@@ -107,25 +108,28 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
107
108
|
|
|
108
109
|
// Only create if doesn't exist
|
|
109
110
|
if (!fs.existsSync(routeFile)) {
|
|
110
|
-
const routeCode =
|
|
111
|
-
resource
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
const routeCode = isSQL
|
|
112
|
+
? generateSqlCrudRoutes(resource)
|
|
113
|
+
: generateAdvancedCrudRoutes(
|
|
114
|
+
resource.name,
|
|
115
|
+
resource.fields,
|
|
116
|
+
false,
|
|
117
|
+
[],
|
|
118
|
+
[]
|
|
119
|
+
);
|
|
117
120
|
fs.writeFileSync(routeFile, routeCode, 'utf8');
|
|
118
121
|
routeCount++;
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
step4.succeed(
|
|
125
|
+
step4.succeed(`Generated ${routeCount} route files`);
|
|
123
126
|
|
|
124
127
|
// ========================================================
|
|
125
128
|
// STEP 4.5: GENERATE SQL FILES (for SQL databases only)
|
|
126
129
|
// ========================================================
|
|
127
130
|
if (isSQL && resources.length > 0) {
|
|
128
|
-
|
|
131
|
+
printStep('4.5', 6, 'Generating SQL Files');
|
|
132
|
+
const sqlStep = ora('Creating SQL schema and seed files...').start();
|
|
129
133
|
try {
|
|
130
134
|
// Convert resources array to Map format expected by generateSQLFiles
|
|
131
135
|
const resourcesMap = new Map();
|
|
@@ -137,11 +141,9 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
generateSQLFiles(backendPath, resourcesMap, config, { relationships: [] });
|
|
140
|
-
sqlStep.succeed(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
console.log(chalk.gray(' • 03_relationships_joins.sql'));
|
|
144
|
-
console.log(chalk.gray(' • 04_seed_data.sql\n'));
|
|
144
|
+
sqlStep.succeed('SQL files generated in backend/sql/');
|
|
145
|
+
const sqlFiles = ['01_schema.sql', '02_crud_operations.sql', '03_relationships_joins.sql', '04_seed_data.sql'];
|
|
146
|
+
printBox('Created SQL Files', sqlFiles);
|
|
145
147
|
} catch (error) {
|
|
146
148
|
sqlStep.fail(`Failed to generate SQL files: ${error.message}`);
|
|
147
149
|
console.error(chalk.red(error.stack));
|
|
@@ -151,17 +153,16 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
151
153
|
// ========================================================
|
|
152
154
|
// STEP 5: GENERATE FRONTEND API CLIENTS
|
|
153
155
|
// ========================================================
|
|
154
|
-
|
|
156
|
+
printStep(5, 6, 'Generating Frontend API Clients');
|
|
157
|
+
const step5 = ora('Creating API client modules...').start();
|
|
155
158
|
|
|
156
159
|
try {
|
|
157
160
|
const generatedClients = generateApiClients(projectPath, resources);
|
|
158
|
-
step5.succeed(
|
|
161
|
+
step5.succeed(`Generated ${generatedClients.length} API client files`);
|
|
159
162
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log(chalk.gray(` • ${file}`));
|
|
163
|
+
if (generatedClients.length > 0) {
|
|
164
|
+
printBox('Created API Clients', generatedClients.slice(0, 8));
|
|
163
165
|
}
|
|
164
|
-
console.log('');
|
|
165
166
|
} catch (error) {
|
|
166
167
|
step5.fail(`Failed to generate API clients: ${error.message}`);
|
|
167
168
|
}
|
|
@@ -170,24 +171,20 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
170
171
|
// STEP 6: INJECT API CALLS IN FRONTEND (Optional)
|
|
171
172
|
// ========================================================
|
|
172
173
|
if (options.inject !== false) {
|
|
173
|
-
|
|
174
|
+
printStep(6, 6, 'Injecting API Calls in Frontend');
|
|
175
|
+
const step6 = ora('Connecting frontend components to APIs...').start();
|
|
174
176
|
|
|
175
177
|
try {
|
|
176
|
-
const injectedFiles = injectApiCalls(projectPath, resources
|
|
178
|
+
const injectedFiles = injectApiCalls(projectPath, resources, {
|
|
179
|
+
idField: isSQL ? 'id' : '_id'
|
|
180
|
+
});
|
|
177
181
|
|
|
178
182
|
if (injectedFiles.length > 0) {
|
|
179
|
-
step6.succeed(
|
|
183
|
+
step6.succeed(`Injected API calls in ${injectedFiles.length} files`);
|
|
180
184
|
|
|
181
|
-
|
|
182
|
-
for (const file of injectedFiles.slice(0, 10)) {
|
|
183
|
-
console.log(chalk.gray(` • ${file}`));
|
|
184
|
-
}
|
|
185
|
-
if (injectedFiles.length > 10) {
|
|
186
|
-
console.log(chalk.gray(` ... and ${injectedFiles.length - 10} more`));
|
|
187
|
-
}
|
|
188
|
-
console.log('');
|
|
185
|
+
printBox('Modified Components', injectedFiles.slice(0, 8));
|
|
189
186
|
} else {
|
|
190
|
-
step6.succeed('
|
|
187
|
+
step6.succeed('No injection needed (files already up-to-date)');
|
|
191
188
|
}
|
|
192
189
|
} catch (error) {
|
|
193
190
|
step6.warn(`Skipped injection: ${error.message}`);
|
|
@@ -209,27 +206,23 @@ export async function generateSmartAPI(projectPath, options = {}) {
|
|
|
209
206
|
// ========================================================
|
|
210
207
|
// SUMMARY
|
|
211
208
|
// ========================================================
|
|
212
|
-
|
|
209
|
+
printSummary('Smart API Generation Complete!', [
|
|
210
|
+
`Generated ${resources.length} resources with full CRUD APIs`,
|
|
211
|
+
`${isSQL ? `Using ${config.database.toUpperCase()} database` : 'MongoDB collections ready'}`,
|
|
212
|
+
`Frontend components connected and injected with API calls`
|
|
213
|
+
]);
|
|
213
214
|
|
|
214
|
-
|
|
215
|
+
const apiEndpoints = [];
|
|
215
216
|
for (const resource of resources) {
|
|
216
|
-
|
|
217
|
-
console.log(chalk.gray(` GET /api/${resource.name}`));
|
|
218
|
-
console.log(chalk.gray(` GET /api/${resource.name}/:id`));
|
|
219
|
-
console.log(chalk.gray(` POST /api/${resource.name}`));
|
|
220
|
-
console.log(chalk.gray(` PUT /api/${resource.name}/:id`));
|
|
221
|
-
console.log(chalk.gray(` DELETE /api/${resource.name}/:id`));
|
|
217
|
+
apiEndpoints.push(`${resource.name}: GET, POST, PUT, DELETE /api/${resource.name}`);
|
|
222
218
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
console.log(chalk.gray(' • Start backend: cd backend && npm run dev'));
|
|
231
|
-
console.log(chalk.gray(' • Start frontend: npm start'));
|
|
232
|
-
console.log(chalk.green('\n Your frontend and backend will be fully connected! 🎉\n'));
|
|
219
|
+
printBox('Generated API Endpoints', apiEndpoints);
|
|
220
|
+
|
|
221
|
+
printFooter([
|
|
222
|
+
'cd backend && npm install',
|
|
223
|
+
'npm run dev (or npm run start)',
|
|
224
|
+
'Frontend will auto-connect to API at http://localhost:5000'
|
|
225
|
+
]);
|
|
233
226
|
}
|
|
234
227
|
|
|
235
228
|
/**
|
|
@@ -431,5 +424,153 @@ function capitalize(str) {
|
|
|
431
424
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
432
425
|
}
|
|
433
426
|
|
|
427
|
+
function generateSqlCrudModel(resource) {
|
|
428
|
+
const modelName = capitalize(resource.singular);
|
|
429
|
+
const fieldLines = (resource.fields || [])
|
|
430
|
+
.filter((field) => field && field !== 'id' && field !== '_id')
|
|
431
|
+
.map((field) => {
|
|
432
|
+
const lower = field.toLowerCase();
|
|
433
|
+
if (lower.includes('amount') || lower.includes('price') || lower.includes('cost') || lower.includes('total')) {
|
|
434
|
+
return ` ${field}: { type: DataTypes.DECIMAL(10, 2), allowNull: true },`;
|
|
435
|
+
}
|
|
436
|
+
if (lower.includes('count') || lower.includes('quantity')) {
|
|
437
|
+
return ` ${field}: { type: DataTypes.INTEGER, allowNull: true },`;
|
|
438
|
+
}
|
|
439
|
+
if (lower.includes('is') || lower.startsWith('has')) {
|
|
440
|
+
return ` ${field}: { type: DataTypes.BOOLEAN, allowNull: true },`;
|
|
441
|
+
}
|
|
442
|
+
return ` ${field}: { type: DataTypes.STRING, allowNull: true },`;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const fields = fieldLines.length > 0
|
|
446
|
+
? fieldLines.join('\n')
|
|
447
|
+
: ' name: { type: DataTypes.STRING, allowNull: true },';
|
|
448
|
+
|
|
449
|
+
return `import { DataTypes } from 'sequelize';
|
|
450
|
+
import { sequelize } from '../config/database.js';
|
|
451
|
+
|
|
452
|
+
const ${modelName} = sequelize.define('${modelName}', {
|
|
453
|
+
${fields}
|
|
454
|
+
}, {
|
|
455
|
+
tableName: '${resource.name}',
|
|
456
|
+
timestamps: true
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
export default ${modelName};
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function generateSqlCrudRoutes(resource) {
|
|
464
|
+
const modelName = capitalize(resource.singular);
|
|
465
|
+
|
|
466
|
+
return `import express from 'express';
|
|
467
|
+
import { query, body, param } from 'express-validator';
|
|
468
|
+
import { validateErrors } from '../middleware/validation.js';
|
|
469
|
+
import { ResponseHelper } from '../utils/helper.js';
|
|
470
|
+
import ${modelName} from '../models/${modelName}.js';
|
|
471
|
+
|
|
472
|
+
const router = express.Router();
|
|
473
|
+
|
|
474
|
+
router.get(
|
|
475
|
+
'/',
|
|
476
|
+
[
|
|
477
|
+
query('page').optional().isInt({ min: 1 }).toInt(),
|
|
478
|
+
query('limit').optional().isInt({ min: 1, max: 100 }).toInt(),
|
|
479
|
+
query('search').optional().isString().trim(),
|
|
480
|
+
validateErrors
|
|
481
|
+
],
|
|
482
|
+
async (req, res, next) => {
|
|
483
|
+
try {
|
|
484
|
+
const page = Number.parseInt(req.query.page || '1', 10);
|
|
485
|
+
const limit = Number.parseInt(req.query.limit || '10', 10);
|
|
486
|
+
const offset = (page - 1) * limit;
|
|
487
|
+
|
|
488
|
+
let where = undefined;
|
|
489
|
+
if (req.query.search) {
|
|
490
|
+
const search = req.query.search;
|
|
491
|
+
where = {};
|
|
492
|
+
for (const key of Object.keys(${modelName}.rawAttributes)) {
|
|
493
|
+
if (['id', 'createdAt', 'updatedAt'].includes(key)) continue;
|
|
494
|
+
where[key] = search;
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const result = await ${modelName}.findAndCountAll({ where, limit, offset, order: [['createdAt', 'DESC']] });
|
|
500
|
+
return ResponseHelper.paginated(
|
|
501
|
+
res,
|
|
502
|
+
result.rows,
|
|
503
|
+
{
|
|
504
|
+
page,
|
|
505
|
+
limit,
|
|
506
|
+
total: result.count,
|
|
507
|
+
pages: Math.ceil(result.count / limit),
|
|
508
|
+
hasMore: page * limit < result.count,
|
|
509
|
+
skip: offset
|
|
510
|
+
},
|
|
511
|
+
'${resource.name} loaded successfully'
|
|
512
|
+
);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
next(error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
router.get('/:id', [param('id').isInt({ min: 1 }), validateErrors], async (req, res, next) => {
|
|
520
|
+
try {
|
|
521
|
+
const item = await ${modelName}.findByPk(req.params.id);
|
|
522
|
+
if (!item) return ResponseHelper.notFound(res, '${modelName}');
|
|
523
|
+
return ResponseHelper.success(res, item, 'Item retrieved successfully');
|
|
524
|
+
} catch (error) {
|
|
525
|
+
next(error);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
router.post('/', [body().isObject(), validateErrors], async (req, res, next) => {
|
|
530
|
+
try {
|
|
531
|
+
const created = await ${modelName}.create(req.body);
|
|
532
|
+
return ResponseHelper.success(res, created, '${modelName} created successfully', 201);
|
|
533
|
+
} catch (error) {
|
|
534
|
+
next(error);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
router.put('/:id', [param('id').isInt({ min: 1 }), body().isObject(), validateErrors], async (req, res, next) => {
|
|
539
|
+
try {
|
|
540
|
+
const item = await ${modelName}.findByPk(req.params.id);
|
|
541
|
+
if (!item) return ResponseHelper.notFound(res, '${modelName}');
|
|
542
|
+
await item.update(req.body);
|
|
543
|
+
return ResponseHelper.success(res, item, '${modelName} updated successfully');
|
|
544
|
+
} catch (error) {
|
|
545
|
+
next(error);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
router.patch('/:id', [param('id').isInt({ min: 1 }), body().isObject(), validateErrors], async (req, res, next) => {
|
|
550
|
+
try {
|
|
551
|
+
const item = await ${modelName}.findByPk(req.params.id);
|
|
552
|
+
if (!item) return ResponseHelper.notFound(res, '${modelName}');
|
|
553
|
+
await item.update(req.body);
|
|
554
|
+
return ResponseHelper.success(res, item, 'Partial update successful');
|
|
555
|
+
} catch (error) {
|
|
556
|
+
next(error);
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
router.delete('/:id', [param('id').isInt({ min: 1 }), validateErrors], async (req, res, next) => {
|
|
561
|
+
try {
|
|
562
|
+
const item = await ${modelName}.findByPk(req.params.id);
|
|
563
|
+
if (!item) return ResponseHelper.notFound(res, '${modelName}');
|
|
564
|
+
await item.destroy();
|
|
565
|
+
return ResponseHelper.success(res, { id: Number.parseInt(req.params.id, 10) }, '${modelName} deleted successfully');
|
|
566
|
+
} catch (error) {
|
|
567
|
+
next(error);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
export default router;
|
|
572
|
+
`;
|
|
573
|
+
}
|
|
574
|
+
|
|
434
575
|
export default { generateSmartAPI };
|
|
435
576
|
|
|
@@ -7,15 +7,15 @@ import inquirer from 'inquirer';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
|
|
9
9
|
export async function getInteractiveSetup() {
|
|
10
|
-
console.log(chalk.cyan('\n
|
|
11
|
-
console.log(chalk.cyan('
|
|
12
|
-
console.log(chalk.cyan('
|
|
10
|
+
console.log(chalk.cyan('\nâ•”â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•—'));
|
|
11
|
+
console.log(chalk.cyan('â•‘ offbyt - Interactive Setup â•‘'));
|
|
12
|
+
console.log(chalk.cyan('╚â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•\n'));
|
|
13
13
|
|
|
14
14
|
const answers = await inquirer.prompt([
|
|
15
15
|
{
|
|
16
16
|
type: 'list',
|
|
17
17
|
name: 'database',
|
|
18
|
-
message: '
|
|
18
|
+
message: '📦 Select Database:',
|
|
19
19
|
choices: [
|
|
20
20
|
{ name: 'MongoDB (Mongoose)', value: 'mongodb' },
|
|
21
21
|
{ name: 'PostgreSQL (Sequelize)', value: 'postgresql' },
|
|
@@ -27,7 +27,7 @@ export async function getInteractiveSetup() {
|
|
|
27
27
|
{
|
|
28
28
|
type: 'list',
|
|
29
29
|
name: 'framework',
|
|
30
|
-
message: '
|
|
30
|
+
message: 'âš™ï¸ Select Backend Framework:',
|
|
31
31
|
choices: [
|
|
32
32
|
{ name: 'Express.js (Recommended)', value: 'express' },
|
|
33
33
|
{ name: 'Fastify (High Performance)', value: 'fastify' },
|
|
@@ -38,19 +38,19 @@ export async function getInteractiveSetup() {
|
|
|
38
38
|
{
|
|
39
39
|
type: 'confirm',
|
|
40
40
|
name: 'enableSocket',
|
|
41
|
-
message: '
|
|
41
|
+
message: '🔌 Enable Realtime Sockets?',
|
|
42
42
|
default: true
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
type: 'confirm',
|
|
46
46
|
name: 'enableAuth',
|
|
47
|
-
message: '
|
|
47
|
+
message: '🔠Generate Authentication System?',
|
|
48
48
|
default: true
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
type: 'list',
|
|
52
52
|
name: 'authType',
|
|
53
|
-
message: '
|
|
53
|
+
message: '🔑 Select Authentication Type:',
|
|
54
54
|
choices: [
|
|
55
55
|
{ name: 'JWT (JSON Web Token)', value: 'jwt' },
|
|
56
56
|
{ name: 'OAuth 2.0', value: 'oauth' },
|
|
@@ -62,19 +62,19 @@ export async function getInteractiveSetup() {
|
|
|
62
62
|
{
|
|
63
63
|
type: 'confirm',
|
|
64
64
|
name: 'enableValidation',
|
|
65
|
-
message: '
|
|
65
|
+
message: '✅ Enable Request Validation (Joi)?',
|
|
66
66
|
default: true
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
type: 'confirm',
|
|
70
70
|
name: 'enableCaching',
|
|
71
|
-
message: '
|
|
71
|
+
message: 'âš¡ Enable Redis Caching?',
|
|
72
72
|
default: false
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
type: 'confirm',
|
|
76
76
|
name: 'enableLogging',
|
|
77
|
-
message: '
|
|
77
|
+
message: '📊 Enable Advanced Logging?',
|
|
78
78
|
default: true
|
|
79
79
|
}
|
|
80
80
|
]);
|
|
@@ -86,14 +86,14 @@ export async function getInteractiveSetup() {
|
|
|
86
86
|
* Display configuration summary
|
|
87
87
|
*/
|
|
88
88
|
export function displaySetupSummary(config) {
|
|
89
|
-
console.log(chalk.cyan('\n
|
|
90
|
-
console.log(chalk.green(`
|
|
91
|
-
console.log(chalk.green(`
|
|
92
|
-
console.log(chalk.green(`
|
|
93
|
-
console.log(chalk.green(`
|
|
94
|
-
console.log(chalk.green(`
|
|
95
|
-
console.log(chalk.green(`
|
|
96
|
-
console.log(chalk.green(`
|
|
89
|
+
console.log(chalk.cyan('\n✨ Configuration Summary:\n'));
|
|
90
|
+
console.log(chalk.green(` ✔ Database → ${formatDatabase(config.database)}`));
|
|
91
|
+
console.log(chalk.green(` ✔ Framework → ${formatFramework(config.framework)}`));
|
|
92
|
+
console.log(chalk.green(` ✔ Realtime Socket → ${config.enableSocket ? 'Enabled' : 'Disabled'}`));
|
|
93
|
+
console.log(chalk.green(` ✔ Authentication → ${config.enableAuth ? `Enabled (${formatAuth(config.authType)})` : 'Disabled'}`));
|
|
94
|
+
console.log(chalk.green(` ✔ Validation → ${config.enableValidation ? 'Enabled' : 'Disabled'}`));
|
|
95
|
+
console.log(chalk.green(` ✔ Caching → ${config.enableCaching ? 'Enabled (Redis)' : 'Disabled'}`));
|
|
96
|
+
console.log(chalk.green(` ✔ Logging → ${config.enableLogging ? 'Enabled' : 'Disabled'}`));
|
|
97
97
|
console.log(chalk.cyan('\n'));
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -257,9 +257,9 @@ export async function connectDatabase() {
|
|
|
257
257
|
useUnifiedTopology: true,
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
-
console.log('
|
|
260
|
+
console.log('[ok] MongoDB connected');
|
|
261
261
|
} catch (error) {
|
|
262
|
-
console.error('
|
|
262
|
+
console.error('[error] MongoDB connection error:', error.message);
|
|
263
263
|
process.exit(1);
|
|
264
264
|
}
|
|
265
265
|
}
|
|
@@ -283,10 +283,10 @@ export const sequelize = new Sequelize(
|
|
|
283
283
|
export async function connectDatabase() {
|
|
284
284
|
try {
|
|
285
285
|
await sequelize.authenticate();
|
|
286
|
-
console.log('
|
|
286
|
+
console.log('[ok] PostgreSQL connected');
|
|
287
287
|
await sequelize.sync({ alter: true });
|
|
288
288
|
} catch (error) {
|
|
289
|
-
console.error('
|
|
289
|
+
console.error('[error] PostgreSQL connection error:', error.message);
|
|
290
290
|
process.exit(1);
|
|
291
291
|
}
|
|
292
292
|
}
|
|
@@ -301,7 +301,7 @@ export const sequelize = new Sequelize(
|
|
|
301
301
|
process.env.DB_PASSWORD || 'password',
|
|
302
302
|
{
|
|
303
303
|
host: process.env.DB_HOST || 'localhost',
|
|
304
|
-
port: process.env.DB_PORT || 3306,
|
|
304
|
+
port: Number.parseInt(process.env.DB_PORT, 10) || 3306,
|
|
305
305
|
dialect: 'mysql',
|
|
306
306
|
logging: false,
|
|
307
307
|
}
|
|
@@ -310,10 +310,10 @@ export const sequelize = new Sequelize(
|
|
|
310
310
|
export async function connectDatabase() {
|
|
311
311
|
try {
|
|
312
312
|
await sequelize.authenticate();
|
|
313
|
-
console.log('
|
|
313
|
+
console.log('[ok] MySQL connected');
|
|
314
314
|
await sequelize.sync({ alter: true });
|
|
315
315
|
} catch (error) {
|
|
316
|
-
console.error('
|
|
316
|
+
console.error('[error] MySQL connection error:', error.message);
|
|
317
317
|
process.exit(1);
|
|
318
318
|
}
|
|
319
319
|
}
|
|
@@ -332,10 +332,10 @@ export const sequelize = new Sequelize({
|
|
|
332
332
|
export async function connectDatabase() {
|
|
333
333
|
try {
|
|
334
334
|
await sequelize.authenticate();
|
|
335
|
-
console.log('
|
|
335
|
+
console.log('[ok] SQLite connected');
|
|
336
336
|
await sequelize.sync({ alter: true });
|
|
337
337
|
} catch (error) {
|
|
338
|
-
console.error('
|
|
338
|
+
console.error('[error] SQLite connection error:', error.message);
|
|
339
339
|
process.exit(1);
|
|
340
340
|
}
|
|
341
341
|
}
|
|
@@ -545,4 +545,3 @@ LOG_ERROR_FILE=./logs/error.log
|
|
|
545
545
|
return env;
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
|