mwalajs 1.0.8 → 1.0.9
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 +215 -162
- package/package.json +1 -1
- package/utils/dbUtils.mjs +100 -74
package/bin/mwala.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -9,19 +9,20 @@ import readlineSync from 'readline-sync';
|
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = path.dirname(__filename);
|
|
11
11
|
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const command = args[0];
|
|
14
|
+
|
|
12
15
|
// Dynamic imports
|
|
13
16
|
const { getDbConnection } = await import(pathToFileURL(path.join(__dirname, '../config/createdatabase.mjs')).href);
|
|
14
|
-
const { createTable, dropTable, migrateAll, rollbackLastMigration } = await import(pathToFileURL(path.join(__dirname, '../runMigrations.mjs')).href);
|
|
15
17
|
const { setupMwalajs } = await import(pathToFileURL(path.join(__dirname, '../setupMwalajs.mjs')).href);
|
|
16
18
|
const { createProject } = await import(pathToFileURL(path.join(__dirname, '../createProject.mjs')).href);
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
backupDatabase,
|
|
20
|
+
const {
|
|
21
|
+
listTables,
|
|
22
|
+
describeTable,
|
|
23
|
+
truncateTable,
|
|
24
|
+
renameTable,
|
|
25
|
+
backupDatabase,
|
|
25
26
|
restoreDatabase,
|
|
26
27
|
seedDatabase,
|
|
27
28
|
showDatabaseSize,
|
|
@@ -37,88 +38,74 @@ const {
|
|
|
37
38
|
showConnections,
|
|
38
39
|
killConnections,
|
|
39
40
|
checkTableExists
|
|
40
|
-
} = await import(pathToFileURL(path.join(__dirname, '../dbUtils.mjs')).href);
|
|
41
|
-
|
|
42
|
-
const args = process.argv.slice(2);
|
|
43
|
-
const command = args[0];
|
|
41
|
+
} = await import(pathToFileURL(path.join(__dirname, '../utils/dbUtils.mjs')).href);
|
|
44
42
|
|
|
45
43
|
if (!command || command === 'help' || command === 'h') {
|
|
46
44
|
console.log(`
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
Maintenance & Stats:
|
|
101
|
-
- mwala db:size → Show database size
|
|
102
|
-
- mwala db:indexes <table> → List indexes on table
|
|
103
|
-
- mwala db:analyze <table> → Analyze table (PostgreSQL)
|
|
104
|
-
- mwala db:reindex <table> → Rebuild indexes
|
|
105
|
-
- mwala db:vacuum → Vacuum database (SQLite/PostgreSQL)
|
|
106
|
-
- mwala db:connections → Show active connections
|
|
107
|
-
- mwala db:kill-connections → Kill all other connections (admin)
|
|
108
|
-
- mwala db:drop-all-tables → ⚠️ Drop ALL tables (dangerous!)
|
|
109
|
-
|
|
110
|
-
Use: mwala <command> [options]
|
|
45
|
+
╔══════════════════════════════════════════════════════════╗
|
|
46
|
+
║ MwalaJS CLI v1.1.0 ║
|
|
47
|
+
╚══════════════════════════════════════════════════════════╝
|
|
48
|
+
|
|
49
|
+
General:
|
|
50
|
+
mwala -v | --version → Show version
|
|
51
|
+
mwala help | h → Show this help
|
|
52
|
+
|
|
53
|
+
Project:
|
|
54
|
+
mwala create-project → Create new project
|
|
55
|
+
mwala init → Initialize MwalaJS
|
|
56
|
+
|
|
57
|
+
Run:
|
|
58
|
+
mwala serve → Start the server (runs app.mjs)
|
|
59
|
+
|
|
60
|
+
Generate:
|
|
61
|
+
mwala generate model <name>
|
|
62
|
+
mwala generate controller <name>
|
|
63
|
+
mwala generate route <name>
|
|
64
|
+
mwala generate view <name>
|
|
65
|
+
mwala generate midware <name>
|
|
66
|
+
|
|
67
|
+
Database Setup:
|
|
68
|
+
mwala create-db → Interactive DB setup & creation
|
|
69
|
+
mwala db:config → Reconfigure database (.env)
|
|
70
|
+
|
|
71
|
+
Database Commands:
|
|
72
|
+
mwala db:table list → List all tables
|
|
73
|
+
mwala db:table describe <name> → Show table structure
|
|
74
|
+
mwala db:table count <name> → Count rows
|
|
75
|
+
mwala db:table truncate <name> → Empty table
|
|
76
|
+
mwala db:table rename <old> <new> → Rename table
|
|
77
|
+
mwala db:table copy <src> <dest> → Copy table
|
|
78
|
+
mwala db:table exists <name> → Check if exists
|
|
79
|
+
|
|
80
|
+
mwala db:backup → Backup database
|
|
81
|
+
mwala db:restore <file.sql> → Restore from backup
|
|
82
|
+
mwala db:size → Show database size
|
|
83
|
+
mwala db:indexes <table> → List indexes
|
|
84
|
+
mwala db:vacuum → Optimize (SQLite/PostgreSQL)
|
|
85
|
+
mwala db:connections → Show active connections
|
|
86
|
+
mwala db:kill-connections → Kill other connections (admin)
|
|
87
|
+
mwala db:drop-all-tables → ⚠️ DROP ALL TABLES!
|
|
88
|
+
|
|
89
|
+
mwala db:export <table> <file.csv> → Export to CSV
|
|
90
|
+
mwala db:import <file.csv> <table> → Import from CSV
|
|
91
|
+
mwala db:seed <seedFile.mjs> → Run seed file
|
|
92
|
+
|
|
93
|
+
Migrations:
|
|
94
|
+
mwala migrate all → Run all migrations
|
|
95
|
+
mwala rollback last → Rollback last migration
|
|
96
|
+
|
|
97
|
+
Enjoy building with MwalaJS! 🚀
|
|
111
98
|
`);
|
|
112
99
|
process.exit(0);
|
|
113
100
|
}
|
|
114
101
|
|
|
102
|
+
// ====================== COMMAND SWITCH ======================
|
|
115
103
|
switch (command) {
|
|
116
|
-
|
|
117
|
-
case 'version':
|
|
118
104
|
case '-v':
|
|
105
|
+
case 'version':
|
|
119
106
|
case '--version':
|
|
120
107
|
console.log('MwalaJS Version: 1.1.0');
|
|
121
|
-
|
|
108
|
+
process.exit(0);
|
|
122
109
|
|
|
123
110
|
case 'create-project':
|
|
124
111
|
createProject();
|
|
@@ -129,143 +116,178 @@ switch (command) {
|
|
|
129
116
|
break;
|
|
130
117
|
|
|
131
118
|
case 'serve':
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
119
|
+
const child = spawn('node', ['app.mjs'], {
|
|
120
|
+
stdio: 'inherit',
|
|
121
|
+
cwd: process.cwd()
|
|
122
|
+
});
|
|
135
123
|
|
|
136
124
|
process.on('SIGINT', () => child.kill('SIGINT'));
|
|
137
125
|
process.on('SIGTERM', () => child.kill('SIGTERM'));
|
|
138
126
|
|
|
139
|
-
child.on('exit', (code) => process.exit(code
|
|
127
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
140
128
|
child.on('error', (err) => {
|
|
141
129
|
console.error(`Failed to start app: ${err.message}`);
|
|
142
130
|
process.exit(1);
|
|
143
131
|
});
|
|
144
132
|
break;
|
|
145
133
|
|
|
146
|
-
case 'generate':
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
case 'generate': {
|
|
135
|
+
const type = args[1]?.toLowerCase();
|
|
136
|
+
const name = args[2];
|
|
137
|
+
|
|
138
|
+
if (!type || !name) {
|
|
139
|
+
console.error('Usage: mwala generate <model|controller|route|view|midware> <name>');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const folders = {
|
|
144
|
+
model: 'models',
|
|
145
|
+
controller: 'controllers',
|
|
146
|
+
route: 'routes',
|
|
147
|
+
view: 'views',
|
|
148
|
+
midware: 'middlewares'
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (!folders[type]) {
|
|
152
|
+
console.error(`Invalid type. Use: ${Object.keys(folders).join(', ')}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const filePath = path.join(process.cwd(), folders[type], `${name}.mjs`);
|
|
157
|
+
|
|
158
|
+
if (fs.existsSync(filePath)) {
|
|
159
|
+
console.error(`${name} ${type} already exists.`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let content = '';
|
|
164
|
+
switch (type) {
|
|
165
|
+
case 'model':
|
|
166
|
+
content = `export const ${name}Model = {};\n`;
|
|
167
|
+
break;
|
|
168
|
+
case 'controller':
|
|
169
|
+
content = `export const ${name}Controller = {\n getPage: (req, res) => {\n res.render('${name}', { title: '${name}' });\n }\n};\n`;
|
|
170
|
+
break;
|
|
171
|
+
case 'route':
|
|
172
|
+
content = `import mwalajs from 'mwalajs';\nimport { ${name}Controller } from '../controllers/${name}.mjs';\n\nconst router = mwalajs.Router();\nrouter.get('/', ${name}Controller.getPage);\nexport { router as ${name}Route };\n`;
|
|
173
|
+
break;
|
|
174
|
+
case 'view':
|
|
175
|
+
content = `<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="UTF-8">\n <title>${name}</title>\n</head>\n<body>\n <h1>Welcome to ${name} Page</h1>\n</body>\n</html>\n`;
|
|
176
|
+
break;
|
|
177
|
+
case 'midware':
|
|
178
|
+
content = `export const ${name} = (req, res, next) => {\n next();\n};\n`;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
183
|
+
fs.writeFileSync(filePath, content);
|
|
184
|
+
console.log(`✅ ${name} ${type} created at ${folders[type]}/${name}.mjs`);
|
|
149
185
|
break;
|
|
186
|
+
}
|
|
150
187
|
|
|
151
|
-
//
|
|
188
|
+
// ==================== DATABASE COMMANDS ====================
|
|
152
189
|
|
|
153
190
|
case 'create-db':
|
|
154
|
-
getDbConnection()
|
|
155
|
-
console.
|
|
156
|
-
|
|
157
|
-
|
|
191
|
+
getDbConnection()
|
|
192
|
+
.then(() => console.log('✅ Database configured and connected successfully!'))
|
|
193
|
+
.catch(err => {
|
|
194
|
+
console.error('❌ Database setup failed:', err.message);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
});
|
|
158
197
|
break;
|
|
159
198
|
|
|
160
199
|
case 'db:config':
|
|
161
|
-
getDbConnection();
|
|
200
|
+
getDbConnection();
|
|
162
201
|
break;
|
|
163
202
|
|
|
164
203
|
case 'db:table':
|
|
165
|
-
const
|
|
204
|
+
const action = args[1];
|
|
166
205
|
const tableName = args[2];
|
|
167
|
-
const
|
|
206
|
+
const extraArg = args[3];
|
|
168
207
|
|
|
169
|
-
if (!
|
|
170
|
-
console.
|
|
208
|
+
if (!action) {
|
|
209
|
+
console.error('Usage: mwala db:table <list|describe|count|truncate|rename|copy|exists> [args]');
|
|
171
210
|
process.exit(1);
|
|
172
211
|
}
|
|
173
212
|
|
|
174
|
-
switch (
|
|
213
|
+
switch (action) {
|
|
175
214
|
case 'list':
|
|
176
215
|
listTables();
|
|
177
216
|
break;
|
|
178
|
-
case '
|
|
179
|
-
if (!tableName)
|
|
180
|
-
|
|
217
|
+
case 'describe':
|
|
218
|
+
if (!tableName) {
|
|
219
|
+
console.error('Table name required');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
describeTable(tableName);
|
|
181
223
|
break;
|
|
182
|
-
case '
|
|
183
|
-
if (!tableName)
|
|
184
|
-
|
|
224
|
+
case 'count':
|
|
225
|
+
if (!tableName) {
|
|
226
|
+
console.error('Table name required');
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
countRows(tableName);
|
|
185
230
|
break;
|
|
186
231
|
case 'truncate':
|
|
187
|
-
if (!tableName)
|
|
188
|
-
|
|
232
|
+
if (!tableName) {
|
|
233
|
+
console.error('Table name required');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
if (readlineSync.keyInYN(`Truncate ${tableName}? This cannot be undone.`)) {
|
|
237
|
+
truncateTable(tableName);
|
|
238
|
+
}
|
|
189
239
|
break;
|
|
190
240
|
case 'rename':
|
|
191
|
-
if (!tableName || !
|
|
192
|
-
|
|
241
|
+
if (!tableName || !extraArg) {
|
|
242
|
+
console.error('Usage: mwala db:table rename <oldName> <newName>');
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
renameTable(tableName, extraArg);
|
|
193
246
|
break;
|
|
194
247
|
case 'copy':
|
|
195
|
-
if (!tableName || !
|
|
196
|
-
|
|
248
|
+
if (!tableName || !extraArg) {
|
|
249
|
+
console.error('Usage: mwala db:table copy <source> <destination>');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
copyTable(tableName, extraArg);
|
|
197
253
|
break;
|
|
198
254
|
case 'exists':
|
|
199
|
-
if (!tableName)
|
|
255
|
+
if (!tableName) {
|
|
256
|
+
console.error('Table name required');
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
200
259
|
checkTableExists(tableName);
|
|
201
260
|
break;
|
|
202
|
-
case 'describe':
|
|
203
|
-
if (!tableName) return console.error('Table name required');
|
|
204
|
-
describeTable(tableName);
|
|
205
|
-
break;
|
|
206
|
-
case 'count':
|
|
207
|
-
if (!tableName) return console.error('Table name required');
|
|
208
|
-
countRows(tableName);
|
|
209
|
-
break;
|
|
210
261
|
default:
|
|
211
|
-
console.
|
|
262
|
+
console.error(`Unknown db:table action: ${action}`);
|
|
263
|
+
process.exit(1);
|
|
212
264
|
}
|
|
213
265
|
break;
|
|
214
266
|
|
|
215
|
-
case 'migrate':
|
|
216
|
-
if (args[1] === 'all') migrateAll();
|
|
217
|
-
else console.error('Use: mwala migrate all');
|
|
218
|
-
break;
|
|
219
|
-
|
|
220
|
-
case 'rollback':
|
|
221
|
-
if (args[1] === 'last') rollbackLastMigration();
|
|
222
|
-
else if (args[1] === 'all') dropAllTables(); // or full rollback
|
|
223
|
-
else console.error('Use: mwala rollback last');
|
|
224
|
-
break;
|
|
225
|
-
|
|
226
|
-
case 'db:seed':
|
|
227
|
-
if (!args[1]) return console.error('Seed file required');
|
|
228
|
-
seedDatabase(args[1]);
|
|
229
|
-
break;
|
|
230
|
-
|
|
231
267
|
case 'db:backup':
|
|
232
268
|
backupDatabase();
|
|
233
269
|
break;
|
|
234
270
|
|
|
235
271
|
case 'db:restore':
|
|
236
|
-
if (!args[1])
|
|
272
|
+
if (!args[1]) {
|
|
273
|
+
console.error('Usage: mwala db:restore <backup-file.sql>');
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
237
276
|
restoreDatabase(args[1]);
|
|
238
277
|
break;
|
|
239
278
|
|
|
240
|
-
case 'db:export':
|
|
241
|
-
if (!args[1] || !args[2]) return console.error('Usage: db:export <table> <file.csv>');
|
|
242
|
-
exportTableToCsv(args[1], args[2]);
|
|
243
|
-
break;
|
|
244
|
-
|
|
245
|
-
case 'db:import':
|
|
246
|
-
if (!args[1] || !args[2]) return console.error('Usage: db:import <file.csv> <table>');
|
|
247
|
-
importCsvToTable(args[1], args[2]);
|
|
248
|
-
break;
|
|
249
|
-
|
|
250
279
|
case 'db:size':
|
|
251
280
|
showDatabaseSize();
|
|
252
281
|
break;
|
|
253
282
|
|
|
254
283
|
case 'db:indexes':
|
|
255
|
-
if (!args[1])
|
|
284
|
+
if (!args[1]) {
|
|
285
|
+
console.error('Table name required');
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
256
288
|
listIndexes(args[1]);
|
|
257
289
|
break;
|
|
258
290
|
|
|
259
|
-
case 'db:analyze':
|
|
260
|
-
if (!args[1]) return console.error('Table name required');
|
|
261
|
-
analyzeTable(args[1]);
|
|
262
|
-
break;
|
|
263
|
-
|
|
264
|
-
case 'db:reindex':
|
|
265
|
-
if (!args[1]) return console.error('Table name required');
|
|
266
|
-
reindexTable(args[1]);
|
|
267
|
-
break;
|
|
268
|
-
|
|
269
291
|
case 'db:vacuum':
|
|
270
292
|
vacuumDatabase();
|
|
271
293
|
break;
|
|
@@ -275,18 +297,49 @@ switch (command) {
|
|
|
275
297
|
break;
|
|
276
298
|
|
|
277
299
|
case 'db:kill-connections':
|
|
278
|
-
if (readlineSync.keyInYN('⚠️ Kill all other connections?')) {
|
|
300
|
+
if (readlineSync.keyInYN('⚠️ Kill all other database connections?')) {
|
|
279
301
|
killConnections();
|
|
302
|
+
} else {
|
|
303
|
+
console.log('Cancelled.');
|
|
280
304
|
}
|
|
281
305
|
break;
|
|
282
306
|
|
|
283
307
|
case 'db:drop-all-tables':
|
|
284
|
-
if (readlineSync.keyInYN('
|
|
285
|
-
|
|
308
|
+
if (readlineSync.keyInYN('⚠️⚠️⚠️ DROP ALL TABLES PERMANENTLY? Type YES to confirm:')) {
|
|
309
|
+
if (readlineSync.question('Type database name to confirm: ') === process.env.DB_NAME) {
|
|
310
|
+
dropAllTables();
|
|
311
|
+
} else {
|
|
312
|
+
console.log('Database name mismatch. Aborted.');
|
|
313
|
+
}
|
|
286
314
|
}
|
|
287
315
|
break;
|
|
288
316
|
|
|
317
|
+
case 'db:export':
|
|
318
|
+
if (!args[1] || !args[2]) {
|
|
319
|
+
console.error('Usage: mwala db:export <table> <output.csv>');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
exportTableToCsv(args[1], args[2]);
|
|
323
|
+
break;
|
|
324
|
+
|
|
325
|
+
case 'db:import':
|
|
326
|
+
if (!args[1] || !args[2]) {
|
|
327
|
+
console.error('Usage: mwala db:import <input.csv> <table>');
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
importCsvToTable(args[1], args[2]);
|
|
331
|
+
break;
|
|
332
|
+
|
|
333
|
+
case 'db:seed':
|
|
334
|
+
if (!args[1]) {
|
|
335
|
+
console.error('Usage: mwala db:seed <seedFile.mjs>');
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
seedDatabase(args[1]);
|
|
339
|
+
break;
|
|
340
|
+
|
|
289
341
|
default:
|
|
290
|
-
console.error(`Unknown command: ${command}
|
|
342
|
+
console.error(`Unknown command: ${command}`);
|
|
343
|
+
console.log('Run "mwala help" for available commands.');
|
|
291
344
|
process.exit(1);
|
|
292
345
|
}
|
package/package.json
CHANGED
package/utils/dbUtils.mjs
CHANGED
|
@@ -1,124 +1,150 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
3
4
|
import dotenv from 'dotenv';
|
|
4
5
|
import mysql from 'mysql2/promise';
|
|
5
6
|
import { Client } from 'pg';
|
|
6
7
|
import { MongoClient } from 'mongodb';
|
|
7
8
|
import sqlite3 from 'sqlite3';
|
|
8
|
-
import { execSync } from 'child_process';
|
|
9
9
|
import readlineSync from 'readline-sync';
|
|
10
10
|
|
|
11
11
|
dotenv.config();
|
|
12
12
|
|
|
13
|
-
const { DB_TYPE, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME } = process.env;
|
|
13
|
+
const { DB_TYPE, DB_HOST = 'localhost', DB_USER, DB_PASSWORD, DB_NAME } = process.env;
|
|
14
14
|
|
|
15
|
-
let
|
|
15
|
+
let conn = null;
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
17
|
+
const getConn = async () => {
|
|
18
|
+
if (conn) return conn;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
host: DB_HOST,
|
|
20
|
+
try {
|
|
21
|
+
if (DB_TYPE === 'mysql') {
|
|
22
|
+
conn = await mysql.createConnection({
|
|
23
|
+
host: DB_HOST,
|
|
24
|
+
user: DB_USER,
|
|
25
|
+
password: DB_PASSWORD,
|
|
26
|
+
database: DB_NAME
|
|
27
|
+
});
|
|
28
|
+
} else if (DB_TYPE === 'postgresql') {
|
|
29
|
+
conn = new Client({
|
|
30
|
+
host: DB_HOST,
|
|
31
|
+
user: DB_USER,
|
|
32
|
+
password: DB_PASSWORD,
|
|
33
|
+
database: DB_NAME
|
|
24
34
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
break;
|
|
35
|
+
await conn.connect();
|
|
36
|
+
} else if (DB_TYPE === 'sqlite') {
|
|
37
|
+
conn = new sqlite3.Database(`./${DB_NAME}.sqlite`);
|
|
38
|
+
} else if (DB_TYPE === 'mongodb') {
|
|
39
|
+
conn = await MongoClient.connect(`mongodb://${DB_HOST}:27017`);
|
|
40
|
+
conn = conn.db(DB_NAME);
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('Connection failed:', err.message);
|
|
44
|
+
process.exit(1);
|
|
36
45
|
}
|
|
37
|
-
|
|
46
|
+
|
|
47
|
+
return conn;
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
export const listTables = async () => {
|
|
41
|
-
const
|
|
51
|
+
const connection = await getConn();
|
|
42
52
|
if (DB_TYPE === 'mysql') {
|
|
43
|
-
const [rows] = await
|
|
44
|
-
console.log('Tables
|
|
53
|
+
const [rows] = await connection.query('SHOW TABLES');
|
|
54
|
+
console.log('Tables:\n', rows.map(r => Object.values(r)[0]).join('\n'));
|
|
45
55
|
} else if (DB_TYPE === 'postgresql') {
|
|
46
|
-
const res = await
|
|
47
|
-
console.log('Tables
|
|
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'));
|
|
48
58
|
} else if (DB_TYPE === 'sqlite') {
|
|
49
|
-
|
|
50
|
-
console.log('Tables
|
|
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'));
|
|
51
61
|
});
|
|
52
62
|
}
|
|
53
63
|
};
|
|
54
64
|
|
|
55
65
|
export const describeTable = async (table) => {
|
|
56
|
-
const
|
|
66
|
+
const connection = await getConn();
|
|
57
67
|
if (DB_TYPE === 'mysql') {
|
|
58
|
-
const [rows] = await
|
|
68
|
+
const [rows] = await connection.query(`DESCRIBE \`${table}\``);
|
|
59
69
|
console.table(rows);
|
|
60
70
|
} else if (DB_TYPE === 'postgresql') {
|
|
61
|
-
const res = await
|
|
62
|
-
SELECT column_name, data_type, is_nullable
|
|
63
|
-
FROM information_schema.columns
|
|
64
|
-
|
|
71
|
+
const res = await connection.query(`
|
|
72
|
+
SELECT column_name, data_type, is_nullable, column_default
|
|
73
|
+
FROM information_schema.columns WHERE table_name = $1
|
|
74
|
+
`, [table]);
|
|
65
75
|
console.table(res.rows);
|
|
66
76
|
}
|
|
67
77
|
};
|
|
68
78
|
|
|
79
|
+
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}`);
|
|
83
|
+
};
|
|
84
|
+
|
|
69
85
|
export const truncateTable = async (table) => {
|
|
70
|
-
const
|
|
71
|
-
await
|
|
72
|
-
console.log(
|
|
86
|
+
const connection = await getConn();
|
|
87
|
+
await connection.query(`TRUNCATE TABLE \`${table}\` RESTART IDENTITY CASCADE`);
|
|
88
|
+
console.log(`✅ ${table} truncated`);
|
|
73
89
|
};
|
|
74
90
|
|
|
75
91
|
export const renameTable = async (oldName, newName) => {
|
|
76
|
-
const
|
|
77
|
-
await
|
|
92
|
+
const connection = await getConn();
|
|
93
|
+
await connection.query(`ALTER TABLE \`${oldName}\` RENAME TO \`${newName}\``);
|
|
78
94
|
console.log(`Renamed ${oldName} → ${newName}`);
|
|
79
95
|
};
|
|
80
96
|
|
|
81
97
|
export const backupDatabase = () => {
|
|
82
|
-
const timestamp = new Date().toISOString().
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
99
|
+
const file = `backup-${DB_NAME}-${timestamp}.sql`;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
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') {
|
|
105
|
+
process.env.PGPASSWORD = DB_PASSWORD;
|
|
106
|
+
execSync(`pg_dump -h ${DB_HOST} -U ${DB_USER} ${DB_NAME} > ${file}`);
|
|
107
|
+
}
|
|
108
|
+
console.log(`✅ Backup saved: ${file}`);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error('Backup failed:', err.message);
|
|
89
111
|
}
|
|
90
|
-
console.log(`Backup saved: ${backupFile}`);
|
|
91
112
|
};
|
|
92
113
|
|
|
93
114
|
export const restoreDatabase = (file) => {
|
|
94
|
-
if (!fs.existsSync(file))
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
if (!fs.existsSync(file)) {
|
|
116
|
+
console.error('Backup file not found');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
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') {
|
|
123
|
+
process.env.PGPASSWORD = DB_PASSWORD;
|
|
124
|
+
execSync(`psql -h ${DB_HOST} -U ${DB_USER} -d ${DB_NAME} -f ${file}`);
|
|
125
|
+
}
|
|
126
|
+
console.log('✅ Database restored');
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('Restore failed:', err.message);
|
|
100
129
|
}
|
|
101
|
-
console.log('Database restored');
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Add more functions similarly: exportTableToCsv, countRows, etc.
|
|
105
|
-
// (You can expand this file further as needed)
|
|
106
|
-
|
|
107
|
-
export const showDatabaseSize = () => {
|
|
108
|
-
console.log(`Size estimation not implemented for ${DB_TYPE} yet.`);
|
|
109
130
|
};
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
export const seedDatabase = (file) => console.log(`Seeding from ${file} not yet implemented`);
|
|
132
|
+
export const showDatabaseSize = () => console.log(`Size info not implemented for ${DB_TYPE} yet`);
|
|
113
133
|
export const listIndexes = () => console.log('Indexes list coming soon');
|
|
114
|
-
export const vacuumDatabase = () => console.log('VACUUM
|
|
115
|
-
export const
|
|
116
|
-
export const
|
|
117
|
-
export const
|
|
118
|
-
export const
|
|
119
|
-
export const
|
|
120
|
-
export const
|
|
121
|
-
export const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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');
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
console.error('Seed file not found');
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
export const checkTableExists = () => console.log('Check exists not implemented');
|