HealthcheckAlert.tsx raw
1 import { AlertTriangleIcon, CheckCircle2Icon } from "lucide-react";
2 import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert";
3 import { useHealthCheck } from "src/hooks/useHealthCheck";
4 import { AlbyInfoIncident, HealthAlarm } from "src/types";
5 import { ExternalLinkButton } from "../ui/custom/external-link-button";
6
7 export function HealthCheckAlert() {
8 const { data: health } = useHealthCheck();
9
10 const ok = !health?.alarms?.length;
11
12 if (!health) {
13 return null;
14 }
15
16 if (ok) {
17 return null;
18 }
19
20 function getAlarmTitle(alarm: HealthAlarm) {
21 // TODO: could show extra data from alarm.rawDetails
22 // for some alarm types
23 try {
24 switch (alarm.kind) {
25 case "alby_service":
26 return (
27 "Alby Services: " +
28 (alarm.rawDetails as AlbyInfoIncident[])
29 ?.map((incident) => `${incident.name} (${incident.status})`)
30 .join(", ")
31 );
32 case "channels_offline":
33 return "One or more channels are offline";
34 case "node_not_ready":
35 return "Node is not ready";
36 case "nostr_relay_offline":
37 return (
38 "Could not connect to relay: " +
39 (alarm.rawDetails as string[]).join(", ")
40 );
41 case "vss_no_subscription":
42 return "Your lightning channel data is stored encrypted by Alby's Versioned Storage Service which is a paid feature. Restart your subscription or send your funds to another wallet as soon as possible.";
43 }
44 } catch (error) {
45 console.error("failed to parse alarm details", alarm.kind, error);
46 }
47 return alarm.kind || "Unknown";
48 }
49
50 return (
51 <>
52 <Alert className="animate-highlight">
53 {ok ? (
54 <>
55 <CheckCircle2Icon className="h-4 w-4" />
56 <AlertTitle>Alby Hub is running smoothly</AlertTitle>
57 </>
58 ) : (
59 <>
60 <AlertTriangleIcon className="h-4 w-4" />
61 <AlertTitle>
62 {health.alarms.length} issues impacting your hub were found
63 </AlertTitle>
64 </>
65 )}
66 <AlertDescription>
67 {health.alarms?.length && (
68 <ul className="mt-2 whitespace-pre-wrap list-disc list-inside">
69 {health.alarms?.map((alarm) => (
70 <li key={alarm.kind}>{getAlarmTitle(alarm)}</li>
71 ))}
72 </ul>
73 )}
74 <div className="mt-4 flex gap-2">
75 <ExternalLinkButton
76 size="sm"
77 variant="secondary"
78 to="https://status.getalby.com"
79 >
80 Alby Service Status
81 </ExternalLinkButton>
82 <ExternalLinkButton
83 size="sm"
84 variant="secondary"
85 to="https://guides.getalby.com/user-guide/alby-hub/faq/what-happens-if-the-healthcheck-indicator-turns-red"
86 >
87 Learn More
88 </ExternalLinkButton>
89 </div>
90 </AlertDescription>
91 </Alert>
92 </>
93 );
94 }
95