mwalajs 1.0.9 → 1.1.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/bin/mwala.mjs CHANGED
@@ -43,7 +43,7 @@ const {
43
43
  if (!command || command === 'help' || command === 'h') {
44
44
  console.log(`
45
45
  ╔══════════════════════════════════════════════════════════╗
46
- ║ MwalaJS CLI v1.1.0
46
+ ║ MwalaJS CLI v1.2.0
47
47
  ╚══════════════════════════════════════════════════════════╝
48
48
 
49
49
  General:
@@ -84,7 +84,7 @@ Database Commands:
84
84
  mwala db:vacuum → Optimize (SQLite/PostgreSQL)
85
85
  mwala db:connections → Show active connections
86
86
  mwala db:kill-connections → Kill other connections (admin)
87
- mwala db:drop-all-tables → ⚠️ DROP ALL TABLES!
87
+ mwala db:drop-all-tables → DROP ALL TABLES!
88
88
 
89
89
  mwala db:export <table> <file.csv> → Export to CSV
90
90
  mwala db:import <file.csv> <table> → Import from CSV
@@ -94,7 +94,7 @@ Migrations:
94
94
  mwala migrate all → Run all migrations
95
95
  mwala rollback last → Rollback last migration
96
96
 
97
- Enjoy building with MwalaJS! 🚀
97
+ Enjoy building with MwalaJS!
98
98
  `);
99
99
  process.exit(0);
100
100
  }
@@ -297,7 +297,7 @@ switch (command) {
297
297
  break;
298
298
 
299
299
  case 'db:kill-connections':
300
- if (readlineSync.keyInYN('⚠️ Kill all other database connections?')) {
300
+ if (readlineSync.keyInYN(' Kill all other database connections?')) {
301
301
  killConnections();
302
302
  } else {
303
303
  console.log('Cancelled.');
@@ -305,7 +305,7 @@ switch (command) {
305
305
  break;
306
306
 
307
307
  case 'db:drop-all-tables':
308
- if (readlineSync.keyInYN('⚠️⚠️⚠️ DROP ALL TABLES PERMANENTLY? Type YES to confirm:')) {
308
+ if (readlineSync.keyInYN(' DROP ALL TABLES PERMANENTLY? Type YES to confirm:')) {
309
309
  if (readlineSync.question('Type database name to confirm: ') === process.env.DB_NAME) {
310
310
  dropAllTables();
311
311
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mwalajs",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "MwalaJS Framework CLI Tool and Web Framework for Backend and Frontend Development.",
5
5
  "type": "module",
6
6
  "main": "app.mjs",
package/utils/dbUtils.mjs CHANGED
@@ -1,97 +1,272 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { execSync } from 'child_process';
4
+ import { createWriteStream, createReadStream } from 'fs';
5
+ import csv from 'csv-parser';
6
+ import { stringify } from 'csv-stringify';
4
7
  import dotenv from 'dotenv';
5
8
  import mysql from 'mysql2/promise';
6
9
  import { Client } from 'pg';
7
10
  import { MongoClient } from 'mongodb';
8
11
  import sqlite3 from 'sqlite3';
9
12
  import readlineSync from 'readline-sync';
13
+ import { fileURLToPath } from 'url';
10
14
 
15
+ // Load .env
11
16
  dotenv.config();
12
17
 
13
- const { DB_TYPE, DB_HOST = 'localhost', DB_USER, DB_PASSWORD, DB_NAME } = process.env;
18
+ const { DB_TYPE, DB_HOST = 'localhost', DB_USER, DB_PASSWORD, DB_NAME, DB_PORT } = process.env;
14
19
 
15
- let conn = null;
20
+ // Helper to check if DB is configured
21
+ const isDbConfigured = () => {
22
+ return DB_TYPE && DB_NAME && (DB_TYPE === 'sqlite' || (DB_USER && DB_PASSWORD));
23
+ };
24
+
25
+ const suggestDbSetup = () => {
26
+ console.log('\n⚠️ Database not configured or connection failed.');
27
+ console.log(' Run: mwala create-db → to set up your database interactively');
28
+ console.log(' Or manually create a .env file with:');
29
+ console.log(' DB_TYPE=mysql|postgresql|sqlite|mongodb');
30
+ console.log(' DB_NAME=your_db_name');
31
+ console.log(' DB_HOST=localhost');
32
+ console.log(' DB_USER=your_user');
33
+ console.log(' DB_PASSWORD=your_pass\n');
34
+ };
35
+
36
+ // Global connection cache
37
+ let connection = null;
16
38
 
17
- const getConn = async () => {
18
- if (conn) return conn;
39
+ const getConnection = async () => {
40
+ if (!isDbConfigured()) {
41
+ suggestDbSetup();
42
+ process.exit(1);
43
+ }
44
+
45
+ if (connection) return connection;
19
46
 
20
47
  try {
21
48
  if (DB_TYPE === 'mysql') {
22
- conn = await mysql.createConnection({
49
+ connection = await mysql.createConnection({
23
50
  host: DB_HOST,
24
51
  user: DB_USER,
25
52
  password: DB_PASSWORD,
26
- database: DB_NAME
53
+ database: DB_NAME,
54
+ port: DB_PORT || 3306
27
55
  });
28
56
  } else if (DB_TYPE === 'postgresql') {
29
- conn = new Client({
57
+ connection = new Client({
30
58
  host: DB_HOST,
31
59
  user: DB_USER,
32
60
  password: DB_PASSWORD,
33
- database: DB_NAME
61
+ database: DB_NAME,
62
+ port: DB_PORT || 5432
34
63
  });
35
- await conn.connect();
64
+ await connection.connect();
36
65
  } else if (DB_TYPE === 'sqlite') {
37
- conn = new sqlite3.Database(`./${DB_NAME}.sqlite`);
66
+ connection = new sqlite3.Database(`./${DB_NAME}.sqlite`);
67
+ connection.serialize();
38
68
  } else if (DB_TYPE === 'mongodb') {
39
- conn = await MongoClient.connect(`mongodb://${DB_HOST}:27017`);
40
- conn = conn.db(DB_NAME);
69
+ const client = await MongoClient.connect(`mongodb://${DB_HOST}:${DB_PORT || 27017}`);
70
+ connection = client.db(DB_NAME);
71
+ } else {
72
+ console.error(' Unsupported DB_TYPE:', DB_TYPE);
73
+ process.exit(1);
41
74
  }
42
75
  } catch (err) {
43
- console.error('Connection failed:', err.message);
76
+ console.error(' Failed to connect to database:', err.message);
77
+ suggestDbSetup();
44
78
  process.exit(1);
45
79
  }
46
80
 
47
- return conn;
81
+ return connection;
48
82
  };
49
83
 
84
+ // ====================== IMPLEMENTED COMMANDS ======================
85
+
50
86
  export const listTables = async () => {
51
- const connection = await getConn();
87
+ const conn = await getConnection();
88
+
52
89
  if (DB_TYPE === 'mysql') {
53
- const [rows] = await connection.query('SHOW TABLES');
54
- console.log('Tables:\n', rows.map(r => Object.values(r)[0]).join('\n'));
55
- } else if (DB_TYPE === 'postgresql') {
56
- const res = await connection.query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
57
- console.log('Tables:\n', res.rows.map(r => r.tablename).join('\n'));
58
- } else if (DB_TYPE === 'sqlite') {
59
- connection.all("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'", (_, rows) => {
60
- console.log('Tables:\n', rows.map(r => r.name).join('\n'));
90
+ const [rows] = await conn.query('SHOW TABLES');
91
+ const tables = rows.map(r => Object.values(r)[0]);
92
+ console.log('📋 Tables in database:');
93
+ tables.forEach(t => console.log(` • ${t}`));
94
+ if (tables.length === 0) console.log(' (No tables found)');
95
+ }
96
+ else if (DB_TYPE === 'postgresql') {
97
+ const res = await conn.query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
98
+ console.log('📋 Tables:');
99
+ res.rows.forEach(r => console.log(` • ${r.tablename}`));
100
+ }
101
+ else if (DB_TYPE === 'sqlite') {
102
+ return new Promise((resolve) => {
103
+ conn.all("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'", (err, rows) => {
104
+ if (err) return console.error(err.message);
105
+ console.log('📋 Tables:');
106
+ rows.forEach(r => console.log(` • ${r.name}`));
107
+ if (rows.length === 0) console.log(' (No tables found)');
108
+ resolve();
109
+ });
61
110
  });
111
+ }
112
+ else if (DB_TYPE === 'mongodb') {
113
+ const collections = await conn.listCollections().toArray();
114
+ console.log('📋 Collections:');
115
+ collections.forEach(c => console.log(` • ${c.name}`));
62
116
  }
63
117
  };
64
118
 
65
119
  export const describeTable = async (table) => {
66
- const connection = await getConn();
120
+ const conn = await getConnection();
121
+
67
122
  if (DB_TYPE === 'mysql') {
68
- const [rows] = await connection.query(`DESCRIBE \`${table}\``);
123
+ const [rows] = await conn.query(`DESCRIBE \`${table}\``);
69
124
  console.table(rows);
70
- } else if (DB_TYPE === 'postgresql') {
71
- const res = await connection.query(`
125
+ }
126
+ else if (DB_TYPE === 'postgresql') {
127
+ const res = await conn.query(`
72
128
  SELECT column_name, data_type, is_nullable, column_default
73
- FROM information_schema.columns WHERE table_name = $1
129
+ FROM information_schema.columns
130
+ WHERE table_name = $1
131
+ ORDER BY ordinal_position
74
132
  `, [table]);
75
133
  console.table(res.rows);
134
+ }
135
+ else if (DB_TYPE === 'sqlite') {
136
+ return new Promise((resolve) => {
137
+ conn.all(`PRAGMA table_info(\`${table}\`)`, (err, rows) => {
138
+ if (err) return console.error('Table not found or error:', err.message);
139
+ console.table(rows);
140
+ resolve();
141
+ });
142
+ });
143
+ }
144
+ else if (DB_TYPE === 'mongodb') {
145
+ const sample = await conn.collection(table).findOne();
146
+ if (!sample) {
147
+ console.log(`Collection "${table}" is empty.`);
148
+ return;
149
+ }
150
+ console.log(`📄 Sample document from "${table}":`);
151
+ console.dir(sample, { depth: null, colors: true });
76
152
  }
77
153
  };
78
154
 
79
155
  export const countRows = async (table) => {
80
- const connection = await getConn();
81
- const [res] = await connection.query(`SELECT COUNT(*) AS count FROM \`${table}\``);
82
- console.log(`Rows in ${table}: ${res[0].count}`);
156
+ const conn = await getConnection();
157
+
158
+ if (DB_TYPE === 'mysql' || DB_TYPE === 'postgresql') {
159
+ const [res] = await conn.query(`SELECT COUNT(*) AS count FROM \`${table}\``);
160
+ console.log(`📊 Rows in "${table}": ${res[0].count}`);
161
+ }
162
+ else if (DB_TYPE === 'sqlite') {
163
+ return new Promise((resolve) => {
164
+ conn.get(`SELECT COUNT(*) AS count FROM \`${table}\``, (err, row) => {
165
+ if (err) return console.error(err.message);
166
+ console.log(`📊 Rows in "${table}": ${row.count}`);
167
+ resolve();
168
+ });
169
+ });
170
+ }
171
+ else if (DB_TYPE === 'mongodb') {
172
+ const count = await conn.collection(table).estimatedDocumentCount();
173
+ console.log(`📊 Documents in "${table}": ${count}`);
174
+ }
83
175
  };
84
176
 
85
177
  export const truncateTable = async (table) => {
86
- const connection = await getConn();
87
- await connection.query(`TRUNCATE TABLE \`${table}\` RESTART IDENTITY CASCADE`);
88
- console.log(`✅ ${table} truncated`);
178
+ const conn = await getConnection();
179
+
180
+ if (DB_TYPE === 'mysql') {
181
+ await conn.query(`TRUNCATE TABLE \`${table}\``);
182
+ }
183
+ else if (DB_TYPE === 'postgresql') {
184
+ await conn.query(`TRUNCATE TABLE "${table}" RESTART IDENTITY CASCADE`);
185
+ }
186
+ else if (DB_TYPE === 'sqlite') {
187
+ await new Promise((resolve, reject) => {
188
+ conn.run(`DELETE FROM \`${table}\``, function(err) {
189
+ if (err) reject(err);
190
+ conn.run(`DELETE FROM sqlite_sequence WHERE name='${table}'`, () => resolve());
191
+ });
192
+ });
193
+ }
194
+ else if (DB_TYPE === 'mongodb') {
195
+ await conn.collection(table).deleteMany({});
196
+ }
197
+
198
+ console.log(` "${table}" truncated successfully`);
89
199
  };
90
200
 
91
201
  export const renameTable = async (oldName, newName) => {
92
- const connection = await getConn();
93
- await connection.query(`ALTER TABLE \`${oldName}\` RENAME TO \`${newName}\``);
94
- console.log(`Renamed ${oldName} ${newName}`);
202
+ const conn = await getConnection();
203
+
204
+ if (DB_TYPE === 'mysql') {
205
+ await conn.query(`ALTER TABLE \`${oldName}\` RENAME TO \`${newName}\``);
206
+ }
207
+ else if (DB_TYPE === 'postgresql') {
208
+ await conn.query(`ALTER TABLE "${oldName}" RENAME TO "${newName}"`);
209
+ }
210
+ else if (DB_TYPE === 'sqlite') {
211
+ await new Promise((resolve, reject) => {
212
+ conn.run(`ALTER TABLE \`${oldName}\` RENAME TO \`${newName}\``, err => {
213
+ err ? reject(err) : resolve();
214
+ });
215
+ });
216
+ }
217
+ else if (DB_TYPE === 'mongodb') {
218
+ await conn.collection(oldName).rename(newName);
219
+ }
220
+
221
+ console.log(` Renamed "${oldName}" → "${newName}"`);
222
+ };
223
+
224
+ export const checkTableExists = async (table) => {
225
+ const conn = await getConnection();
226
+
227
+ let exists = false;
228
+ if (DB_TYPE === 'mysql') {
229
+ const [rows] = await conn.query(`SHOW TABLES LIKE '${table}'`);
230
+ exists = rows.length > 0;
231
+ }
232
+ else if (DB_TYPE === 'postgresql') {
233
+ const res = await conn.query(`SELECT to_regclass('public.${table}')`);
234
+ exists = res.rows[0].to_regclass !== null;
235
+ }
236
+ else if (DB_TYPE === 'sqlite') {
237
+ await new Promise((resolve) => {
238
+ conn.get(`SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`, (err, row) => {
239
+ exists = !!row;
240
+ resolve();
241
+ });
242
+ });
243
+ }
244
+ else if (DB_TYPE === 'mongodb') {
245
+ const collections = await conn.listCollections({ name: table }).toArray();
246
+ exists = collections.length > 0;
247
+ }
248
+
249
+ console.log(`Table/collection "${table}" ${exists ? ' exists' : ' does not exist'}`);
250
+ };
251
+
252
+ export const copyTable = async (source, destination) => {
253
+ const conn = await getConnection();
254
+
255
+ if (DB_TYPE === 'mysql' || DB_TYPE === 'postgresql') {
256
+ const sql = DB_TYPE === 'mysql'
257
+ ? `CREATE TABLE \`${destination}\` LIKE \`${source}\`; INSERT INTO \`${destination}\` SELECT * FROM \`${source}\`;`
258
+ : `CREATE TABLE "${destination}" AS TABLE "${source}" WITH DATA;`;
259
+ await conn.query(sql);
260
+ }
261
+ else if (DB_TYPE === 'sqlite') {
262
+ await new Promise((resolve, reject) => {
263
+ conn.run(`CREATE TABLE \`${destination}\` AS SELECT * FROM \`${source}\``, err => {
264
+ err ? reject(err) : resolve();
265
+ });
266
+ });
267
+ }
268
+
269
+ console.log(` Copied "${source}" → "${destination}"`);
95
270
  };
96
271
 
97
272
  export const backupDatabase = () => {
@@ -100,51 +275,243 @@ export const backupDatabase = () => {
100
275
 
101
276
  try {
102
277
  if (DB_TYPE === 'mysql') {
103
- execSync(`mysqldump -h ${DB_HOST} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} > ${file}`);
104
- } else if (DB_TYPE === 'postgresql') {
278
+ execSync(`mysqldump -h ${DB_HOST} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} > "${file}"`);
279
+ }
280
+ else if (DB_TYPE === 'postgresql') {
105
281
  process.env.PGPASSWORD = DB_PASSWORD;
106
- execSync(`pg_dump -h ${DB_HOST} -U ${DB_USER} ${DB_NAME} > ${file}`);
282
+ execSync(`pg_dump -h ${DB_HOST} -U ${DB_USER} ${DB_NAME} > "${file}"`);
283
+ }
284
+ else if (DB_TYPE === 'sqlite') {
285
+ fs.copyFileSync(`./${DB_NAME}.sqlite`, file.replace('.sql', '.sqlite'));
107
286
  }
108
- console.log(`✅ Backup saved: ${file}`);
287
+
288
+ console.log(` Backup saved: ${file}`);
109
289
  } catch (err) {
110
- console.error('Backup failed:', err.message);
290
+ console.error(' Backup failed:', err.message);
111
291
  }
112
292
  };
113
293
 
114
294
  export const restoreDatabase = (file) => {
115
295
  if (!fs.existsSync(file)) {
116
- console.error('Backup file not found');
296
+ console.error(' Backup file not found:', file);
117
297
  return;
118
298
  }
299
+
119
300
  try {
120
301
  if (DB_TYPE === 'mysql') {
121
- execSync(`mysql -h ${DB_HOST} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} < ${file}`);
122
- } else if (DB_TYPE === 'postgresql') {
302
+ execSync(`mysql -h ${DB_HOST} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} < "${file}"`);
303
+ }
304
+ else if (DB_TYPE === 'postgresql') {
123
305
  process.env.PGPASSWORD = DB_PASSWORD;
124
- execSync(`psql -h ${DB_HOST} -U ${DB_USER} -d ${DB_NAME} -f ${file}`);
306
+ execSync(`psql -h ${DB_HOST} -U ${DB_USER} -d ${DB_NAME} -f "${file}"`);
307
+ }
308
+ else if (DB_TYPE === 'sqlite') {
309
+ fs.copyFileSync(file, `./${DB_NAME}.sqlite`);
125
310
  }
126
- console.log('✅ Database restored');
311
+
312
+ console.log(' Database restored from:', file);
127
313
  } catch (err) {
128
- console.error('Restore failed:', err.message);
129
- }
130
- };
131
-
132
- export const showDatabaseSize = () => console.log(`Size info not implemented for ${DB_TYPE} yet`);
133
- export const listIndexes = () => console.log('Indexes list coming soon');
134
- export const vacuumDatabase = () => console.log('VACUUM/optimize not implemented yet');
135
- export const showConnections = () => console.log('Connections list not implemented');
136
- export const killConnections = () => console.log('Kill connections not implemented');
137
- export const dropAllTables = () => console.log('Drop all tables not implemented (too dangerous without confirmation logic)');
138
- export const copyTable = () => console.log('Copy table not implemented');
139
- export const exportTableToCsv = () => console.log('CSV export not implemented');
140
- export const importCsvToTable = () => console.log('CSV import not implemented');
141
- export const seedDatabase = (file) => {
142
- if (fs.existsSync(file)) {
143
- import(pathToFileURL(path.resolve(file)).href).then(mod => {
144
- console.log('Seed executed');
314
+ console.error(' Restore failed:', err.message);
315
+ }
316
+ };
317
+
318
+ export const showDatabaseSize = async () => {
319
+ const conn = await getConnection();
320
+
321
+ if (DB_TYPE === 'mysql') {
322
+ const [res] = await conn.query(`
323
+ SELECT table_schema AS "Database",
324
+ ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size (MB)"
325
+ FROM information_schema.TABLES
326
+ WHERE table_schema = '${DB_NAME}'
327
+ GROUP BY table_schema
328
+ `);
329
+ console.table(res);
330
+ }
331
+ else if (DB_TYPE === 'postgresql') {
332
+ const res = await conn.query(`SELECT pg_size_pretty(pg_database_size('${DB_NAME}')) AS size`);
333
+ console.log(`Database size: ${res.rows[0].size}`);
334
+ }
335
+ else if (DB_TYPE === 'sqlite') {
336
+ const stats = fs.statSync(`./${DB_NAME}.sqlite`);
337
+ console.log(`Database file size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
338
+ }
339
+ else if (DB_TYPE === 'mongodb') {
340
+ const stats = await conn.stats();
341
+ console.log(`Database size: ${(stats.dataSize / 1024 / 1024).toFixed(2)} MB`);
342
+ }
343
+ };
344
+
345
+ export const listIndexes = async (table) => {
346
+ const conn = await getConnection();
347
+
348
+ if (DB_TYPE === 'mysql') {
349
+ const [rows] = await conn.query(`SHOW INDEX FROM \`${table}\``);
350
+ console.table(rows);
351
+ }
352
+ else if (DB_TYPE === 'postgresql') {
353
+ const res = await conn.query(`
354
+ SELECT indexname, indexdef
355
+ FROM pg_indexes
356
+ WHERE tablename = $1
357
+ `, [table]);
358
+ console.table(res.rows);
359
+ }
360
+ };
361
+
362
+ export const vacuumDatabase = async () => {
363
+ const conn = await getConnection();
364
+
365
+ if (DB_TYPE === 'postgresql') {
366
+ await conn.query('VACUUM ANALYZE');
367
+ console.log(' VACUUM ANALYZE completed');
368
+ }
369
+ else if (DB_TYPE === 'sqlite') {
370
+ await new Promise((resolve) => {
371
+ conn.exec('VACUUM', () => {
372
+ console.log(' VACUUM completed');
373
+ resolve();
374
+ });
375
+ });
376
+ }
377
+ else {
378
+ console.log('VACUUM not supported for', DB_TYPE);
379
+ }
380
+ };
381
+
382
+ export const showConnections = async () => {
383
+ const conn = await getConnection();
384
+
385
+ if (DB_TYPE === 'postgresql') {
386
+ const res = await conn.query(`
387
+ SELECT count(*), state FROM pg_stat_activity
388
+ WHERE datname = '${DB_NAME}'
389
+ GROUP BY state
390
+ `);
391
+ console.table(res.rows);
392
+ }
393
+ else if (DB_TYPE === 'mysql') {
394
+ const [rows] = await conn.query('SHOW PROCESSLIST');
395
+ console.log(`Active connections: ${rows.length}`);
396
+ }
397
+ };
398
+
399
+ export const killConnections = async () => {
400
+ const conn = await getConnection();
401
+
402
+ if (DB_TYPE === 'postgresql') {
403
+ await conn.query(`
404
+ SELECT pg_terminate_backend(pg_stat_activity.pid)
405
+ FROM pg_stat_activity
406
+ WHERE pg_stat_activity.datname = '${DB_NAME}'
407
+ AND pid <> pg_backend_pid()
408
+ `);
409
+ console.log(' All other connections terminated');
410
+ }
411
+ };
412
+
413
+ export const dropAllTables = async () => {
414
+ const conn = await getConnection();
415
+
416
+ if (DB_TYPE === 'mysql') {
417
+ await conn.query('SET FOREIGN_KEY_CHECKS = 0');
418
+ const [tables] = await conn.query('SHOW TABLES');
419
+ for (const t of tables) {
420
+ const tableName = Object.values(t)[0];
421
+ await conn.query(`DROP TABLE IF EXISTS \`${tableName}\``);
422
+ }
423
+ await conn.query('SET FOREIGN_KEY_CHECKS = 1');
424
+ }
425
+ else if (DB_TYPE === 'postgresql') {
426
+ const res = await conn.query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
427
+ for (const row of res.rows) {
428
+ await conn.query(`DROP TABLE IF EXISTS "${row.tablename}" CASCADE`);
429
+ }
430
+ }
431
+ else if (DB_TYPE === 'mongodb') {
432
+ const collections = await conn.listCollections().toArray();
433
+ for (const col of collections) {
434
+ await conn.collection(col.name).drop();
435
+ }
436
+ }
437
+
438
+ console.log('⚠️ All tables/collections dropped!');
439
+ };
440
+
441
+ export const exportTableToCsv = async (table, csvFile) => {
442
+ const conn = await getConnection();
443
+
444
+ const writableStream = createWriteStream(csvFile);
445
+
446
+ if (DB_TYPE === 'mysql' || DB_TYPE === 'postgresql') {
447
+ const [rows] = await conn.query(`SELECT * FROM \`${table}\``);
448
+ const stringifier = stringify({ header: true });
449
+ stringifier.pipe(writableStream);
450
+ rows.forEach(row => stringifier.write(row));
451
+ stringifier.end();
452
+ }
453
+ else if (DB_TYPE === 'sqlite') {
454
+ await new Promise((resolve) => {
455
+ conn.all(`SELECT * FROM \`${table}\``, (err, rows) => {
456
+ const stringifier = stringify({ header: true });
457
+ stringifier.pipe(writableStream);
458
+ rows.forEach(row => stringifier.write(row));
459
+ stringifier.end();
460
+ resolve();
461
+ });
145
462
  });
146
- } else {
147
- console.error('Seed file not found');
148
463
  }
464
+
465
+ console.log(` Exported "${table}" → ${csvFile}`);
466
+ };
467
+
468
+ export const importCsvToTable = async (csvFile, table) => {
469
+ if (!fs.existsSync(csvFile)) {
470
+ console.error('CSV file not found:', csvFile);
471
+ return;
472
+ }
473
+
474
+ const conn = await getConnection();
475
+ const results = [];
476
+
477
+ createReadStream(csvFile)
478
+ .pipe(csv())
479
+ .on('data', (data) => results.push(data))
480
+ .on('end', async () => {
481
+ if (results.length === 0) {
482
+ console.log('No data to import');
483
+ return;
484
+ }
485
+
486
+ const columns = Object.keys(results[0]).map(c => `\`${c}\``).join(', ');
487
+ const placeholders = Object.keys(results[0]).map(() => '?').join(', ');
488
+
489
+ for (const row of results) {
490
+ const values = Object.values(row);
491
+ await conn.query(`INSERT INTO \`${table}\` (${columns}) VALUES (${placeholders})`, values);
492
+ }
493
+
494
+ console.log(` Imported ${results.length} rows into "${table}"`);
495
+ });
149
496
  };
150
- export const checkTableExists = () => console.log('Check exists not implemented');
497
+
498
+ export const seedDatabase = async (seedFile) => {
499
+ const fullPath = path.resolve(seedFile);
500
+
501
+ if (!fs.existsSync(fullPath)) {
502
+ console.error(' Seed file not found:', fullPath);
503
+ return;
504
+ }
505
+
506
+ try {
507
+ const seedModule = await import(`file://${fullPath}`);
508
+ if (typeof seedModule.default === 'function') {
509
+ await seedModule.default();
510
+ console.log(' Seed completed');
511
+ } else {
512
+ console.log('Seed file should export a default async function');
513
+ }
514
+ } catch (err) {
515
+ console.error(' Seed failed:', err.message);
516
+ }
517
+ };