postgres uptime
This commit is contained in:
+78
-57
@@ -10,6 +10,7 @@ const dateFormat = new Intl.DateTimeFormat('en-GB', {
|
||||
|
||||
const apiEndpoints = {
|
||||
health: '/health',
|
||||
uptime: '/api/uptime',
|
||||
teams: '/api/tss/leaderboard/teams?limit=100',
|
||||
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
|
||||
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
|
||||
@@ -322,7 +323,7 @@ function App() {
|
||||
|
||||
const controller = new AbortController()
|
||||
|
||||
async function checkUptime() {
|
||||
async function loadUptime() {
|
||||
setUptime((current) => ({
|
||||
status: current.status === 'ready' ? 'refreshing' : 'loading',
|
||||
checks: current.checks,
|
||||
@@ -330,65 +331,85 @@ function App() {
|
||||
updatedAt: current.updatedAt,
|
||||
}))
|
||||
|
||||
const startedAt = performance.now()
|
||||
const healthResult = await fetchJson(apiEndpoints.health, controller.signal)
|
||||
.then(() => ({ ok: true, label: 'Operational' }))
|
||||
.catch((error) => ({ ok: false, label: error.message }))
|
||||
|
||||
const apiResult = await fetchJson(apiEndpoints.teamsHealth, controller.signal)
|
||||
fetchJson(apiEndpoints.uptime, controller.signal)
|
||||
.then((data) => {
|
||||
const teamCount = data.teams?.length || data.squadrons?.length || 0
|
||||
return { ok: true, label: `${teamCount} sample team${teamCount === 1 ? '' : 's'} returned` }
|
||||
const latest = data.latest
|
||||
const checks = latest
|
||||
? [
|
||||
{
|
||||
name: 'Website',
|
||||
detail: 'App shell and static assets',
|
||||
ok: latest.website_ok,
|
||||
label: latest.details?.website?.label || (latest.website_ok ? 'Online' : 'Issue'),
|
||||
latency: latest.latency_ms,
|
||||
},
|
||||
{
|
||||
name: 'Health endpoint',
|
||||
detail: apiEndpoints.health,
|
||||
ok: latest.health_ok,
|
||||
label: latest.details?.health?.label || (latest.health_ok ? 'Operational' : 'Issue'),
|
||||
latency: latest.latency_ms,
|
||||
},
|
||||
{
|
||||
name: 'TSS data proxy',
|
||||
detail: apiEndpoints.teamsHealth,
|
||||
ok: latest.tss_ok,
|
||||
label: latest.details?.tss?.label || (latest.tss_ok ? 'Operational' : 'Issue'),
|
||||
latency: latest.details?.tss?.latency_ms || latest.latency_ms,
|
||||
},
|
||||
]
|
||||
: []
|
||||
|
||||
setUptime({
|
||||
status: 'ready',
|
||||
checks,
|
||||
history: (data.history || []).map((sample) => ({
|
||||
timestamp: new Date(sample.checked_at).getTime(),
|
||||
onlineChecks: [sample.website_ok, sample.health_ok, sample.tss_ok].filter(Boolean).length,
|
||||
totalChecks: 3,
|
||||
ok: sample.ok,
|
||||
})),
|
||||
updatedAt: latest ? new Date(latest.checked_at).getTime() : null,
|
||||
configured: data.configured,
|
||||
})
|
||||
})
|
||||
.catch((error) => ({ ok: false, label: error.message }))
|
||||
.catch((error) => {
|
||||
if (controller.signal.aborted) return
|
||||
|
||||
if (controller.signal.aborted) return
|
||||
|
||||
setUptime((current) => {
|
||||
const checks = [
|
||||
{
|
||||
name: 'Website',
|
||||
detail: 'App shell and static assets',
|
||||
ok: true,
|
||||
label: 'Online',
|
||||
latency: Math.round(performance.now() - startedAt),
|
||||
},
|
||||
{
|
||||
name: 'Health endpoint',
|
||||
detail: apiEndpoints.health,
|
||||
ok: healthResult.ok,
|
||||
label: healthResult.label,
|
||||
latency: Math.round(performance.now() - startedAt),
|
||||
},
|
||||
{
|
||||
name: 'TSS data proxy',
|
||||
detail: apiEndpoints.teamsHealth,
|
||||
ok: apiResult.ok,
|
||||
label: apiResult.label,
|
||||
latency: Math.round(performance.now() - startedAt),
|
||||
},
|
||||
]
|
||||
const onlineChecks = checks.filter((check) => check.ok).length
|
||||
|
||||
return {
|
||||
status: 'ready',
|
||||
updatedAt: Date.now(),
|
||||
checks,
|
||||
history: [
|
||||
...current.history,
|
||||
{
|
||||
timestamp: Date.now(),
|
||||
onlineChecks,
|
||||
totalChecks: checks.length,
|
||||
ok: onlineChecks === checks.length,
|
||||
},
|
||||
].slice(-48),
|
||||
}
|
||||
})
|
||||
setUptime({
|
||||
status: 'error',
|
||||
updatedAt: null,
|
||||
configured: false,
|
||||
history: [],
|
||||
checks: [
|
||||
{
|
||||
name: 'Website',
|
||||
detail: 'App shell and static assets',
|
||||
ok: true,
|
||||
label: 'Online',
|
||||
latency: 0,
|
||||
},
|
||||
{
|
||||
name: 'Health endpoint',
|
||||
detail: apiEndpoints.health,
|
||||
ok: false,
|
||||
label: error.message,
|
||||
latency: 0,
|
||||
},
|
||||
{
|
||||
name: 'TSS data proxy',
|
||||
detail: apiEndpoints.teamsHealth,
|
||||
ok: false,
|
||||
label: 'Uptime history unavailable',
|
||||
latency: 0,
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
checkUptime()
|
||||
const timer = window.setInterval(checkUptime, 30000)
|
||||
loadUptime()
|
||||
const timer = window.setInterval(loadUptime, 60000)
|
||||
|
||||
return () => {
|
||||
window.clearInterval(timer)
|
||||
@@ -1093,7 +1114,7 @@ function UptimePage({ uptime }) {
|
||||
{allOperational ? 'All systems operational' : 'Status check'}
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-text-soft">
|
||||
Last checked {updatedAt}. Refreshes every 30 seconds while this page is open.
|
||||
Last server snapshot {updatedAt}. The page refreshes once a minute.
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
@@ -1112,7 +1133,7 @@ function UptimePage({ uptime }) {
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Availability timeline</h2>
|
||||
<p className="mt-1 text-sm text-text-soft">
|
||||
Last {formatNumber(history.length)} checks from this browser session
|
||||
Last {formatNumber(history.length)} persisted server snapshots
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-fury-cyan">
|
||||
|
||||
Reference in New Issue
Block a user