feat: implement real-time service status tracking and health reporting

This commit is contained in:
2026-01-28 18:37:42 -06:00
parent 1733ee5f86
commit 7cc8267c2e
22 changed files with 308 additions and 284 deletions
+7 -5
View File
@@ -31,11 +31,13 @@ describe("BannerApiClient", () => {
it("should fetch status data", async () => {
const mockStatus = {
status: "operational",
bot: { status: "running", uptime: "1h" },
cache: { status: "connected", courses: "100", subjects: "50" },
banner_api: { status: "connected" },
timestamp: "2024-01-01T00:00:00Z",
status: "active",
version: "0.3.4",
commit: "abc1234",
services: {
web: { name: "web", status: "active" },
database: { name: "database", status: "connected" },
},
};
vi.mocked(fetch).mockResolvedValueOnce({
+1 -1
View File
@@ -6,7 +6,7 @@ export interface HealthResponse {
timestamp: string;
}
export type Status = "Disabled" | "Connected" | "Active" | "Healthy" | "Error";
export type Status = "starting" | "active" | "connected" | "disabled" | "error";
export interface ServiceInfo {
name: string;
+12 -9
View File
@@ -37,6 +37,9 @@ const SERVICE_ICONS: Record<string, typeof Bot> = {
bot: Bot,
banner: Globe,
discord: MessageCircle,
database: Activity,
web: Globe,
scraper: Clock,
};
interface ResponseTiming {
@@ -80,11 +83,11 @@ const formatNumber = (num: number): string => {
const getStatusIcon = (status: Status | "Unreachable"): StatusIcon => {
const statusMap: Record<Status | "Unreachable", StatusIcon> = {
Active: { icon: CheckCircle, color: "green" },
Connected: { icon: CheckCircle, color: "green" },
Healthy: { icon: CheckCircle, color: "green" },
Disabled: { icon: Circle, color: "gray" },
Error: { icon: XCircle, color: "red" },
active: { icon: CheckCircle, color: "green" },
connected: { icon: CheckCircle, color: "green" },
starting: { icon: Hourglass, color: "orange" },
disabled: { icon: Circle, color: "gray" },
error: { icon: XCircle, color: "red" },
Unreachable: { icon: WifiOff, color: "red" },
};
@@ -93,9 +96,9 @@ const getStatusIcon = (status: Status | "Unreachable"): StatusIcon => {
const getOverallHealth = (state: StatusState): Status | "Unreachable" => {
if (state.mode === "timeout") return "Unreachable";
if (state.mode === "error") return "Error";
if (state.mode === "error") return "error";
if (state.mode === "response") return state.status.status;
return "Error";
return "error";
};
const getServices = (state: StatusState): Service[] => {
@@ -116,8 +119,8 @@ const StatusDisplay = ({ status }: { status: Status | "Unreachable" }) => {
<Text
size="2"
style={{
color: status === "Disabled" ? "var(--gray-11)" : undefined,
opacity: status === "Disabled" ? 0.7 : undefined,
color: status === "disabled" ? "var(--gray-11)" : undefined,
opacity: status === "disabled" ? 0.7 : undefined,
}}
>
{status}