From 530698f52efcd226ff575beee429bd85311c1a17 Mon Sep 17 00:00:00 2001 From: Ryan Moon Date: Fri, 3 Apr 2026 20:23:48 -0500 Subject: [PATCH] feat: add infrastructure checks (DB exists, Spaces prefix) to customer overview --- frontend/index.html | 26 ++++++++++++++++++++++++- src/routes/customers.ts | 43 ++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 34bc353..12d9dc1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -624,7 +624,7 @@ } } - function renderDetail({ customer, status, sizeHistory }) { + function renderDetail({ customer, status, infra, sizeHistory }) { // Header document.getElementById('detail-name').textContent = customer.name || customer.slug; document.getElementById('detail-slug').textContent = customer.slug; @@ -718,6 +718,30 @@ ${conditionsHtml} ${podsHtml} +
+
Infrastructure
+
+ Database + ${infra?.database?.exists + ? 'Exists' + : 'Not found'} +
+
+ Storage + ${infra?.spaces?.configured + ? 'Configured' + : 'Not configured'} +
+ ${infra?.spaces?.configured ? ` +
+ Bucket + ${infra.spaces.bucket} +
+
+ Prefix + ${infra.spaces.prefix} +
` : ''} +
Provisioning Steps
${stepsHtml}
diff --git a/src/routes/customers.ts b/src/routes/customers.ts index 720cc7d..ffb69f3 100644 --- a/src/routes/customers.ts +++ b/src/routes/customers.ts @@ -219,19 +219,44 @@ export async function customerRoutes(app: FastifyInstance) { statusEntry = { data: liveStatus, cachedAt: new Date().toISOString() }; } - // ── Size history (last 30 days) ─────────────────────────────────────────── - const sizeHistory = await db` - SELECT recorded_at, db_size_bytes, spaces_size_bytes, spaces_object_count - FROM customer_size_snapshots - WHERE slug = ${slug} - ORDER BY recorded_at DESC - LIMIT 30 - `; + // ── Infrastructure checks ───────────────────────────────────────────────── + const [dbCheck, sizeHistory, secrets] = await Promise.allSettled([ + // Try connecting to the customer DB + (async () => { + const sql = postgres(config.doadminDbUrl.replace(/\/([^/?]+)(\?|$)/, `/${slug}$2`), { max: 1, connect_timeout: 5 }); + try { + await sql`SELECT 1`; + return true; + } finally { + await sql.end(); + } + })(), + db` + SELECT recorded_at, db_size_bytes, spaces_size_bytes, spaces_object_count + FROM customer_size_snapshots + WHERE slug = ${slug} + ORDER BY recorded_at DESC + LIMIT 30 + `, + getSecret(namespace, "lunarfront-secrets").catch(() => null), + ]); + + const dbExists = dbCheck.status === "fulfilled" ? dbCheck.value : false; + const secretData = secrets.status === "fulfilled" ? secrets.value : null; + const infra = { + database: { exists: dbExists }, + spaces: { + configured: !!(secretData?.["spaces-prefix"]), + bucket: secretData?.["spaces-bucket"] ?? null, + prefix: secretData?.["spaces-prefix"] ?? null, + }, + }; return reply.send({ customer, status: { ...statusEntry.data, cachedAt: statusEntry.cachedAt }, - sizeHistory, + infra, + sizeHistory: sizeHistory.status === "fulfilled" ? sizeHistory.value : [], }); });