File size: 2,554 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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} />}
    />
  );
}