@indiekitai/pg-dash 0.4.1 → 0.4.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.
- package/dist/cli.js +47 -41
- package/dist/cli.js.map +1 -1
- package/dist/mcp.js +2 -2
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -970,7 +970,7 @@ var init_schema = __esm({
|
|
|
970
970
|
});
|
|
971
971
|
|
|
972
972
|
// src/server/schema-diff.ts
|
|
973
|
-
function
|
|
973
|
+
function diffSchemaSnapshots(oldSnap, newSnap) {
|
|
974
974
|
const changes = [];
|
|
975
975
|
const oldTableMap = new Map(oldSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));
|
|
976
976
|
const newTableMap = new Map(newSnap.tables.map((t) => [`${t.schema}.${t.name}`, t]));
|
|
@@ -1105,12 +1105,13 @@ async function buildLiveSnapshot(pool) {
|
|
|
1105
1105
|
enums: enums.map((e) => ({ name: e.name, schema: e.schema, values: e.values }))
|
|
1106
1106
|
};
|
|
1107
1107
|
}
|
|
1108
|
-
var SchemaTracker;
|
|
1108
|
+
var SNAPSHOT_RETENTION, SchemaTracker;
|
|
1109
1109
|
var init_schema_tracker = __esm({
|
|
1110
1110
|
"src/server/schema-tracker.ts"() {
|
|
1111
1111
|
"use strict";
|
|
1112
1112
|
init_schema();
|
|
1113
1113
|
init_schema_diff();
|
|
1114
|
+
SNAPSHOT_RETENTION = 50;
|
|
1114
1115
|
SchemaTracker = class {
|
|
1115
1116
|
db;
|
|
1116
1117
|
pool;
|
|
@@ -1147,11 +1148,19 @@ var init_schema_tracker = __esm({
|
|
|
1147
1148
|
const json = JSON.stringify(snapshot);
|
|
1148
1149
|
const info = this.db.prepare("INSERT INTO schema_snapshots (timestamp, snapshot) VALUES (?, ?)").run(now, json);
|
|
1149
1150
|
const snapshotId = Number(info.lastInsertRowid);
|
|
1151
|
+
this.db.prepare(`
|
|
1152
|
+
DELETE FROM schema_snapshots
|
|
1153
|
+
WHERE id NOT IN (
|
|
1154
|
+
SELECT id FROM schema_snapshots
|
|
1155
|
+
ORDER BY timestamp DESC
|
|
1156
|
+
LIMIT ?
|
|
1157
|
+
)
|
|
1158
|
+
`).run(SNAPSHOT_RETENTION);
|
|
1150
1159
|
const prev = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id < ? ORDER BY id DESC LIMIT 1").get(snapshotId);
|
|
1151
1160
|
let changes = [];
|
|
1152
1161
|
if (prev) {
|
|
1153
1162
|
const oldSnap = JSON.parse(prev.snapshot);
|
|
1154
|
-
changes =
|
|
1163
|
+
changes = diffSchemaSnapshots(oldSnap, snapshot);
|
|
1155
1164
|
if (changes.length > 0) {
|
|
1156
1165
|
const insert = this.db.prepare("INSERT INTO schema_changes (snapshot_id, timestamp, change_type, object_type, table_name, detail) VALUES (?, ?, ?, ?, ?, ?)");
|
|
1157
1166
|
const tx = this.db.transaction((chs) => {
|
|
@@ -1198,7 +1207,7 @@ var init_schema_tracker = __esm({
|
|
|
1198
1207
|
const from = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id = ?").get(fromId);
|
|
1199
1208
|
const to = this.db.prepare("SELECT snapshot FROM schema_snapshots WHERE id = ?").get(toId);
|
|
1200
1209
|
if (!from || !to) return null;
|
|
1201
|
-
return
|
|
1210
|
+
return diffSchemaSnapshots(JSON.parse(from.snapshot), JSON.parse(to.snapshot));
|
|
1202
1211
|
}
|
|
1203
1212
|
};
|
|
1204
1213
|
}
|
|
@@ -1207,7 +1216,7 @@ var init_schema_tracker = __esm({
|
|
|
1207
1216
|
// src/server/snapshot.ts
|
|
1208
1217
|
var snapshot_exports = {};
|
|
1209
1218
|
__export(snapshot_exports, {
|
|
1210
|
-
diffSnapshots: () =>
|
|
1219
|
+
diffSnapshots: () => diffSnapshots,
|
|
1211
1220
|
loadSnapshot: () => loadSnapshot,
|
|
1212
1221
|
saveSnapshot: () => saveSnapshot
|
|
1213
1222
|
});
|
|
@@ -1229,7 +1238,7 @@ function loadSnapshot(snapshotPath) {
|
|
|
1229
1238
|
return null;
|
|
1230
1239
|
}
|
|
1231
1240
|
}
|
|
1232
|
-
function
|
|
1241
|
+
function diffSnapshots(prev, current) {
|
|
1233
1242
|
const prevNormIds = new Set(prev.issues.map((i) => normalizeIssueId(i.id)));
|
|
1234
1243
|
const currNormIds = new Set(current.issues.map((i) => normalizeIssueId(i.id)));
|
|
1235
1244
|
const newIssues = current.issues.filter((i) => !prevNormIds.has(normalizeIssueId(i.id)));
|
|
@@ -1739,7 +1748,7 @@ async function diffEnvironments(sourceConn, targetConn, options) {
|
|
|
1739
1748
|
const constraintDiffs = [];
|
|
1740
1749
|
const enumDiffs = [];
|
|
1741
1750
|
if (sourceSnap && targetSnap) {
|
|
1742
|
-
const snapChanges =
|
|
1751
|
+
const snapChanges = diffSchemaSnapshots(sourceSnap, targetSnap);
|
|
1743
1752
|
for (const c of snapChanges) {
|
|
1744
1753
|
if (c.object_type === "constraint") {
|
|
1745
1754
|
constraintDiffs.push({
|
|
@@ -2417,9 +2426,14 @@ var SEVERITY_COLORS = {
|
|
|
2417
2426
|
info: { hex: "#3498db", decimal: 3447003, emoji: "\u{1F535}" }
|
|
2418
2427
|
};
|
|
2419
2428
|
function detectWebhookType(url) {
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2429
|
+
try {
|
|
2430
|
+
const { hostname } = new URL(url);
|
|
2431
|
+
if (hostname.endsWith("hooks.slack.com")) return "slack";
|
|
2432
|
+
if (hostname.endsWith("discord.com") || hostname.endsWith("discordapp.com")) return "discord";
|
|
2433
|
+
return "unknown";
|
|
2434
|
+
} catch {
|
|
2435
|
+
return "unknown";
|
|
2436
|
+
}
|
|
2423
2437
|
}
|
|
2424
2438
|
function formatSlackMessage(alert, rule) {
|
|
2425
2439
|
const colors = SEVERITY_COLORS[rule.severity] || SEVERITY_COLORS.info;
|
|
@@ -3895,40 +3909,32 @@ async function startServer(opts) {
|
|
|
3895
3909
|
if (Object.keys(snapshot).length > 0) {
|
|
3896
3910
|
try {
|
|
3897
3911
|
const alertMetrics = {};
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
const r = await
|
|
3912
|
+
const alertClient = await pool.connect();
|
|
3913
|
+
try {
|
|
3914
|
+
if (snapshot.connections_total !== void 0) {
|
|
3915
|
+
const r = await alertClient.query("SELECT setting::int AS max FROM pg_settings WHERE name = 'max_connections'");
|
|
3902
3916
|
const max = r.rows[0]?.max || 100;
|
|
3903
3917
|
alertMetrics.connection_util = snapshot.connections_total / max * 100;
|
|
3904
|
-
} finally {
|
|
3905
|
-
client.release();
|
|
3906
3918
|
}
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;
|
|
3910
|
-
}
|
|
3911
|
-
try {
|
|
3912
|
-
const client = await pool.connect();
|
|
3913
|
-
try {
|
|
3914
|
-
const r = await client.query(`SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > $1 * interval '1 minute' AND pid != pg_backend_pid()`, [longQueryThreshold]);
|
|
3915
|
-
alertMetrics.long_query_count = r.rows[0]?.c || 0;
|
|
3916
|
-
} finally {
|
|
3917
|
-
client.release();
|
|
3919
|
+
if (snapshot.cache_hit_ratio !== void 0) {
|
|
3920
|
+
alertMetrics.cache_hit_pct = snapshot.cache_hit_ratio * 100;
|
|
3918
3921
|
}
|
|
3922
|
+
const [longQueriesResult, idleInTxResult] = await Promise.all([
|
|
3923
|
+
alertClient.query(
|
|
3924
|
+
`SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > $1 * interval '1 minute' AND pid != pg_backend_pid()`,
|
|
3925
|
+
[longQueryThreshold]
|
|
3926
|
+
),
|
|
3927
|
+
alertClient.query(
|
|
3928
|
+
`SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`,
|
|
3929
|
+
[longQueryThreshold]
|
|
3930
|
+
)
|
|
3931
|
+
]);
|
|
3932
|
+
alertMetrics.long_query_count = longQueriesResult.rows[0]?.c || 0;
|
|
3933
|
+
alertMetrics.idle_in_tx_count = idleInTxResult.rows[0]?.c || 0;
|
|
3919
3934
|
} catch (err) {
|
|
3920
|
-
console.error("[alerts] Error
|
|
3921
|
-
}
|
|
3922
|
-
|
|
3923
|
-
const client = await pool.connect();
|
|
3924
|
-
try {
|
|
3925
|
-
const r = await client.query(`SELECT count(*)::int AS c FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > $1 * interval '1 minute'`, [longQueryThreshold]);
|
|
3926
|
-
alertMetrics.idle_in_tx_count = r.rows[0]?.c || 0;
|
|
3927
|
-
} finally {
|
|
3928
|
-
client.release();
|
|
3929
|
-
}
|
|
3930
|
-
} catch (err) {
|
|
3931
|
-
console.error("[alerts] Error checking idle-in-tx:", err.message);
|
|
3935
|
+
console.error("[alerts] Error collecting alert metrics:", err.message);
|
|
3936
|
+
} finally {
|
|
3937
|
+
alertClient.release();
|
|
3932
3938
|
}
|
|
3933
3939
|
collectCycleCount++;
|
|
3934
3940
|
if (collectCycleCount % 10 === 0) {
|
|
@@ -4140,7 +4146,7 @@ if (subcommand === "check") {
|
|
|
4140
4146
|
const useDiff = values.diff || false;
|
|
4141
4147
|
const { Pool: Pool3 } = await import("pg");
|
|
4142
4148
|
const { getAdvisorReport: getAdvisorReport2 } = await Promise.resolve().then(() => (init_advisor(), advisor_exports));
|
|
4143
|
-
const { saveSnapshot: saveSnapshot2, loadSnapshot: loadSnapshot2, diffSnapshots:
|
|
4149
|
+
const { saveSnapshot: saveSnapshot2, loadSnapshot: loadSnapshot2, diffSnapshots: diffSnapshots2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
|
|
4144
4150
|
const os4 = await import("os");
|
|
4145
4151
|
const pool = new Pool3({ connectionString, connectionTimeoutMillis: 1e4 });
|
|
4146
4152
|
const checkDataDir = values["data-dir"] || path5.join(os4.homedir(), ".pg-dash");
|
|
@@ -4152,7 +4158,7 @@ if (subcommand === "check") {
|
|
|
4152
4158
|
if (useDiff) {
|
|
4153
4159
|
const prev = loadSnapshot2(snapshotPath);
|
|
4154
4160
|
if (prev) {
|
|
4155
|
-
diff =
|
|
4161
|
+
diff = diffSnapshots2(prev.result, report);
|
|
4156
4162
|
}
|
|
4157
4163
|
saveSnapshot2(snapshotPath, report);
|
|
4158
4164
|
}
|