feat: add infrastructure checks (DB exists, Spaces prefix) to customer overview
Some checks failed
Build & Release / build (push) Has been cancelled
Some checks failed
Build & Release / build (push) Has been cancelled
This commit is contained in:
@@ -624,7 +624,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDetail({ customer, status, sizeHistory }) {
|
function renderDetail({ customer, status, infra, sizeHistory }) {
|
||||||
// Header
|
// Header
|
||||||
document.getElementById('detail-name').textContent = customer.name || customer.slug;
|
document.getElementById('detail-name').textContent = customer.name || customer.slug;
|
||||||
document.getElementById('detail-slug').textContent = customer.slug;
|
document.getElementById('detail-slug').textContent = customer.slug;
|
||||||
@@ -718,6 +718,30 @@
|
|||||||
${conditionsHtml}
|
${conditionsHtml}
|
||||||
${podsHtml}
|
${podsHtml}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="card-title">Infrastructure</div>
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">Database</span>
|
||||||
|
<span class="stat-value">${infra?.database?.exists
|
||||||
|
? '<span class="badge badge-green">Exists</span>'
|
||||||
|
: '<span class="badge badge-red">Not found</span>'}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">Storage</span>
|
||||||
|
<span class="stat-value">${infra?.spaces?.configured
|
||||||
|
? '<span class="badge badge-green">Configured</span>'
|
||||||
|
: '<span class="badge badge-gray">Not configured</span>'}</span>
|
||||||
|
</div>
|
||||||
|
${infra?.spaces?.configured ? `
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">Bucket</span>
|
||||||
|
<span class="stat-value" style="font-family:monospace;font-size:0.8rem">${infra.spaces.bucket}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">Prefix</span>
|
||||||
|
<span class="stat-value" style="font-family:monospace;font-size:0.8rem">${infra.spaces.prefix}</span>
|
||||||
|
</div>` : ''}
|
||||||
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="card-title">Provisioning Steps</div>
|
<div class="card-title">Provisioning Steps</div>
|
||||||
<div class="step-list">${stepsHtml}</div>
|
<div class="step-list">${stepsHtml}</div>
|
||||||
|
|||||||
@@ -219,19 +219,44 @@ export async function customerRoutes(app: FastifyInstance) {
|
|||||||
statusEntry = { data: liveStatus, cachedAt: new Date().toISOString() };
|
statusEntry = { data: liveStatus, cachedAt: new Date().toISOString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Size history (last 30 days) ───────────────────────────────────────────
|
// ── Infrastructure checks ─────────────────────────────────────────────────
|
||||||
const sizeHistory = await db`
|
const [dbCheck, sizeHistory, secrets] = await Promise.allSettled([
|
||||||
SELECT recorded_at, db_size_bytes, spaces_size_bytes, spaces_object_count
|
// Try connecting to the customer DB
|
||||||
FROM customer_size_snapshots
|
(async () => {
|
||||||
WHERE slug = ${slug}
|
const sql = postgres(config.doadminDbUrl.replace(/\/([^/?]+)(\?|$)/, `/${slug}$2`), { max: 1, connect_timeout: 5 });
|
||||||
ORDER BY recorded_at DESC
|
try {
|
||||||
LIMIT 30
|
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({
|
return reply.send({
|
||||||
customer,
|
customer,
|
||||||
status: { ...statusEntry.data, cachedAt: statusEntry.cachedAt },
|
status: { ...statusEntry.data, cachedAt: statusEntry.cachedAt },
|
||||||
sizeHistory,
|
infra,
|
||||||
|
sizeHistory: sizeHistory.status === "fulfilled" ? sizeHistory.value : [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user