mwalajs 1.1.16 → 1.1.18

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 HEKIMA MWALA
3
+ Copyright (c) 2026 HEKIMA MWALA
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/bin/mwala.mjs CHANGED
@@ -39,7 +39,24 @@ const __dirname = path.dirname(__filename);
39
39
  // ────────────────────────────────────────────────
40
40
 
41
41
  let imports = {};
42
- let setupMwalajs, createProject, dropAllTables, getDbConnection;
42
+ // let setupMwalajs, createProject, dropAllTables, getDbConnection;
43
+ let {
44
+ setupMwalajs,
45
+ createProject,
46
+ dropAllTables,
47
+ getDbConnection,
48
+ listTables,
49
+ createTable,
50
+ dropTable,
51
+ migrateAll,
52
+ rollbackLastMigration,
53
+ showDatabaseSize,
54
+ listIndexes,
55
+ analyzeTable,
56
+ vacuumDatabase,
57
+ showConnections,
58
+ killConnections,
59
+ } = imports;
43
60
 
44
61
  try {
45
62
  const [
@@ -48,6 +65,7 @@ try {
48
65
  setup,
49
66
  proj,
50
67
  dbUtilsRaw,
68
+ maintenance,
51
69
  ] = await Promise.all([
52
70
  import(pathToFileURL(path.join(__dirname, '../config/createdatabase.mjs')).href),
53
71
  import(pathToFileURL(path.join(__dirname, '../runMigrations.mjs')).href),
@@ -64,16 +82,31 @@ try {
64
82
  ...normalize(setup),
65
83
  ...normalize(proj),
66
84
  ...normalize(dbUtilsRaw),
85
+ ...normalize(maintenance), // ← important
67
86
  };
68
87
 
69
88
  // 🔥 IMPORTANT FIX
70
- ({
71
- setupMwalajs,
72
- createProject,
73
- dropAllTables,
74
- getDbConnection
75
- } = imports);
76
-
89
+ ({
90
+ setupMwalajs,
91
+ createProject,
92
+ dropAllTables,
93
+ getDbConnection,
94
+ listTables,
95
+ createTable,
96
+ dropTable,
97
+ migrateAll,
98
+ rollbackLastMigration,
99
+ // ongeza hizi zingine ukizihitaji baadaye
100
+ showDatabaseSize,
101
+ listIndexes,
102
+ analyzeTable,
103
+ vacuumDatabase,
104
+ showConnections,
105
+ killConnections,
106
+ // n.k.
107
+ } = imports);
108
+
109
+
77
110
  } catch (err) {
78
111
  error(`Failed to load required modules:\n${err.stack || err.message}`);
79
112
  process.exit(1);
@@ -596,38 +629,67 @@ case 'db:restore': {
596
629
  }
597
630
 
598
631
 
632
+ // case 'db:size':
633
+ // await runSafe(showDatabaseSize, 'Database size shown');
634
+ // break;
635
+
636
+ // case 'db:indexes':
637
+ // if (!args[1]) return error('Table name required');
638
+ // await runSafe(() => listIndexes(args[1]), `Indexes for ${args[1]}`);
639
+ // break;
640
+
641
+ // case 'db:analyze':
642
+ // if (!args[1]) return error('Table name required');
643
+ // await runSafe(() => analyzeTable(args[1]), `Table ${args[1]} analyzed`);
644
+ // break;
645
+
646
+ // case 'db:reindex':
647
+ // if (!args[1]) return error('Table name required');
648
+ // await runSafe(() => reindexTable(args[1]), `Table ${args[1]} reindexed`);
649
+ // break;
650
+
651
+ // case 'db:vacuum':
652
+ // await runSafe(vacuumDatabase, 'Database vacuumed');
653
+ // break;
654
+
655
+ // case 'db:connections':
656
+ // await runSafe(showConnections, 'Active connections shown');
657
+ // break;
658
+
659
+
599
660
  case 'db:size':
600
- await runSafe(showDatabaseSize, 'Database size shown');
601
- break;
661
+ await runSafe(() => imports.showDatabaseSize(), 'Database size shown');
662
+ break;
602
663
 
603
- case 'db:indexes':
604
- if (!args[1]) return error('Table name required');
605
- await runSafe(() => listIndexes(args[1]), `Indexes for ${args[1]}`);
606
- break;
664
+ case 'db:indexes':
665
+ if (!args[1]) return error('Table name required: mwala db:indexes <table>');
666
+ await runSafe(() => imports.listIndexes(args[1]), `Indexes for ${args[1]}`);
667
+ break;
607
668
 
608
- case 'db:analyze':
609
- if (!args[1]) return error('Table name required');
610
- await runSafe(() => analyzeTable(args[1]), `Table ${args[1]} analyzed`);
611
- break;
669
+ case 'db:analyze':
670
+ if (!args[1]) return error('Table name required');
671
+ await runSafe(() => imports.analyzeTable(args[1]), `Table ${args[1]} analyzed`);
672
+ break;
612
673
 
613
- case 'db:reindex':
614
- if (!args[1]) return error('Table name required');
615
- await runSafe(() => reindexTable(args[1]), `Table ${args[1]} reindexed`);
616
- break;
674
+ case 'db:vacuum':
675
+ await runSafe(() => imports.vacuumDatabase(), 'Database vacuumed');
676
+ break;
617
677
 
618
- case 'db:vacuum':
619
- await runSafe(vacuumDatabase, 'Database vacuumed');
620
- break;
678
+ case 'db:connections':
679
+ await runSafe(() => imports.showConnections(), 'Active connections shown');
680
+ break;
621
681
 
622
- case 'db:connections':
623
- await runSafe(showConnections, 'Active connections shown');
624
- break;
682
+ case 'db:kill-connections':
683
+ if (readlineSync.keyInYNStrict('⚠️ Kill ALL other database connections? (hatari!)')) {
684
+ await runSafe(() => imports.killConnections(), 'Other connections killed');
685
+ }
686
+ break;
625
687
 
626
- case 'db:kill-connections':
627
- if (readlineSync.keyInYNStrict('⚠️ Kill ALL other database connections?')) {
628
- await runSafe(killConnections, 'Other connections killed');
629
- }
630
- break;
688
+ // case 'db:kill-connections':
689
+ // if (readlineSync.keyInYNStrict('⚠️ Kill ALL other database connections?')) {
690
+ // await runSafe(killConnections, 'Other connections killed');
691
+ // }
692
+ // break;
631
693
 
632
694
  case 'db:drop-all-tables':
633
695
  if (readlineSync.keyInYNStrict('⚠️⚠️ THIS WILL DROP **ALL** TABLES! Continue?')) {
package/createProject.mjs CHANGED
@@ -1,44 +1,67 @@
1
- // createProject.mjs
1
+ // createProject.mjs (ULTIMATE CROSS-PLATFORM VERSION)
2
+
2
3
  import fs from "fs-extra";
3
4
  import path from "path";
4
5
  import readline from "readline";
5
6
  import os from "os";
7
+ import { fileURLToPath } from "url";
8
+
9
+ /* =========================
10
+ SAFE PATH HELPERS
11
+ ========================= */
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
6
15
 
7
16
  /**
8
- * Get template source safely
17
+ * Detect best possible MwalaJS template path
18
+ * Works on ALL OS
9
19
  */
10
20
  function getMwalajsPath() {
11
21
  const envPath = process.env.MWALAJSPATH;
12
22
 
13
- const defaultPaths = [
23
+ const possiblePaths = [
14
24
  envPath,
25
+ path.join(process.cwd(), "template"),
26
+ path.join(__dirname, "template"),
27
+ path.join(__dirname, "../template"),
28
+ path.join(__dirname, "../../template"),
15
29
  "C:\\Program Files\\mwalajs",
30
+ "C:\\mwalajs",
16
31
  "/usr/local/lib/mwalajs",
32
+ "/usr/lib/mwalajs",
17
33
  "/var/www/mwalajs",
18
- path.join(process.cwd(), "template") // fallback local dev template
34
+ "/opt/mwalajs"
19
35
  ];
20
36
 
21
- for (const p of defaultPaths) {
22
- if (p && fs.existsSync(p)) return p;
37
+ for (const p of possiblePaths) {
38
+ if (p && fs.existsSync(p)) {
39
+ return fs.realpathSync(p);
40
+ }
23
41
  }
24
42
 
25
- return null; // IMPORTANT: we will generate manual scaffold
43
+ return null;
26
44
  }
27
45
 
28
- /**
29
- * Ask CLI input
30
- */
31
- function ask(rl, q) {
32
- return new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
46
+ /* =========================
47
+ CLI INPUT
48
+ ========================= */
49
+
50
+ function ask(rl, question) {
51
+ return new Promise((resolve) => {
52
+ rl.question(question, (ans) => resolve(ans.trim()));
53
+ });
33
54
  }
34
55
 
35
- /**
36
- * Manual fallback template generator (VERY IMPORTANT FIX)
37
- */
56
+ /* =========================
57
+ MANUAL TEMPLATE GENERATOR
58
+ (ALWAYS WORKS - FALLBACK)
59
+ ========================= */
60
+
38
61
  function createManualTemplate(target) {
39
- console.log("⚠ No template found. Creating manual MwalaJS scaffold...");
62
+ console.log("\n⚠ No template found. Generating FULL MwalaJS scaffold...\n");
40
63
 
41
- const structure = [
64
+ const dirs = [
42
65
  "controllers",
43
66
  "routes",
44
67
  "models",
@@ -47,16 +70,23 @@ function createManualTemplate(target) {
47
70
  "views/pages",
48
71
  "middlewares",
49
72
  "migrations",
73
+ "config",
74
+ "public",
50
75
  "public/css",
51
76
  "public/js",
52
- "public/images"
77
+ "public/images",
78
+ "storage",
79
+ "logs"
53
80
  ];
54
81
 
55
- structure.forEach((dir) => {
82
+ dirs.forEach((dir) => {
56
83
  fs.mkdirSync(path.join(target, dir), { recursive: true });
57
84
  });
58
85
 
59
- // app.mjs
86
+ /* =========================
87
+ APP ENTRY
88
+ ========================= */
89
+
60
90
  fs.writeFileSync(
61
91
  path.join(target, "app.mjs"),
62
92
  `import mwalajs from 'mwalajs';
@@ -68,30 +98,43 @@ const __dirname = path.dirname(__filename);
68
98
 
69
99
  mwalajs.set('view engine', 'ejs');
70
100
  mwalajs.set('views', path.join(__dirname, 'views'));
101
+
71
102
  mwalajs.useStatic(path.join(__dirname, 'public'));
72
103
 
104
+ /* ROUTES */
73
105
  mwalajs.get('/', (req, res) => {
74
- res.render('pages/index', { title: 'MwalaJS App' });
106
+ res.render('pages/index', {
107
+ title: '🚀 MwalaJS Application Running'
108
+ });
75
109
  });
76
110
 
77
- const port = process.env.PORT || 3000;
78
- mwalajs.listen(port, () => {
79
- console.log('🚀 Server running on http://localhost:' + port);
111
+ /* SERVER */
112
+ const PORT = process.env.PORT || 3000;
113
+ mwalajs.listen(PORT, () => {
114
+ console.log(\`🚀 Server running on http://localhost:\${PORT}\`);
80
115
  });
81
116
  `
82
117
  );
83
118
 
84
- // sample controller
119
+ /* =========================
120
+ CONTROLLER
121
+ ========================= */
122
+
85
123
  fs.writeFileSync(
86
124
  path.join(target, "controllers/homeController.mjs"),
87
125
  `export const homeController = {
88
- getHome: (req, res) => {
89
- res.render('pages/index', { title: 'Home Page' });
126
+ index: (req, res) => {
127
+ res.render('pages/index', {
128
+ title: 'Home Page'
129
+ });
90
130
  }
91
131
  };`
92
132
  );
93
133
 
94
- // sample route
134
+ /* =========================
135
+ ROUTES
136
+ ========================= */
137
+
95
138
  fs.writeFileSync(
96
139
  path.join(target, "routes/homeRoutes.mjs"),
97
140
  `import mwalajs from 'mwalajs';
@@ -99,70 +142,127 @@ import { homeController } from '../controllers/homeController.mjs';
99
142
 
100
143
  const router = mwalajs.Router();
101
144
 
102
- router.get('/', homeController.getHome);
145
+ router.get('/', homeController.index);
103
146
 
104
147
  export { router as homeRoutes };
105
148
  `
106
149
  );
107
150
 
108
- // sample view
151
+ /* =========================
152
+ MODEL EXAMPLE
153
+ ========================= */
154
+
155
+ fs.writeFileSync(
156
+ path.join(target, "models/User.mjs"),
157
+ `export class User {
158
+ constructor() {
159
+ this.table = "users";
160
+ }
161
+
162
+ // Add model logic here
163
+ }
164
+ `
165
+ );
166
+
167
+ /* =========================
168
+ VIEW
169
+ ========================= */
170
+
109
171
  fs.writeFileSync(
110
172
  path.join(target, "views/pages/index.ejs"),
111
173
  `<!DOCTYPE html>
112
174
  <html>
113
175
  <head>
176
+ <meta charset="UTF-8" />
114
177
  <title><%= title %></title>
115
178
  <style>
116
- body { font-family: Arial; background:#0f172a; color:white; text-align:center; padding:50px; }
117
- .card { background:#1e293b; padding:20px; border-radius:12px; display:inline-block; }
179
+ body {
180
+ font-family: Arial;
181
+ background: linear-gradient(135deg,#0f172a,#1e293b);
182
+ color: white;
183
+ text-align: center;
184
+ padding: 60px;
185
+ }
186
+
187
+ .box {
188
+ background: rgba(255,255,255,0.05);
189
+ padding: 30px;
190
+ border-radius: 16px;
191
+ display: inline-block;
192
+ box-shadow: 0 10px 30px rgba(0,0,0,0.3);
193
+ }
194
+
195
+ h1 {
196
+ font-size: 40px;
197
+ }
118
198
  </style>
119
199
  </head>
120
200
  <body>
121
- <div class="card">
122
- <h1>🚀 Welcome to MwalaJS</h1>
201
+ <div class="box">
202
+ <h1>🚀 MwalaJS Ready</h1>
123
203
  <p><%= title %></p>
124
204
  </div>
125
205
  </body>
126
206
  </html>`
127
207
  );
128
208
 
129
- // package.json
209
+ /* =========================
210
+ PACKAGE JSON
211
+ ========================= */
212
+
130
213
  fs.writeFileSync(
131
214
  path.join(target, "package.json"),
132
215
  `{
133
216
  "name": "mwalajs-app",
217
+ "version": "1.0.0",
134
218
  "type": "module",
135
219
  "scripts": {
136
220
  "start": "node app.mjs"
137
221
  },
138
222
  "dependencies": {
139
- "mwalajs": "*"
223
+ "mwalajs": "*",
224
+ "ejs": "*"
140
225
  }
141
226
  }`
142
227
  );
143
228
 
229
+ /* =========================
230
+ README
231
+ ========================= */
232
+
144
233
  fs.writeFileSync(
145
234
  path.join(target, "README.md"),
146
- `# MwalaJS App
235
+ `# 🚀 MwalaJS Project
236
+
237
+ ## Run project
147
238
 
148
- Run:
149
239
  npm install
150
240
  npm start
241
+
242
+ ## Structure
243
+ - MVC architecture
244
+ - Controllers
245
+ - Routes
246
+ - Models
247
+ - Views
151
248
  `
152
249
  );
153
250
 
154
- console.log("✅ Manual template created successfully!");
251
+ console.log("✅ FULL manual scaffold created successfully!");
155
252
  }
156
253
 
157
- /**
158
- * Main project creator
159
- */
254
+ /* =========================
255
+ MAIN PROJECT CREATOR
256
+ ========================= */
257
+
160
258
  export async function createProject(projectArg) {
161
259
  const rl = readline.createInterface({
162
260
  input: process.stdin,
163
261
  output: process.stdout
164
262
  });
165
263
 
264
+ let createdMode = "unknown";
265
+
166
266
  try {
167
267
  let projectName = projectArg?.trim();
168
268
 
@@ -171,7 +271,7 @@ export async function createProject(projectArg) {
171
271
  }
172
272
 
173
273
  if (!projectName) {
174
- console.log("❌ Project name required");
274
+ console.log("❌ Project name is required");
175
275
  return;
176
276
  }
177
277
 
@@ -186,10 +286,12 @@ export async function createProject(projectArg) {
186
286
 
187
287
  const templatePath = getMwalajsPath();
188
288
 
189
- if (!templatePath) {
190
- createManualTemplate(target);
191
- } else {
192
- console.log("📦 Using template from:", templatePath);
289
+ /* =========================
290
+ TEMPLATE MODE
291
+ ========================= */
292
+
293
+ if (templatePath) {
294
+ console.log("📦 Template found:", templatePath);
193
295
 
194
296
  const items = [
195
297
  "controllers",
@@ -203,6 +305,8 @@ export async function createProject(projectArg) {
203
305
  "README.md"
204
306
  ];
205
307
 
308
+ let copied = 0;
309
+
206
310
  for (const item of items) {
207
311
  const src = path.join(templatePath, item);
208
312
  const dest = path.join(target, item);
@@ -210,19 +314,39 @@ export async function createProject(projectArg) {
210
314
  if (fs.existsSync(src)) {
211
315
  fs.copySync(src, dest);
212
316
  console.log("✔ copied:", item);
317
+ copied++;
213
318
  } else {
214
319
  console.log("⚠ missing:", item);
215
320
  }
216
321
  }
217
322
 
218
- // fallback if empty project
219
- if (!fs.existsSync(path.join(target, "app.mjs"))) {
323
+ if (copied === 0) {
324
+ console.log("⚠ Template empty → switching to manual mode...");
220
325
  createManualTemplate(target);
326
+ createdMode = "manual";
327
+ } else {
328
+ createdMode = "template";
221
329
  }
330
+
331
+ } else {
332
+ /* =========================
333
+ MANUAL MODE
334
+ ========================= */
335
+
336
+ createManualTemplate(target);
337
+ createdMode = "manual";
222
338
  }
223
339
 
224
- console.log("\n🎉 Project created successfully!");
340
+ /* =========================
341
+ FINAL OUTPUT (NO FAKE SUCCESS)
342
+ ========================= */
343
+
344
+ console.log("\n============================");
345
+ console.log("🎉 PROJECT CREATED SUCCESSFULLY");
346
+ console.log("============================");
225
347
  console.log("📁 Path:", target);
348
+ console.log("⚙ Mode:", createdMode.toUpperCase());
349
+ console.log("============================\n");
226
350
 
227
351
  } catch (err) {
228
352
  console.error("❌ Create project failed:", err.message);
@@ -231,7 +355,10 @@ export async function createProject(projectArg) {
231
355
  }
232
356
  }
233
357
 
234
- // CLI support
358
+ /* =========================
359
+ CLI EXECUTION
360
+ ========================= */
361
+
235
362
  if (import.meta.url === `file://${process.argv[1]}`) {
236
363
  createProject(process.argv[2]);
237
364
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mwalajs",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
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/runMigrations.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ // runMigrations.mjs
2
+
1
3
  import fs from 'fs';
2
4
  import path from 'path';
3
5
  import readline from 'readline';
@@ -134,6 +136,38 @@ export const listTables = async () => {
134
136
  }
135
137
  };
136
138
 
139
+
140
+ export const dropAllTables = async () => {
141
+ try {
142
+ const tables = await sequelize.getQueryInterface().showAllTables();
143
+
144
+ if (tables.length === 0) {
145
+ console.log('No tables exist in the database.');
146
+ return;
147
+ }
148
+
149
+ console.log(`Dropping ${tables.length} table(s): ${tables.join(', ')}`);
150
+
151
+ // Drop in reverse order to respect foreign key dependencies if any
152
+ for (const table of tables.reverse()) {
153
+ await sequelize.getQueryInterface().dropTable(table, { cascade: true });
154
+ console.log(` Dropped: ${table}`);
155
+ }
156
+
157
+ // Clear the migration tracking file so future migrates start clean
158
+ fs.writeFileSync(migrationLog, JSON.stringify([]));
159
+ console.log('Migration log cleared – ready for fresh migrations.');
160
+
161
+ } catch (error) {
162
+ console.error('Error while dropping all tables:', error.message);
163
+ if (error.stack) console.error(error.stack);
164
+ throw error; // let runSafe show the full error
165
+ }
166
+ };
167
+
168
+
169
+
170
+
137
171
  const askUser = (question) => {
138
172
  return new Promise((resolve) => {
139
173
  const rl = readline.createInterface({
@@ -146,3 +180,112 @@ const askUser = (question) => {
146
180
  });
147
181
  });
148
182
  };
183
+
184
+
185
+
186
+ // ────────────────────────────────────────────────
187
+ // MAINTENANCE TOOLS (MariaDB / MySQL compatible)
188
+ // ────────────────────────────────────────────────
189
+
190
+ export const showDatabaseSize = async () => {
191
+ const dbName = sequelize.getDatabaseName(); // au tumia sequelize.config.database kama haifanyi kazi
192
+ const [results] = await sequelize.query(`
193
+ SELECT
194
+ ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
195
+ FROM information_schema.tables
196
+ WHERE table_schema = '${dbName}'
197
+ `);
198
+
199
+ const size = results[0]?.size_mb || '0.00';
200
+ console.log(`Database size: ${size} MB`);
201
+ };
202
+
203
+ export const listIndexes = async (tableName) => {
204
+ const dbName = sequelize.getDatabaseName();
205
+ const [results] = await sequelize.query(`
206
+ SHOW INDEXES FROM \`${tableName}\`
207
+ `);
208
+
209
+ if (results.length === 0) {
210
+ console.log(`Hakuna indexes kwenye table "${tableName}".`);
211
+ return;
212
+ }
213
+
214
+ console.log(`Indexes kwenye "${tableName}":`);
215
+ results.forEach(row => {
216
+ const unique = row.Non_unique === 0 ? 'UNIQUE' : 'NON-UNIQUE';
217
+ console.log(` • ${row.Key_name.padEnd(35)} → ${unique} | Column: ${row.Column_name} | Type: ${row.Index_type}`);
218
+ });
219
+ };
220
+
221
+ export const analyzeTable = async (tableName) => {
222
+ await sequelize.query(`ANALYZE TABLE \`${tableName}\`;`);
223
+ console.log(`Table "${tableName}" ime-analyze (statistics zime-update).`);
224
+ };
225
+
226
+ export const vacuumDatabase = async () => {
227
+ // MySQL/MariaDB haina VACUUM kama PostgreSQL, lakini tunaweza optimize tables zote
228
+ const dbName = sequelize.getDatabaseName();
229
+ const [tables] = await sequelize.query(`
230
+ SELECT table_name
231
+ FROM information_schema.tables
232
+ WHERE table_schema = '${dbName}'
233
+ AND table_type = 'BASE TABLE'
234
+ `);
235
+
236
+ if (tables.length === 0) {
237
+ console.log('Hakuna tables za ku-optimize.');
238
+ return;
239
+ }
240
+
241
+ console.log(`Optimizing ${tables.length} table(s)...`);
242
+ for (const row of tables) {
243
+ const table = row.table_name;
244
+ await sequelize.query(`OPTIMIZE TABLE \`${table}\`;`);
245
+ console.log(` Optimized: ${table}`);
246
+ }
247
+ console.log('Optimization imekamilika (space reclaimed na stats updated).');
248
+ };
249
+
250
+ export const showConnections = async () => {
251
+ const [results] = await sequelize.query(`
252
+ SHOW PROCESSLIST
253
+ `);
254
+
255
+ const active = results.filter(row => row.Command !== 'Sleep' && row.Id !== 0); // exclude idle + our connection
256
+
257
+ if (active.length === 0) {
258
+ console.log('Hakuna connections zingine active (isipokuwa yako).');
259
+ return;
260
+ }
261
+
262
+ console.log(`Active connections (${active.length}):`);
263
+ active.forEach(r => {
264
+ const querySnippet = r.Info ? r.Info.substring(0, 60) + '...' : '(idle)';
265
+ console.log(` ID ${r.Id} | User: ${r.User} | Host: ${r.Host} | State: ${r.State} | Query: ${querySnippet}`);
266
+ });
267
+ };
268
+
269
+ export const killConnections = async () => {
270
+ const [results] = await sequelize.query(`
271
+ SHOW PROCESSLIST
272
+ `);
273
+
274
+ const toKill = results.filter(row => row.Id !== 0 && row.Command !== 'Sleep'); // exclude our connection + idle
275
+
276
+ if (toKill.length === 0) {
277
+ console.log('Hakuna connections za ku-kill.');
278
+ return;
279
+ }
280
+
281
+ console.log(`Killing ${toKill.length} connection(s)...`);
282
+ for (const row of toKill) {
283
+ try {
284
+ await sequelize.query(`KILL ${row.Id};`);
285
+ console.log(` Killed ID ${row.Id}`);
286
+ } catch (e) {
287
+ console.warn(` Failed to kill ${row.Id}: ${e.message}`);
288
+ }
289
+ }
290
+ console.log('Kill operation imekamilika.');
291
+ };