81 lines
2.5 KiB
React
81 lines
2.5 KiB
React
import React from "react";
|
|
import { Box, Text } from "ink";
|
|
import { HeaderSwr } from "../../tui-components/HeaderSwr.jsx";
|
|
import { Sparkline } from "../../tui-components/Sparkline.jsx";
|
|
import { StatusBadge } from "../../tui-components/StatusBadge.jsx";
|
|
|
|
function formatUptime(seconds) {
|
|
const d = Math.floor(seconds / 86400);
|
|
const h = Math.floor((seconds % 86400) / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
if (d > 0) return `${d}d ${h}h`;
|
|
if (h > 0) return `${h}h ${m}m`;
|
|
return `${m}m`;
|
|
}
|
|
|
|
function OverviewContent({ data }) {
|
|
const reqs = data?.requests24h ?? 0;
|
|
const cost = (data?.cost24h ?? 0).toFixed(4);
|
|
const uptimeSecs = data?.uptimeSeconds ?? 0;
|
|
const sparkData = data?.requestsSparkline ?? [];
|
|
|
|
return (
|
|
<Box flexDirection="column" gap={1}>
|
|
<Box gap={4}>
|
|
<Box flexDirection="column">
|
|
<Text bold>Server</Text>
|
|
<StatusBadge status={data?.status ?? "unknown"} />
|
|
</Box>
|
|
<Box flexDirection="column">
|
|
<Text bold>Uptime</Text>
|
|
<Text>{formatUptime(uptimeSecs)}</Text>
|
|
</Box>
|
|
<Box flexDirection="column">
|
|
<Text bold>Requests/24h</Text>
|
|
<Box>
|
|
<Text>{reqs.toLocaleString()} </Text>
|
|
<Sparkline data={sparkData} width={12} />
|
|
</Box>
|
|
</Box>
|
|
<Box flexDirection="column">
|
|
<Text bold>Cost/24h</Text>
|
|
<Text color="yellow">${cost}</Text>
|
|
</Box>
|
|
</Box>
|
|
{data?.recentActivity?.length > 0 && (
|
|
<Box flexDirection="column" marginTop={1}>
|
|
<Text bold dimColor>
|
|
Recent Activity
|
|
</Text>
|
|
{data.recentActivity.slice(0, 5).map((r, i) => (
|
|
<Box key={i} gap={2}>
|
|
<Text dimColor>{r.time}</Text>
|
|
<Text color={r.status < 400 ? "green" : "red"}>{r.status < 400 ? "✓" : "✗"}</Text>
|
|
<Text>{r.path}</Text>
|
|
<Text dimColor>{r.model}</Text>
|
|
<Text dimColor>{r.duration}ms</Text>
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export default function Overview({ baseUrl, apiKey }) {
|
|
const fetcher = React.useCallback(async () => {
|
|
const res = await fetch(`${baseUrl}/api/monitoring/health`, {
|
|
headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
|
|
});
|
|
return res.ok ? res.json() : null;
|
|
}, [baseUrl, apiKey]);
|
|
|
|
return (
|
|
<HeaderSwr
|
|
fetcher={fetcher}
|
|
interval={5000}
|
|
render={(data) => <OverviewContent data={data} />}
|
|
/>
|
|
);
|
|
}
|