postgresai 0.11.0-alpha.4 → 0.11.0-alpha.6
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/postgres-ai.js +177 -4
- package/package.json +1 -1
package/bin/postgres-ai.js
CHANGED
|
@@ -123,7 +123,52 @@ program
|
|
|
123
123
|
const code = await runCompose(args);
|
|
124
124
|
if (code !== 0) process.exitCode = code;
|
|
125
125
|
});
|
|
126
|
-
program
|
|
126
|
+
program
|
|
127
|
+
.command("health")
|
|
128
|
+
.description("health check")
|
|
129
|
+
.action(async () => {
|
|
130
|
+
const { exec } = require("child_process");
|
|
131
|
+
const util = require("util");
|
|
132
|
+
const execPromise = util.promisify(exec);
|
|
133
|
+
|
|
134
|
+
console.log("Checking service health...\n");
|
|
135
|
+
|
|
136
|
+
const services = [
|
|
137
|
+
{ name: "Grafana", url: "http://localhost:3000/api/health" },
|
|
138
|
+
{ name: "Prometheus", url: "http://localhost:59090/-/healthy" },
|
|
139
|
+
{ name: "PGWatch (Postgres)", url: "http://localhost:58080/health" },
|
|
140
|
+
{ name: "PGWatch (Prometheus)", url: "http://localhost:58089/health" },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
let allHealthy = true;
|
|
144
|
+
|
|
145
|
+
for (const service of services) {
|
|
146
|
+
try {
|
|
147
|
+
const { stdout, stderr } = await execPromise(
|
|
148
|
+
`curl -sf -o /dev/null -w "%{http_code}" ${service.url}`,
|
|
149
|
+
{ timeout: 5000 }
|
|
150
|
+
);
|
|
151
|
+
const code = stdout.trim();
|
|
152
|
+
if (code === "200") {
|
|
153
|
+
console.log(`✓ ${service.name}: healthy`);
|
|
154
|
+
} else {
|
|
155
|
+
console.log(`✗ ${service.name}: unhealthy (HTTP ${code})`);
|
|
156
|
+
allHealthy = false;
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.log(`✗ ${service.name}: unreachable`);
|
|
160
|
+
allHealthy = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log("");
|
|
165
|
+
if (allHealthy) {
|
|
166
|
+
console.log("All services are healthy");
|
|
167
|
+
} else {
|
|
168
|
+
console.log("Some services are unhealthy");
|
|
169
|
+
process.exitCode = 1;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
127
172
|
program
|
|
128
173
|
.command("config")
|
|
129
174
|
.description("show configuration")
|
|
@@ -151,7 +196,44 @@ program
|
|
|
151
196
|
.command("reset [service]")
|
|
152
197
|
.description("reset all or specific service")
|
|
153
198
|
.action(stub("reset"));
|
|
154
|
-
program
|
|
199
|
+
program
|
|
200
|
+
.command("clean")
|
|
201
|
+
.description("cleanup artifacts")
|
|
202
|
+
.action(async () => {
|
|
203
|
+
const { exec } = require("child_process");
|
|
204
|
+
const util = require("util");
|
|
205
|
+
const execPromise = util.promisify(exec);
|
|
206
|
+
|
|
207
|
+
console.log("Cleaning up Docker resources...\n");
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
// Remove stopped containers
|
|
211
|
+
const { stdout: containers } = await execPromise("docker ps -aq --filter 'status=exited'");
|
|
212
|
+
if (containers.trim()) {
|
|
213
|
+
await execPromise(`docker rm ${containers.trim().split('\n').join(' ')}`);
|
|
214
|
+
console.log("✓ Removed stopped containers");
|
|
215
|
+
} else {
|
|
216
|
+
console.log("✓ No stopped containers to remove");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Remove unused volumes
|
|
220
|
+
const { stdout: volumeOut } = await execPromise("docker volume prune -f");
|
|
221
|
+
console.log("✓ Removed unused volumes");
|
|
222
|
+
|
|
223
|
+
// Remove unused networks
|
|
224
|
+
const { stdout: networkOut } = await execPromise("docker network prune -f");
|
|
225
|
+
console.log("✓ Removed unused networks");
|
|
226
|
+
|
|
227
|
+
// Remove dangling images
|
|
228
|
+
const { stdout: imageOut } = await execPromise("docker image prune -f");
|
|
229
|
+
console.log("✓ Removed dangling images");
|
|
230
|
+
|
|
231
|
+
console.log("\nCleanup completed");
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(`Error during cleanup: ${error.message}`);
|
|
234
|
+
process.exitCode = 1;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
155
237
|
program
|
|
156
238
|
.command("shell <service>")
|
|
157
239
|
.description("open service shell")
|
|
@@ -285,7 +367,69 @@ program
|
|
|
285
367
|
program
|
|
286
368
|
.command("test-instance <name>")
|
|
287
369
|
.description("test instance connectivity")
|
|
288
|
-
.action(
|
|
370
|
+
.action(async (name) => {
|
|
371
|
+
const fs = require("fs");
|
|
372
|
+
const path = require("path");
|
|
373
|
+
const { exec } = require("child_process");
|
|
374
|
+
const util = require("util");
|
|
375
|
+
const execPromise = util.promisify(exec);
|
|
376
|
+
|
|
377
|
+
const instancesPath = path.resolve(process.cwd(), "instances.yml");
|
|
378
|
+
if (!fs.existsSync(instancesPath)) {
|
|
379
|
+
console.error("instances.yml not found");
|
|
380
|
+
process.exitCode = 1;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const content = fs.readFileSync(instancesPath, "utf8");
|
|
385
|
+
const lines = content.split(/\r?\n/);
|
|
386
|
+
let connStr = "";
|
|
387
|
+
let foundInstance = false;
|
|
388
|
+
|
|
389
|
+
for (let i = 0; i < lines.length; i++) {
|
|
390
|
+
const nameLine = lines[i].match(/^-[\t ]*name:[\t ]*(.+)$/);
|
|
391
|
+
if (nameLine && nameLine[1].trim() === name) {
|
|
392
|
+
foundInstance = true;
|
|
393
|
+
// Look for conn_str in next lines
|
|
394
|
+
for (let j = i + 1; j < lines.length && j < i + 15; j++) {
|
|
395
|
+
const connLine = lines[j].match(/^[\t ]*conn_str:[\t ]*(.+)$/);
|
|
396
|
+
if (connLine) {
|
|
397
|
+
connStr = connLine[1].trim();
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
// Stop at next instance
|
|
401
|
+
if (lines[j].match(/^-[\t ]*name:/)) break;
|
|
402
|
+
}
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!foundInstance) {
|
|
408
|
+
console.error(`Instance '${name}' not found`);
|
|
409
|
+
process.exitCode = 1;
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (!connStr) {
|
|
414
|
+
console.error(`Connection string not found for instance '${name}'`);
|
|
415
|
+
process.exitCode = 1;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
console.log(`Testing connection to '${name}'...`);
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const { stdout, stderr } = await execPromise(
|
|
423
|
+
`psql "${connStr}" -c "SELECT version();" --no-psqlrc`,
|
|
424
|
+
{ timeout: 10000, env: { ...process.env, PAGER: 'cat' } }
|
|
425
|
+
);
|
|
426
|
+
console.log(`✓ Connection successful`);
|
|
427
|
+
console.log(stdout.trim());
|
|
428
|
+
} catch (error) {
|
|
429
|
+
console.error(`✗ Connection failed: ${error.message}`);
|
|
430
|
+
process.exitCode = 1;
|
|
431
|
+
}
|
|
432
|
+
});
|
|
289
433
|
|
|
290
434
|
// API key and grafana
|
|
291
435
|
program
|
|
@@ -359,7 +503,36 @@ program
|
|
|
359
503
|
program
|
|
360
504
|
.command("show-grafana-credentials")
|
|
361
505
|
.description("show Grafana credentials")
|
|
362
|
-
.action(
|
|
506
|
+
.action(async () => {
|
|
507
|
+
const fs = require("fs");
|
|
508
|
+
const path = require("path");
|
|
509
|
+
const cfgPath = path.resolve(process.cwd(), ".pgwatch-config");
|
|
510
|
+
if (!fs.existsSync(cfgPath)) {
|
|
511
|
+
console.error("Configuration file not found. Run 'quickstart' first.");
|
|
512
|
+
process.exitCode = 1;
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const content = fs.readFileSync(cfgPath, "utf8");
|
|
516
|
+
const lines = content.split(/\r?\n/);
|
|
517
|
+
let password = "";
|
|
518
|
+
for (const line of lines) {
|
|
519
|
+
const m = line.match(/^grafana_password=(.+)$/);
|
|
520
|
+
if (m) {
|
|
521
|
+
password = m[1].trim();
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (!password) {
|
|
526
|
+
console.error("Grafana password not found in configuration");
|
|
527
|
+
process.exitCode = 1;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
console.log("\nGrafana credentials:");
|
|
531
|
+
console.log(" URL: http://localhost:3000");
|
|
532
|
+
console.log(" Username: monitor");
|
|
533
|
+
console.log(` Password: ${password}`);
|
|
534
|
+
console.log("");
|
|
535
|
+
});
|
|
363
536
|
|
|
364
537
|
program.parseAsync(process.argv);
|
|
365
538
|
|