@calltelemetry/cli 0.7.10 → 0.9.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/commands/appliance.d.ts.map +1 -1
- package/dist/commands/appliance.js +16 -1
- package/dist/commands/appliance.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +74 -1
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +149 -4
- package/dist/commands/update.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/bundle.d.ts +12 -0
- package/dist/lib/bundle.d.ts.map +1 -1
- package/dist/lib/bundle.js +60 -3
- package/dist/lib/bundle.js.map +1 -1
- package/dist/lib/compose.d.ts +2 -0
- package/dist/lib/compose.d.ts.map +1 -1
- package/dist/lib/compose.js +4 -0
- package/dist/lib/compose.js.map +1 -1
- package/dist/lib/db-backup.d.ts.map +1 -1
- package/dist/lib/db-backup.js +5 -2
- package/dist/lib/db-backup.js.map +1 -1
- package/dist/lib/db-probe.d.ts +75 -0
- package/dist/lib/db-probe.d.ts.map +1 -0
- package/dist/lib/db-probe.js +149 -0
- package/dist/lib/db-probe.js.map +1 -0
- package/dist/lib/hardware.d.ts +31 -0
- package/dist/lib/hardware.d.ts.map +1 -1
- package/dist/lib/hardware.js +75 -1
- package/dist/lib/hardware.js.map +1 -1
- package/dist/lib/offline-steps.d.ts.map +1 -1
- package/dist/lib/offline-steps.js +5 -0
- package/dist/lib/offline-steps.js.map +1 -1
- package/dist/lib/prefs.d.ts +39 -0
- package/dist/lib/prefs.d.ts.map +1 -1
- package/dist/lib/prefs.js +0 -0
- package/dist/lib/prefs.js.map +1 -1
- package/dist/lib/update-steps.d.ts.map +1 -1
- package/dist/lib/update-steps.js +19 -3
- package/dist/lib/update-steps.js.map +1 -1
- package/dist/lib/update.d.ts +12 -2
- package/dist/lib/update.d.ts.map +1 -1
- package/dist/lib/update.js +105 -6
- package/dist/lib/update.js.map +1 -1
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/shell/commands/appliance-hardware.d.ts +1 -0
- package/dist/shell/commands/appliance-hardware.d.ts.map +1 -1
- package/dist/shell/commands/appliance-hardware.js +17 -1
- package/dist/shell/commands/appliance-hardware.js.map +1 -1
- package/dist/shell/commands/db.d.ts +1 -0
- package/dist/shell/commands/db.d.ts.map +1 -1
- package/dist/shell/commands/db.js +115 -0
- package/dist/shell/commands/db.js.map +1 -1
- package/dist/shell/commands/registry.d.ts.map +1 -1
- package/dist/shell/commands/registry.js +27 -0
- package/dist/shell/commands/registry.js.map +1 -1
- package/dist/shell/commands/sql-monitor.d.ts +35 -0
- package/dist/shell/commands/sql-monitor.d.ts.map +1 -0
- package/dist/shell/commands/sql-monitor.js +228 -0
- package/dist/shell/commands/sql-monitor.js.map +1 -0
- package/dist/ui/views/DbCompactView.d.ts.map +1 -1
- package/dist/ui/views/DbCompactView.js +7 -5
- package/dist/ui/views/DbCompactView.js.map +1 -1
- package/dist/ui/views/DbSizeView.d.ts.map +1 -1
- package/dist/ui/views/DbSizeView.js +5 -9
- package/dist/ui/views/DbSizeView.js.map +1 -1
- package/dist/ui/views/DbStatusView.d.ts.map +1 -1
- package/dist/ui/views/DbStatusView.js +8 -10
- package/dist/ui/views/DbStatusView.js.map +1 -1
- package/dist/ui/views/DbTablesView.d.ts.map +1 -1
- package/dist/ui/views/DbTablesView.js +8 -12
- package/dist/ui/views/DbTablesView.js.map +1 -1
- package/dist/ui/views/MainMenu.d.ts.map +1 -1
- package/dist/ui/views/MainMenu.js +19 -3
- package/dist/ui/views/MainMenu.js.map +1 -1
- package/dist/ui/views/MigrateHistoryView.d.ts.map +1 -1
- package/dist/ui/views/MigrateHistoryView.js +8 -9
- package/dist/ui/views/MigrateHistoryView.js.map +1 -1
- package/dist/ui/views/SystemHardwareCheckView.d.ts +6 -0
- package/dist/ui/views/SystemHardwareCheckView.d.ts.map +1 -0
- package/dist/ui/views/SystemHardwareCheckView.js +16 -0
- package/dist/ui/views/SystemHardwareCheckView.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell command handlers for the `sql-monitor` command group.
|
|
3
|
+
*
|
|
4
|
+
* Toggles the opt-in PgHero web SQL-health dashboard. PgHero ships as the
|
|
5
|
+
* compose service `pghero`, gated behind the compose profile `sql-monitor`
|
|
6
|
+
* via `.env` (COMPOSE_PROFILES). Off by default — complements the
|
|
7
|
+
* point-in-time pg-bench-audit tool.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the appliance `ova/cli.sh` `sql_monitor_cmd` behaviour:
|
|
10
|
+
* enable | disable | status | password (alias show-password) | rotate-password
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { readEnvKeys, updateEnvKeys } from '../../lib/env.js';
|
|
15
|
+
import { composeUp, composePs, compose } from '../../lib/compose.js';
|
|
16
|
+
import { exec } from '../../lib/exec.js';
|
|
17
|
+
import { statusLine } from '../formatter.js';
|
|
18
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
19
|
+
const PROFILE = 'sql-monitor';
|
|
20
|
+
const SERVICE = 'pghero';
|
|
21
|
+
const DEFAULT_USERNAME = 'admin';
|
|
22
|
+
const DEFAULT_PORT = '8080';
|
|
23
|
+
// ── Profile helpers ──────────────────────────────────────────────────────────
|
|
24
|
+
/** Split COMPOSE_PROFILES into a clean, non-empty list. */
|
|
25
|
+
export function parseProfiles(raw) {
|
|
26
|
+
return (raw ?? '')
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((p) => p.trim())
|
|
29
|
+
.filter((p) => p.length > 0);
|
|
30
|
+
}
|
|
31
|
+
/** Whether the sql-monitor profile is present in a COMPOSE_PROFILES value. */
|
|
32
|
+
export function isEnabled(profilesRaw) {
|
|
33
|
+
return parseProfiles(profilesRaw).includes(PROFILE);
|
|
34
|
+
}
|
|
35
|
+
/** Add sql-monitor to a profile list without duplicating it. */
|
|
36
|
+
export function addProfile(profilesRaw) {
|
|
37
|
+
const profiles = parseProfiles(profilesRaw);
|
|
38
|
+
if (!profiles.includes(PROFILE))
|
|
39
|
+
profiles.push(PROFILE);
|
|
40
|
+
return profiles.join(',');
|
|
41
|
+
}
|
|
42
|
+
/** Remove sql-monitor from a profile list. */
|
|
43
|
+
export function removeProfile(profilesRaw) {
|
|
44
|
+
return parseProfiles(profilesRaw)
|
|
45
|
+
.filter((p) => p !== PROFILE)
|
|
46
|
+
.join(',');
|
|
47
|
+
}
|
|
48
|
+
// ── Secret generation ────────────────────────────────────────────────────────
|
|
49
|
+
/** Generate a 32-hex-char basic-auth password (16 random bytes). */
|
|
50
|
+
export function generatePassword() {
|
|
51
|
+
return randomBytes(16).toString('hex');
|
|
52
|
+
}
|
|
53
|
+
// ── Host address resolution ──────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the LAN address an operator would type into a browser.
|
|
56
|
+
* Prefers /etc/network-environment DEFAULT_IPV4 (last occurrence, matching
|
|
57
|
+
* cli.sh `grep … | tail -1`), falls back to the first `hostname -I` address,
|
|
58
|
+
* then localhost.
|
|
59
|
+
*/
|
|
60
|
+
export async function resolveHostAddr() {
|
|
61
|
+
// 1. /etc/network-environment DEFAULT_IPV4 (last-wins)
|
|
62
|
+
try {
|
|
63
|
+
const content = readFileSync('/etc/network-environment', 'utf-8');
|
|
64
|
+
const matches = [...content.matchAll(/^DEFAULT_IPV4=(.+)$/gm)];
|
|
65
|
+
if (matches.length > 0) {
|
|
66
|
+
const value = matches[matches.length - 1][1].trim();
|
|
67
|
+
if (value)
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// file absent in dev/test — fall through
|
|
73
|
+
}
|
|
74
|
+
// 2. first `hostname -I` address
|
|
75
|
+
try {
|
|
76
|
+
const { stdout } = await exec('hostname', ['-I']);
|
|
77
|
+
const first = String(stdout).trim().split(/\s+/).filter(Boolean)[0];
|
|
78
|
+
if (first)
|
|
79
|
+
return first;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// hostname unavailable — fall through
|
|
83
|
+
}
|
|
84
|
+
// 3. localhost
|
|
85
|
+
return 'localhost';
|
|
86
|
+
}
|
|
87
|
+
// ── Env mutation helpers (testable) ──────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Ensure a PGHERO_PASSWORD exists. Generates one (and a default username) only
|
|
90
|
+
* when absent, or always when `rotate` is true. Returns the password in effect
|
|
91
|
+
* and whether a new one was written.
|
|
92
|
+
*/
|
|
93
|
+
export async function ensurePassword(rotate = false) {
|
|
94
|
+
const env = readEnvKeys();
|
|
95
|
+
const existing = env.PGHERO_PASSWORD;
|
|
96
|
+
if (!rotate && existing) {
|
|
97
|
+
return { password: existing, generated: false };
|
|
98
|
+
}
|
|
99
|
+
const password = generatePassword();
|
|
100
|
+
const updates = { PGHERO_PASSWORD: password };
|
|
101
|
+
// No-clobber default username.
|
|
102
|
+
if (!env.PGHERO_USERNAME) {
|
|
103
|
+
updates.PGHERO_USERNAME = DEFAULT_USERNAME;
|
|
104
|
+
}
|
|
105
|
+
await updateEnvKeys(updates);
|
|
106
|
+
return { password, generated: true };
|
|
107
|
+
}
|
|
108
|
+
// ── Access block ─────────────────────────────────────────────────────────────
|
|
109
|
+
async function printAccessBlock() {
|
|
110
|
+
const env = readEnvKeys();
|
|
111
|
+
const addr = await resolveHostAddr();
|
|
112
|
+
const port = env.PGHERO_PORT || DEFAULT_PORT;
|
|
113
|
+
const user = env.PGHERO_USERNAME || DEFAULT_USERNAME;
|
|
114
|
+
const pass = env.PGHERO_PASSWORD ?? '';
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log(' PgHero SQL monitor — web dashboard');
|
|
117
|
+
console.log(' ─────────────────────────────────');
|
|
118
|
+
console.log(` URL: http://${addr}:${port}/`);
|
|
119
|
+
console.log(` Username: ${user}`);
|
|
120
|
+
console.log(` Password: ${pass}`);
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(statusLine('Basic-auth only, served over HTTP on the LAN. Disable when finished:', 'warn', 'sql-monitor disable'));
|
|
123
|
+
}
|
|
124
|
+
// ── Command handlers ─────────────────────────────────────────────────────────
|
|
125
|
+
export async function sqlMonitorEnable(_args, _opts, _ctx) {
|
|
126
|
+
try {
|
|
127
|
+
await ensurePassword(false);
|
|
128
|
+
const env = readEnvKeys();
|
|
129
|
+
const next = addProfile(env.COMPOSE_PROFILES);
|
|
130
|
+
if (next !== (env.COMPOSE_PROFILES ?? '')) {
|
|
131
|
+
await updateEnvKeys({ COMPOSE_PROFILES: next });
|
|
132
|
+
}
|
|
133
|
+
console.log(statusLine('SQL monitor (PgHero) enabled — starting service', 'ok'));
|
|
134
|
+
await composeUp(['-d', SERVICE]);
|
|
135
|
+
await printAccessBlock();
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
139
|
+
console.log(statusLine(`Failed to enable SQL monitor: ${msg}`, 'fail'));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export async function sqlMonitorDisable(_args, _opts, _ctx) {
|
|
143
|
+
try {
|
|
144
|
+
const env = readEnvKeys();
|
|
145
|
+
const next = removeProfile(env.COMPOSE_PROFILES);
|
|
146
|
+
await updateEnvKeys({ COMPOSE_PROFILES: next });
|
|
147
|
+
console.log(statusLine('SQL monitor disabled — stopping PgHero', 'ok'));
|
|
148
|
+
// Stop and remove the pghero container. `--remove-orphans` does not evict
|
|
149
|
+
// profile-gated services, so target it explicitly via rm.
|
|
150
|
+
await compose(['rm', '-fs', SERVICE]);
|
|
151
|
+
console.log(statusLine('PgHero stopped. PGHERO_PASSWORD is kept in .env for next enable', 'info', 'rotate with: sql-monitor rotate-password'));
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
155
|
+
console.log(statusLine(`Failed to disable SQL monitor: ${msg}`, 'fail'));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export async function sqlMonitorStatus(_args, _opts, _ctx) {
|
|
159
|
+
const env = readEnvKeys();
|
|
160
|
+
if (isEnabled(env.COMPOSE_PROFILES)) {
|
|
161
|
+
console.log(statusLine('SQL monitor (PgHero): enabled', 'ok'));
|
|
162
|
+
try {
|
|
163
|
+
const out = await composePs(SERVICE);
|
|
164
|
+
if (out)
|
|
165
|
+
console.log(out);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// compose unavailable — status string already printed
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
console.log(statusLine('SQL monitor (PgHero): disabled', 'info'));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export async function sqlMonitorPassword(_args, _opts, _ctx) {
|
|
176
|
+
const env = readEnvKeys();
|
|
177
|
+
if (!isEnabled(env.COMPOSE_PROFILES)) {
|
|
178
|
+
console.log(statusLine('SQL monitor is disabled — showing saved credentials anyway', 'warn', 'PgHero is not running; start it with: sql-monitor enable'));
|
|
179
|
+
}
|
|
180
|
+
if (!env.PGHERO_PASSWORD) {
|
|
181
|
+
console.log(statusLine('No PGHERO_PASSWORD set yet', 'warn', 'run: sql-monitor enable'));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
await printAccessBlock();
|
|
185
|
+
}
|
|
186
|
+
export async function sqlMonitorRotatePassword(_args, _opts, _ctx) {
|
|
187
|
+
try {
|
|
188
|
+
await ensurePassword(true);
|
|
189
|
+
if (isEnabled(readEnvKeys().COMPOSE_PROFILES)) {
|
|
190
|
+
console.log(statusLine('Restarting PgHero with the new password', 'info'));
|
|
191
|
+
await composeUp(['-d', SERVICE]);
|
|
192
|
+
}
|
|
193
|
+
await printAccessBlock();
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
197
|
+
console.log(statusLine(`Failed to rotate password: ${msg}`, 'fail'));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ── Wire handlers into command tree ──────────────────────────────────────────
|
|
201
|
+
export function wireSqlMonitorHandlers(node) {
|
|
202
|
+
if (!node.children)
|
|
203
|
+
return;
|
|
204
|
+
for (const child of node.children) {
|
|
205
|
+
switch (child.name) {
|
|
206
|
+
case 'enable':
|
|
207
|
+
child.handler = sqlMonitorEnable;
|
|
208
|
+
break;
|
|
209
|
+
case 'disable':
|
|
210
|
+
child.handler = sqlMonitorDisable;
|
|
211
|
+
break;
|
|
212
|
+
case 'status':
|
|
213
|
+
child.handler = sqlMonitorStatus;
|
|
214
|
+
break;
|
|
215
|
+
case 'password':
|
|
216
|
+
case 'show-password':
|
|
217
|
+
child.handler = sqlMonitorPassword;
|
|
218
|
+
break;
|
|
219
|
+
case 'rotate-password':
|
|
220
|
+
child.handler = sqlMonitorRotatePassword;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Group with no bare handler defaults to `status` for convenience.
|
|
225
|
+
if (!node.handler)
|
|
226
|
+
node.handler = sqlMonitorStatus;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=sql-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-monitor.js","sourceRoot":"","sources":["../../../src/shell/commands/sql-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,gFAAgF;AAEhF,MAAM,OAAO,GAAG,aAAa,CAAC;AAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC;AACzB,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACjC,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,gFAAgF;AAEhF,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,GAAuB;IACnD,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CAAC,WAA+B;IACvD,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,WAA+B;IACxD,MAAM,QAAQ,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,aAAa,CAAC,WAA+B;IAC3D,OAAO,aAAa,CAAC,WAAW,CAAC;SAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC;SAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,gFAAgF;AAEhF,oEAAoE;AACpE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,uDAAuD;IACvD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAC/D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;IAED,iCAAiC;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IAED,eAAe;IACf,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAM,GAAG,KAAK;IACjD,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC;IAErC,IAAI,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAClD,CAAC;IAED,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,OAAO,GAA2B,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;IACtE,+BAA+B;IAC/B,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACzB,OAAO,CAAC,eAAe,GAAG,gBAAgB,CAAC;IAC7C,CAAC;IACD,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,gFAAgF;AAEhF,KAAK,UAAU,gBAAgB;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,eAAe,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,IAAI,gBAAgB,CAAC;IACrD,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,UAAU,CACpB,sEAAsE,EACtE,MAAM,EACN,qBAAqB,CACtB,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAe,EACf,KAA6B,EAC7B,IAAkB;IAElB,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;QAE5B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,CAAC;YAC1C,MAAM,aAAa,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,iDAAiD,EAAE,IAAI,CAAC,CAAC,CAAC;QACjF,MAAM,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACjC,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,iCAAiC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAe,EACf,KAA6B,EAC7B,IAAkB;IAElB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACjD,MAAM,aAAa,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;QAEhD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,wCAAwC,EAAE,IAAI,CAAC,CAAC,CAAC;QACxE,0EAA0E;QAC1E,0DAA0D;QAC1D,MAAM,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,UAAU,CACpB,iEAAiE,EACjE,MAAM,EACN,0CAA0C,CAC3C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,kCAAkC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAe,EACf,KAA6B,EAC7B,IAAkB;IAElB,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAI,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,GAAG;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,sDAAsD;QACxD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAe,EACf,KAA6B,EAC7B,IAAkB;IAElB,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,UAAU,CACpB,4DAA4D,EAC5D,MAAM,EACN,0DAA0D,CAC3D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,4BAA4B,EAAE,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC;QACzF,OAAO;IACT,CAAC;IACD,MAAM,gBAAgB,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAe,EACf,KAA6B,EAC7B,IAAkB;IAElB,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,SAAS,CAAC,WAAW,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,yCAAyC,EAAE,MAAM,CAAC,CAAC,CAAC;YAC3E,MAAM,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,8BAA8B,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,sBAAsB,CAAC,IAAiB;IACtD,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,QAAQ;gBACX,KAAK,CAAC,OAAO,GAAG,gBAAgB,CAAC;gBACjC,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,CAAC,OAAO,GAAG,iBAAiB,CAAC;gBAClC,MAAM;YACR,KAAK,QAAQ;gBACX,KAAK,CAAC,OAAO,GAAG,gBAAgB,CAAC;gBACjC,MAAM;YACR,KAAK,UAAU,CAAC;YAChB,KAAK,eAAe;gBAClB,KAAK,CAAC,OAAO,GAAG,kBAAkB,CAAC;gBACnC,MAAM;YACR,KAAK,iBAAiB;gBACpB,KAAK,CAAC,OAAO,GAAG,wBAAwB,CAAC;gBACzC,MAAM;QACV,CAAC;IACH,CAAC;IACD,mEAAmE;IACnE,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,OAAO,GAAG,gBAAgB,CAAC;AACrD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbCompactView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbCompactView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DbCompactView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbCompactView.tsx"],"names":[],"mappings":"AAKA,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAiC3D"}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { DB_USER, DB_NAME, PG_ENV } from '../../lib/db.js';
|
|
3
|
+
import { probeDatabaseReady, runBoundedQuery } from '../../lib/db-probe.js';
|
|
5
4
|
import { AppShell, Section, Spinner, StatusLine } from '../components/index.js';
|
|
6
5
|
import { useAsyncTask } from '../hooks/index.js';
|
|
7
6
|
export function DbCompactView({ onBack }) {
|
|
7
|
+
// Readiness is bounded + classified. VACUUM FULL legitimately runs for
|
|
8
|
+
// minutes, so it opts OUT of the timeouts (all 0) — we keep only the
|
|
9
|
+
// classified-failure reporting, not a kill.
|
|
8
10
|
const readyTask = useAsyncTask(async () => {
|
|
9
|
-
await
|
|
11
|
+
await probeDatabaseReady();
|
|
10
12
|
return true;
|
|
11
13
|
});
|
|
12
14
|
const vacuumTask = useAsyncTask(async () => {
|
|
13
|
-
await
|
|
15
|
+
await runBoundedQuery('VACUUM FULL;', { timeoutMs: 0, statementTimeoutMs: 0, lockTimeoutMs: 0 });
|
|
14
16
|
return true;
|
|
15
17
|
}, false); // Don't auto-run, wait for readiness
|
|
16
18
|
// Chain: run vacuum after ready
|
|
@@ -19,6 +21,6 @@ export function DbCompactView({ onBack }) {
|
|
|
19
21
|
vacuumTask.run();
|
|
20
22
|
}, [readyTask.status]);
|
|
21
23
|
const allDone = readyTask.status === 'error' || vacuumTask.status === 'done' || vacuumTask.status === 'error';
|
|
22
|
-
return (_jsx(AppShell, { command: "ct db compact", onBack: onBack, isComplete: allDone, children: _jsxs(Section, { title: "Database Compaction", children: [readyTask.status === 'pending' && _jsx(Spinner, { label: "Checking database..." }), readyTask.status === 'error' && _jsx(StatusLine, { status: "fail", label:
|
|
24
|
+
return (_jsx(AppShell, { command: "ct db compact", onBack: onBack, isComplete: allDone, children: _jsxs(Section, { title: "Database Compaction", children: [readyTask.status === 'pending' && _jsx(Spinner, { label: "Checking database..." }), readyTask.status === 'error' && _jsx(StatusLine, { status: "fail", label: `Database not ready — ${readyTask.error?.message ?? 'unknown error'}` }), readyTask.status === 'done' && vacuumTask.status === 'pending' && _jsx(Spinner, { label: "Running VACUUM FULL (this may take several minutes)..." }), vacuumTask.status === 'error' && _jsx(StatusLine, { status: "fail", label: `Database vacuum failed — ${vacuumTask.error?.message ?? 'unknown error'}` }), vacuumTask.status === 'done' && _jsx(StatusLine, { status: "ok", label: "Database vacuum completed successfully" })] }) }));
|
|
23
25
|
}
|
|
24
26
|
//# sourceMappingURL=DbCompactView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbCompactView.js","sourceRoot":"","sources":["../../../src/ui/views/DbCompactView.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DbCompactView.js","sourceRoot":"","sources":["../../../src/ui/views/DbCompactView.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAMjD,MAAM,UAAU,aAAa,CAAC,EAAE,MAAM,EAAsB;IAE1D,uEAAuE;IACvE,qEAAqE;IACrE,4CAA4C;IAC5C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE;QACxC,MAAM,kBAAkB,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE;QACzC,MAAM,eAAe,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC;QACjG,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,qCAAqC;IAEhD,gCAAgC;IAChC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM;YAAE,UAAU,CAAC,GAAG,EAAE,CAAC;IACpD,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAEvB,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,OAAO,CAAC;IAE9G,OAAO,CACL,KAAC,QAAQ,IAAC,OAAO,EAAC,eAAe,EAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,YACnE,MAAC,OAAO,IAAC,KAAK,EAAC,qBAAqB,aACjC,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,KAAC,OAAO,IAAC,KAAK,EAAC,sBAAsB,GAAG,EAC1E,SAAS,CAAC,MAAM,KAAK,OAAO,IAAI,KAAC,UAAU,IAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAE,wBAAwB,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE,GAAI,EAC1I,SAAS,CAAC,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,KAAC,OAAO,IAAC,KAAK,EAAC,wDAAwD,GAAG,EAC5I,UAAU,CAAC,MAAM,KAAK,OAAO,IAAI,KAAC,UAAU,IAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAE,4BAA4B,UAAU,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE,GAAI,EAChJ,UAAU,CAAC,MAAM,KAAK,MAAM,IAAI,KAAC,UAAU,IAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC,wCAAwC,GAAG,IAClG,GACD,CACZ,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbSizeView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbSizeView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DbSizeView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbSizeView.tsx"],"names":[],"mappings":"AAKA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,eAAe,2CAyBrD"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { composeExec } from '../../lib/compose.js';
|
|
3
|
-
import { DB_USER, DB_NAME, PG_ENV } from '../../lib/db.js';
|
|
4
2
|
import { AppShell, Section, Spinner, StatusLine, KeyboardHint } from '../components/index.js';
|
|
5
3
|
import { useAsyncTask } from '../hooks/index.js';
|
|
6
|
-
import {
|
|
4
|
+
import { measureDatabaseSize } from '../../lib/db-probe.js';
|
|
7
5
|
export function DbSizeView({ onBack }) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return parsePsqlScalar((result.stdout ?? '').trim());
|
|
12
|
-
});
|
|
6
|
+
// Hardened probe: bounded by timeouts and reports WHY on failure (auth
|
|
7
|
+
// mismatch, container down, lock contention, timeout) instead of a guess.
|
|
8
|
+
const sizeTask = useAsyncTask(async () => measureDatabaseSize());
|
|
13
9
|
const sizeDone = sizeTask.status === 'done' || sizeTask.status === 'error';
|
|
14
|
-
return (_jsxs(AppShell, { command: "ct db size", onBack: onBack, isComplete: sizeDone, children: [_jsxs(Section, { title: "Database Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying database size..." }), sizeTask.status === 'error' && _jsx(StatusLine, { status: "fail", label:
|
|
10
|
+
return (_jsxs(AppShell, { command: "ct db size", onBack: onBack, isComplete: sizeDone, children: [_jsxs(Section, { title: "Database Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying database size..." }), sizeTask.status === 'error' && (_jsx(StatusLine, { status: "fail", label: `Could not query database size — ${sizeTask.error?.message ?? 'unknown error'}` })), sizeTask.status === 'done' && sizeTask.data && (_jsx(StatusLine, { status: "ok", label: `Database size: ${sizeTask.data.pretty}` }))] }), _jsx(KeyboardHint, { actions: [{ key: 'q', label: 'Back' }] })] }));
|
|
15
11
|
}
|
|
16
12
|
//# sourceMappingURL=DbSizeView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbSizeView.js","sourceRoot":"","sources":["../../../src/ui/views/DbSizeView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DbSizeView.js","sourceRoot":"","sources":["../../../src/ui/views/DbSizeView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAM5D,MAAM,UAAU,UAAU,CAAC,EAAE,MAAM,EAAmB;IAEpD,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC;IAE3E,OAAO,CACL,MAAC,QAAQ,IAAC,OAAO,EAAC,YAAY,EAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,aACjE,MAAC,OAAO,IAAC,KAAK,EAAC,eAAe,aAC3B,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,KAAC,OAAO,IAAC,KAAK,EAAC,2BAA2B,GAAG,EAC9E,QAAQ,CAAC,MAAM,KAAK,OAAO,IAAI,CAC9B,KAAC,UAAU,IACT,MAAM,EAAC,MAAM,EACb,KAAK,EAAE,mCAAmC,QAAQ,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE,GACtF,CACH,EACA,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,IAAI,IAAI,CAC9C,KAAC,UAAU,IAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAE,kBAAkB,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAI,CAC5E,IACO,EACV,KAAC,YAAY,IAAC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,GAAI,IAC/C,CACZ,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbStatusView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbStatusView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DbStatusView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbStatusView.tsx"],"names":[],"mappings":"AAUA,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CAyFzD"}
|
|
@@ -2,22 +2,20 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { Box, Text } from 'ink';
|
|
5
|
-
import {
|
|
6
|
-
import { DB_USER, DB_NAME, PG_ENV } from '../../lib/db.js';
|
|
5
|
+
import { measureDatabaseSize, probeDatabaseReady } from '../../lib/db-probe.js';
|
|
7
6
|
import { getPaths } from '../../lib/paths.js';
|
|
8
7
|
import { fetchPostgresPerformance } from '../../lib/postgres-performance.js';
|
|
9
8
|
import { AppShell, Section, StatusLine, Spinner, KeyValue, PostgresPerformanceLines } from '../components/index.js';
|
|
10
9
|
import { useAsyncTask } from '../hooks/index.js';
|
|
11
10
|
export function DbStatusView({ onBack }) {
|
|
11
|
+
// Hardened probes: bounded by timeouts and report WHY on failure (auth,
|
|
12
|
+
// not-running, lock, timeout) instead of a generic message.
|
|
12
13
|
const readyTask = useAsyncTask(async () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const sizeTask = useAsyncTask(async () => {
|
|
17
|
-
const result = await composeExec('db', ['psql', '-U', DB_USER, '-d', DB_NAME, '-t', '-c',
|
|
18
|
-
`SELECT pg_size_pretty(pg_database_size('${DB_NAME}')) AS database_size;`], { env: PG_ENV, pipe: true });
|
|
19
|
-
return (result.stdout ?? '').trim();
|
|
14
|
+
// Resolves on success, rejects with a classified DbProbeError on failure;
|
|
15
|
+
// the view keys off readyTask.status / readyTask.error.
|
|
16
|
+
await probeDatabaseReady();
|
|
20
17
|
});
|
|
18
|
+
const sizeTask = useAsyncTask(async () => measureDatabaseSize());
|
|
21
19
|
const backupsTask = useAsyncTask(async () => {
|
|
22
20
|
const { dbDumpsDir } = getPaths();
|
|
23
21
|
if (!existsSync(dbDumpsDir))
|
|
@@ -32,7 +30,7 @@ export function DbStatusView({ onBack }) {
|
|
|
32
30
|
});
|
|
33
31
|
const performanceTask = useAsyncTask(fetchPostgresPerformance);
|
|
34
32
|
const allDone = [readyTask, sizeTask, backupsTask, performanceTask].every(t => t.status === 'done' || t.status === 'error');
|
|
35
|
-
return (_jsxs(AppShell, { command: "ct db status", onBack: onBack, isComplete: allDone, children: [_jsxs(Section, { title: "Database Connection", children: [readyTask.status === 'pending' && _jsx(Spinner, { label: "Checking database..." }), readyTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Database not accepting connections" }), readyTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: readyTask.error.stderr?.trim() || readyTask.error.message }) }))] })), readyTask.status === 'done' && _jsx(StatusLine, { status: "ok", label: "Database: accepting connections" })] }), _jsxs(Section, { title: "Database Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying size..." }), sizeTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Could not query database size" }), sizeTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: sizeTask.error.stderr?.trim() || sizeTask.error.message }) }))] })), sizeTask.status === 'done' && _jsx(StatusLine, { status: "info", label: `Database size: ${sizeTask.data}` })] }), _jsxs(Section, { title: "PostgreSQL Performance", children: [performanceTask.status === 'pending' && _jsx(Spinner, { label: "Querying timing metrics..." }), performanceTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "warn", label: "Could not query timing metrics" }), performanceTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "yellow", children: performanceTask.error.stderr?.trim() || performanceTask.error.message }) }))] })), performanceTask.status === 'done' && performanceTask.data && (_jsx(PostgresPerformanceLines, { performance: performanceTask.data }))] }), _jsxs(Section, { title: "Available Backups", children: [backupsTask.status === 'pending' && _jsx(Spinner, { label: "Listing backups..." }), backupsTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Could not list backups" }), backupsTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: backupsTask.error.stderr?.trim() || backupsTask.error.message }) }))] })), backupsTask.status === 'done' && backupsTask.data && (backupsTask.data.length === 0
|
|
33
|
+
return (_jsxs(AppShell, { command: "ct db status", onBack: onBack, isComplete: allDone, children: [_jsxs(Section, { title: "Database Connection", children: [readyTask.status === 'pending' && _jsx(Spinner, { label: "Checking database..." }), readyTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Database not accepting connections" }), readyTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: readyTask.error.stderr?.trim() || readyTask.error.message }) }))] })), readyTask.status === 'done' && _jsx(StatusLine, { status: "ok", label: "Database: accepting connections" })] }), _jsxs(Section, { title: "Database Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying size..." }), sizeTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Could not query database size" }), sizeTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: sizeTask.error.stderr?.trim() || sizeTask.error.message }) }))] })), sizeTask.status === 'done' && sizeTask.data && _jsx(StatusLine, { status: "info", label: `Database size: ${sizeTask.data.pretty}` })] }), _jsxs(Section, { title: "PostgreSQL Performance", children: [performanceTask.status === 'pending' && _jsx(Spinner, { label: "Querying timing metrics..." }), performanceTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "warn", label: "Could not query timing metrics" }), performanceTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "yellow", children: performanceTask.error.stderr?.trim() || performanceTask.error.message }) }))] })), performanceTask.status === 'done' && performanceTask.data && (_jsx(PostgresPerformanceLines, { performance: performanceTask.data }))] }), _jsxs(Section, { title: "Available Backups", children: [backupsTask.status === 'pending' && _jsx(Spinner, { label: "Listing backups..." }), backupsTask.status === 'error' && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: "fail", label: "Could not list backups" }), backupsTask.error && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "red", children: backupsTask.error.stderr?.trim() || backupsTask.error.message }) }))] })), backupsTask.status === 'done' && backupsTask.data && (backupsTask.data.length === 0
|
|
36
34
|
? _jsx(StatusLine, { status: "info", label: "No backups found" })
|
|
37
35
|
: _jsx(KeyValue, { items: backupsTask.data }))] })] }));
|
|
38
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbStatusView.js","sourceRoot":"","sources":["../../../src/ui/views/DbStatusView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DbStatusView.js","sourceRoot":"","sources":["../../../src/ui/views/DbStatusView.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbTablesView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbTablesView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DbTablesView.d.ts","sourceRoot":"","sources":["../../../src/ui/views/DbTablesView.tsx"],"names":[],"mappings":"AAMA,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CAsC/D"}
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { DB_USER, DB_NAME, PG_ENV } from '../../lib/db.js';
|
|
2
|
+
import { measureDatabaseSize, runBoundedQuery } from '../../lib/db-probe.js';
|
|
4
3
|
import { AppShell, Section, DataTable, Spinner, StatusLine, KeyboardHint } from '../components/index.js';
|
|
5
4
|
import { useAsyncTask } from '../hooks/index.js';
|
|
6
|
-
import { parsePsqlTable
|
|
5
|
+
import { parsePsqlTable } from '../../utils/parsePsql.js';
|
|
7
6
|
export function DbTablesView({ name, onBack }) {
|
|
7
|
+
// Bounded + classified probes: a slow query can't hang the view, and a
|
|
8
|
+
// failure reports WHY (auth / not-running / lock / timeout).
|
|
8
9
|
const tablesTask = useAsyncTask(async () => {
|
|
9
10
|
const safeName = name ? name.replace(/'/g, "''") : '';
|
|
10
11
|
const whereClause = safeName
|
|
11
12
|
? `AND c.relname LIKE '%${safeName}%'`
|
|
12
13
|
: '';
|
|
13
14
|
const query = `SELECT table_name, row_count, pg_size_pretty(total_bytes) AS total_size, pg_size_pretty(index_bytes) AS index_size, pg_size_pretty(table_bytes) AS table_size FROM (SELECT c.relname AS table_name, c.reltuples::BIGINT AS row_count, pg_total_relation_size(c.oid) AS total_bytes, pg_indexes_size(c.oid) AS index_bytes, pg_relation_size(c.oid) AS table_bytes FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'public' AND c.relkind = 'r' ${whereClause}) t ORDER BY total_bytes DESC;`;
|
|
14
|
-
|
|
15
|
-
return parsePsqlTable((result.stdout ?? '').trim());
|
|
16
|
-
});
|
|
17
|
-
const sizeTask = useAsyncTask(async () => {
|
|
18
|
-
const result = await composeExec('db', ['psql', '-U', DB_USER, '-d', DB_NAME, '-c',
|
|
19
|
-
`SELECT pg_size_pretty(pg_database_size('${DB_NAME}')) AS database_size;`], { env: PG_ENV, pipe: true });
|
|
20
|
-
return parsePsqlScalar((result.stdout ?? '').trim());
|
|
15
|
+
return parsePsqlTable(await runBoundedQuery(query));
|
|
21
16
|
});
|
|
17
|
+
const sizeTask = useAsyncTask(async () => measureDatabaseSize());
|
|
22
18
|
const allDone = [tablesTask, sizeTask].every(t => t.status === 'done' || t.status === 'error');
|
|
23
|
-
return (_jsxs(AppShell, { command: name ? `ct db tables ${name}` : 'ct db tables', onBack: onBack, isComplete: allDone, children: [_jsxs(Section, { title: name ? `Table Sizes: ${name}` : 'All Table Sizes', children: [tablesTask.status === 'pending' && _jsx(Spinner, { label: "Querying table sizes..." }), tablesTask.status === 'error' && _jsx(StatusLine, { status: "fail", label:
|
|
19
|
+
return (_jsxs(AppShell, { command: name ? `ct db tables ${name}` : 'ct db tables', onBack: onBack, isComplete: allDone, children: [_jsxs(Section, { title: name ? `Table Sizes: ${name}` : 'All Table Sizes', children: [tablesTask.status === 'pending' && _jsx(Spinner, { label: "Querying table sizes..." }), tablesTask.status === 'error' && _jsx(StatusLine, { status: "fail", label: `Query failed — ${tablesTask.error?.message ?? 'unknown error'}` }), tablesTask.status === 'done' && tablesTask.data && (tablesTask.data.rows.length > 0
|
|
24
20
|
? _jsx(DataTable, { columns: tablesTask.data.columns, rows: tablesTask.data.rows })
|
|
25
|
-
: _jsx(StatusLine, { status: "ok", label: "No tables found" }))] }), _jsxs(Section, { title: "Database Total Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying size..." }), sizeTask.status === 'error' && _jsx(StatusLine, { status: "fail", label:
|
|
21
|
+
: _jsx(StatusLine, { status: "ok", label: "No tables found" }))] }), _jsxs(Section, { title: "Database Total Size", children: [sizeTask.status === 'pending' && _jsx(Spinner, { label: "Querying size..." }), sizeTask.status === 'error' && _jsx(StatusLine, { status: "fail", label: `Query failed — ${sizeTask.error?.message ?? 'unknown error'}` }), sizeTask.status === 'done' && sizeTask.data && (_jsx(StatusLine, { status: "ok", label: `Database size: ${sizeTask.data.pretty}` }))] }), _jsx(KeyboardHint, { actions: [{ key: 'q', label: 'Back' }] })] }));
|
|
26
22
|
}
|
|
27
23
|
//# sourceMappingURL=DbTablesView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbTablesView.js","sourceRoot":"","sources":["../../../src/ui/views/DbTablesView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DbTablesView.js","sourceRoot":"","sources":["../../../src/ui/views/DbTablesView.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MainMenu.d.ts","sourceRoot":"","sources":["../../../src/ui/views/MainMenu.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MainMenu.d.ts","sourceRoot":"","sources":["../../../src/ui/views/MainMenu.tsx"],"names":[],"mappings":"AAmTA,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,aAAa,2CA4uBlF"}
|
|
@@ -4,8 +4,8 @@ import { Box, Text, useInput, useStdout } from 'ink';
|
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { execa } from 'execa';
|
|
7
|
-
import { ActionBar, AppShell, DashboardBlock, MenuListRow, PanelState, LogLevelBadge, SplitPane, SplitPaneCard, StatusListRow } from '../components/index.js';
|
|
8
|
-
import { usePolling, useLogLevel, useListCursor } from '../hooks/index.js';
|
|
7
|
+
import { ActionBar, AppShell, DashboardBlock, MenuListRow, PanelState, LogLevelBadge, SplitPane, SplitPaneCard, StatusBox, StatusListRow } from '../components/index.js';
|
|
8
|
+
import { useAsyncTask, usePolling, useLogLevel, useListCursor } from '../hooks/index.js';
|
|
9
9
|
import { getServiceList } from '../../lib/services.js';
|
|
10
10
|
import { getAppHealth } from '../../lib/rpc.js';
|
|
11
11
|
import { healthIcon, healthColor, extractUptime } from '../utils/serviceDisplay.js';
|
|
@@ -66,7 +66,9 @@ import { K8sDeployView } from './K8sDeployView.js';
|
|
|
66
66
|
import { SetupWizardView } from './SetupWizardView.js';
|
|
67
67
|
import { PowerActionView } from './PowerActionView.js';
|
|
68
68
|
import { DiskHardwareCheckView } from './DiskHardwareCheckView.js';
|
|
69
|
+
import { SystemHardwareCheckView } from './SystemHardwareCheckView.js';
|
|
69
70
|
import { resetBackendCache } from '../../lib/deployment.js';
|
|
71
|
+
import { validateApplianceHardware } from '../../lib/hardware.js';
|
|
70
72
|
/* ------------------------------------------------------------------ */
|
|
71
73
|
/* Menu definition */
|
|
72
74
|
/* ------------------------------------------------------------------ */
|
|
@@ -188,6 +190,7 @@ const MENU_ITEMS = [
|
|
|
188
190
|
value: 'appliance-menu',
|
|
189
191
|
children: [
|
|
190
192
|
{ label: 'Status', description: 'Migration summary and appliance info', value: 'appliance status' },
|
|
193
|
+
{ label: 'System Hardware Check', description: 'Validate CPU, RAM, and disk requirements', value: 'appliance hardware-check system' },
|
|
191
194
|
{ label: 'Disk Hardware Check', description: 'fio benchmark with live charts and sparklines', value: 'appliance hardware-check disk' },
|
|
192
195
|
{ label: 'Migrations', description: 'Show all migrations and their status', value: 'appliance migrations' },
|
|
193
196
|
{ label: 'Run Migrations', description: 'Run pending appliance migrations', value: 'appliance migrate' },
|
|
@@ -245,6 +248,7 @@ const VIEW_MAP = {
|
|
|
245
248
|
'loadtest': (onBack) => _jsx(LoadTestView, { onBack: onBack }),
|
|
246
249
|
'loadtest xcc': (onBack) => _jsx(XccLoadTestView, { onBack: onBack }),
|
|
247
250
|
'appliance hardware-check disk': (onBack) => _jsx(DiskHardwareCheckView, { onBack: onBack }),
|
|
251
|
+
'appliance hardware-check system': (onBack) => _jsx(SystemHardwareCheckView, { onBack: onBack }),
|
|
248
252
|
'offline list': (onBack) => _jsx(OfflineListView, { onBack: onBack }),
|
|
249
253
|
'offline fetch': (onBack) => _jsx(OfflineFetchView, { onBack: onBack }),
|
|
250
254
|
'offline download': (onBack) => _jsx(OfflineDownloadView, { onBack: onBack }),
|
|
@@ -304,6 +308,7 @@ export function MainMenu({ version, firstRun, onCliCommand, onExit }) {
|
|
|
304
308
|
}, [isK8s]);
|
|
305
309
|
const { data: services, stop: stopSvcPolling, start: startSvcPolling } = usePolling(getServiceList, 5000, false);
|
|
306
310
|
const { data: appHealth, stop: stopHealth, start: startHealth } = usePolling(getAppHealth, 15000, false);
|
|
311
|
+
const hardwareTask = useAsyncTask(validateApplianceHardware, true);
|
|
307
312
|
// Cert expiry warning (passive, non-blocking)
|
|
308
313
|
const [certDaysLeft, setCertDaysLeft] = useState(null);
|
|
309
314
|
useEffect(() => {
|
|
@@ -736,7 +741,18 @@ export function MainMenu({ version, firstRun, onCliCommand, onExit }) {
|
|
|
736
741
|
lines: ['Select a menu item to preview it.'],
|
|
737
742
|
};
|
|
738
743
|
})();
|
|
739
|
-
return (_jsxs(AppShell, { title: `CallTelemetry CLI v${version}`, logLevel: logLevel, backKeys: false, footerHint: "", children: [_jsx(Box, { paddingLeft: 1, marginBottom: 1, children: _jsx(Text, { color: "cyan", children: LOGO }) }),
|
|
744
|
+
return (_jsxs(AppShell, { title: `CallTelemetry CLI v${version}`, logLevel: logLevel, backKeys: false, footerHint: "", children: [_jsx(Box, { paddingLeft: 1, marginBottom: 1, children: _jsx(Text, { color: "cyan", children: LOGO }) }), hardwareTask.status === 'done' && hardwareTask.data && !hardwareTask.data.ok && (_jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsx(StatusBox, { variant: "warning", title: "Hardware validation failed", lines: [
|
|
745
|
+
hardwareTask.data.cpu.ok
|
|
746
|
+
? `CPU ok: ${hardwareTask.data.cpu.model} (${hardwareTask.data.cpu.arch}, ${hardwareTask.data.cpu.cores} cores)`
|
|
747
|
+
: `CPU mismatch: ${hardwareTask.data.cpu.missingFlags.length > 0 ? `missing ${hardwareTask.data.cpu.missingFlags.join(', ')}` : `${hardwareTask.data.cpu.cores} cores detected`}`,
|
|
748
|
+
hardwareTask.data.ram.ok
|
|
749
|
+
? `RAM ok: ${hardwareTask.data.ram.totalMb}MB`
|
|
750
|
+
: `RAM low: ${hardwareTask.data.ram.totalMb}MB detected, ${hardwareTask.data.ram.requiredMb}MB required`,
|
|
751
|
+
hardwareTask.data.disk.ok
|
|
752
|
+
? `Disk ok: ${hardwareTask.data.disk.freePercent}% free`
|
|
753
|
+
: `Disk low: ${hardwareTask.data.disk.freePercent}% free, ${hardwareTask.data.disk.requiredFreePercent}% required`,
|
|
754
|
+
'Updates are blocked until hardware requirements pass.',
|
|
755
|
+
] }) })), appHealth === undefined ? (_jsx(PanelState, { state: "loading", message: "Checking app health..." })) : (_jsxs(DashboardBlock, { title: "App", marginBottom: 0, meta: _jsxs(_Fragment, { children: [_jsxs(Text, { color: appHealth.overall === 'healthy' ? 'green' : appHealth.overall === 'degraded' ? 'yellow' : 'red', children: [appHealth.overall === 'healthy' ? ' ●' : appHealth.overall === 'degraded' ? ' ▲' : ' ✘', ` ${appHealth.overall}`] }), (() => {
|
|
740
756
|
const webSvc = serviceList.find(s => s.service === 'web');
|
|
741
757
|
const uptime = webSvc ? extractUptime(webSvc.status) : '';
|
|
742
758
|
return uptime && uptime !== 'exited' ? (_jsxs(Text, { dimColor: true, children: [' ', "Up ", uptime] })) : null;
|