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.
@@ -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
- console.log(chalk.cyan('\n🎯 offbyt Smart API Generation\n'));
26
- console.log(chalk.gray('Detecting resources from your frontend...\n'));
26
+ printSection('Smart API Generation Engine');
27
+ console.log(chalk.gray('Analyzing frontend & generating full-stack APIs...\n'));
27
28
  if (isSQL) {
28
- console.log(chalk.blue(`ðŸ"¦ Using ${config.database.toUpperCase()} database\n`));
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
- const step1 = ora('Step 1/6: Scanning frontend for data patterns...').start();
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(`✅ Detected ${resources.length} resources`));
49
+ step1.succeed(chalk.green('Found resources to generate APIs for'));
48
50
 
49
- console.log(chalk.cyan('\n📦 Detected Resources:\n'));
50
- for (const resource of resources) {
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
- const step2 = ora('Step 2/6: Creating backend structure...').start();
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('✅ Backend structure exists and required utility files ensured');
64
+ step2.succeed('Backend structure verified and utility files ensured');
68
65
  } else {
69
- step2.succeed('✅ Backend structure created');
66
+ step2.succeed('Backend structure created successfully');
70
67
  }
71
68
 
72
69
  // ========================================================
73
70
  // STEP 3: GENERATE BACKEND MODELS
74
71
  // ========================================================
75
- const step3 = ora('Step 3/6: Generating backend models...').start();
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 = generateAdvancedCrudModel(
86
- resource.name,
87
- resource.fields,
88
- false,
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(`✅ Generated ${modelCount} models`);
96
+ step3.succeed(`Generated ${modelCount} models`);
97
97
 
98
98
  // ========================================================
99
99
  // STEP 4: GENERATE BACKEND ROUTES
100
100
  // ========================================================
101
- const step4 = ora('Step 4/6: Generating backend routes...').start();
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 = generateAdvancedCrudRoutes(
111
- resource.name,
112
- resource.fields,
113
- false,
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(`✅ Generated ${routeCount} route files`);
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
- const sqlStep = ora('Generating SQL schema and seed files...').start();
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(chalk.green('✅ SQL files generated in backend/sql/'));
141
- console.log(chalk.gray(' • 01_schema.sql'));
142
- console.log(chalk.gray(' • 02_crud_operations.sql'));
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
- const step5 = ora('Step 5/6: Generating frontend API clients...').start();
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(`✅ Generated ${generatedClients.length} API client files`);
161
+ step5.succeed(`Generated ${generatedClients.length} API client files`);
159
162
 
160
- console.log(chalk.cyan('\n📁 Generated API Clients:\n'));
161
- for (const file of generatedClients) {
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
- const step6 = ora('Step 6/6: Injecting API calls in frontend...').start();
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(`✅ Injected API calls in ${injectedFiles.length} files`);
183
+ step6.succeed(`Injected API calls in ${injectedFiles.length} files`);
180
184
 
181
- console.log(chalk.cyan('\n💉 Modified Files:\n'));
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('✅ No injection needed (files already up-to-date)');
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
- console.log(chalk.green('\n✅ Smart API Generation Complete!\n'));
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
- console.log(chalk.cyan('📋 Generated APIs:\n'));
215
+ const apiEndpoints = [];
215
216
  for (const resource of resources) {
216
- console.log(chalk.white(` ${resource.name}:`));
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
- console.log(chalk.cyan('\n🚀 Next Steps:\n'));
225
- console.log(chalk.yellow(' 1. Update backend with new APIs:'));
226
- console.log(chalk.green(' backendify sync\n'));
227
- console.log(chalk.gray(' This will generate backend routes & models for your new APIs\n'));
228
- console.log(chalk.yellow(' 2. Start your servers:'));
229
- console.log(chalk.gray(' • Start MongoDB: mongod'));
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('�' offbyt - Interactive Setup ''));
12
- console.log(chalk.cyan('╚════════════════════════════════════════╝\n'));
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: '📦 Select Database:',
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: '⚙️ Select Backend Framework:',
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: '🔌 Enable Realtime Sockets?',
41
+ message: '🔌 Enable Realtime Sockets?',
42
42
  default: true
43
43
  },
44
44
  {
45
45
  type: 'confirm',
46
46
  name: 'enableAuth',
47
- message: '🔐 Generate Authentication System?',
47
+ message: '🔐 Generate Authentication System?',
48
48
  default: true
49
49
  },
50
50
  {
51
51
  type: 'list',
52
52
  name: 'authType',
53
- message: '🔑 Select Authentication Type:',
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: ' Enable Request Validation (Joi)?',
65
+ message: '✅ Enable Request Validation (Joi)?',
66
66
  default: true
67
67
  },
68
68
  {
69
69
  type: 'confirm',
70
70
  name: 'enableCaching',
71
- message: ' Enable Redis Caching?',
71
+ message: 'âš¡ Enable Redis Caching?',
72
72
  default: false
73
73
  },
74
74
  {
75
75
  type: 'confirm',
76
76
  name: 'enableLogging',
77
- message: '📊 Enable Advanced Logging?',
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 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'}`));
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(' MongoDB Connected');
260
+ console.log('[ok] MongoDB connected');
261
261
  } catch (error) {
262
- console.error(' MongoDB Connection Error:', error.message);
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(' PostgreSQL Connected');
286
+ console.log('[ok] PostgreSQL connected');
287
287
  await sequelize.sync({ alter: true });
288
288
  } catch (error) {
289
- console.error(' PostgreSQL Connection Error:', error.message);
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(' MySQL Connected');
313
+ console.log('[ok] MySQL connected');
314
314
  await sequelize.sync({ alter: true });
315
315
  } catch (error) {
316
- console.error(' MySQL Connection Error:', error.message);
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(' SQLite Connected');
335
+ console.log('[ok] SQLite connected');
336
336
  await sequelize.sync({ alter: true });
337
337
  } catch (error) {
338
- console.error(' SQLite Connection Error:', error.message);
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
-