Avoid false Monero outage states in UI

This commit is contained in:
xmrcheckout
2026-03-26 15:46:31 +00:00
parent 10011ffb8c
commit d039d5a984
3 changed files with 151 additions and 57 deletions
+51 -19
View File
@@ -85,6 +85,51 @@ type SystemStatusSummary = {
last_reconcile_completed_at: string | null; last_reconcile_completed_at: string | null;
last_reconcile_error: string | null; last_reconcile_error: string | null;
}; };
const walletRpcBadge = (systemStatus: SystemStatusSummary | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Wallet RPC unavailable",
};
}
if (systemStatus.wallet_rpc === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Wallet RPC ok",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Wallet RPC down",
};
};
const daemonBadge = (systemStatus: SystemStatusSummary | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon unavailable",
};
}
if (systemStatus.daemon === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Daemon connected",
};
}
if (systemStatus.daemon === "unknown") {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon not configured",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Daemon down",
};
};
export default async function DashboardPage({ export default async function DashboardPage({
searchParams, searchParams,
}: { }: {
@@ -259,6 +304,8 @@ export default async function DashboardPage({
const confirmedCount = allInvoices.filter( const confirmedCount = allInvoices.filter(
(invoice) => invoice.status === "confirmed" (invoice) => invoice.status === "confirmed"
).length; ).length;
const walletStatusBadge = walletRpcBadge(systemStatus);
const daemonStatusBadge = daemonBadge(systemStatus);
const lastReconcileCompleted = systemStatus?.last_reconcile_completed_at const lastReconcileCompleted = systemStatus?.last_reconcile_completed_at
? formatRelativeTime(systemStatus.last_reconcile_completed_at) ? formatRelativeTime(systemStatus.last_reconcile_completed_at)
: null; : null;
@@ -377,29 +424,14 @@ export default async function DashboardPage({
</p> </p>
<div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]"> <div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]">
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${walletStatusBadge.className}`}
systemStatus?.wallet_rpc === "ok"
? "bg-emerald-100 text-emerald-900"
: "bg-red-100 text-red-700"
}`}
> >
Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"} {walletStatusBadge.label}
</span> </span>
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${daemonStatusBadge.className}`}
systemStatus?.daemon === "ok"
? "bg-emerald-100 text-emerald-900"
: systemStatus?.daemon === "unknown"
? "bg-amber-100 text-amber-900"
: "bg-red-100 text-red-700"
}`}
> >
Daemon{" "} {daemonStatusBadge.label}
{systemStatus?.daemon === "ok"
? "connected"
: systemStatus?.daemon === "unknown"
? "not configured"
: "down"}
</span> </span>
</div> </div>
</div> </div>
+50 -19
View File
@@ -47,6 +47,50 @@ type SystemStatusResponse = {
last_reconcile_error: string | null; last_reconcile_error: string | null;
}; };
const walletRpcBadge = (systemStatus: SystemStatusResponse | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Wallet RPC unavailable",
};
}
if (systemStatus.wallet_rpc === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Wallet RPC ok",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Wallet RPC down",
};
};
const daemonBadge = (systemStatus: SystemStatusResponse | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon unavailable",
};
}
if (systemStatus.daemon === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Daemon connected",
};
}
if (systemStatus.daemon === "unknown") {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon not configured",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Daemon down",
};
};
const formatStatus = (status: InvoiceStatus) => { const formatStatus = (status: InvoiceStatus) => {
if (status === "payment_detected") { if (status === "payment_detected") {
return "Payment detected"; return "Payment detected";
@@ -219,6 +263,8 @@ export default async function InvoiceStatusDetailPage({
const createdTimestamp = formatTimestamp(invoice.created_at); const createdTimestamp = formatTimestamp(invoice.created_at);
const expiresTimestamp = formatTimestamp(invoice.expires_at); const expiresTimestamp = formatTimestamp(invoice.expires_at);
const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null); const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null);
const walletStatusBadge = walletRpcBadge(systemStatus);
const daemonStatusBadge = daemonBadge(systemStatus);
return ( return (
<main className="px-[6vw] pb-20 pt-10 text-ink"> <main className="px-[6vw] pb-20 pt-10 text-ink">
@@ -284,29 +330,14 @@ export default async function InvoiceStatusDetailPage({
</p> </p>
<div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]"> <div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]">
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${walletStatusBadge.className}`}
systemStatus?.wallet_rpc === "ok"
? "bg-emerald-100 text-emerald-900"
: "bg-red-100 text-red-700"
}`}
> >
Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"} {walletStatusBadge.label}
</span> </span>
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${daemonStatusBadge.className}`}
systemStatus?.daemon === "ok"
? "bg-emerald-100 text-emerald-900"
: systemStatus?.daemon === "unknown"
? "bg-amber-100 text-amber-900"
: "bg-red-100 text-red-700"
}`}
> >
Daemon{" "} {daemonStatusBadge.label}
{systemStatus?.daemon === "ok"
? "connected"
: systemStatus?.daemon === "unknown"
? "not configured"
: "down"}
</span> </span>
</div> </div>
</div> </div>
+50 -19
View File
@@ -62,6 +62,50 @@ type SystemStatusResponse = {
last_reconcile_error: string | null; last_reconcile_error: string | null;
}; };
const walletRpcBadge = (systemStatus: SystemStatusResponse | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Wallet RPC unavailable",
};
}
if (systemStatus.wallet_rpc === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Wallet RPC ok",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Wallet RPC down",
};
};
const daemonBadge = (systemStatus: SystemStatusResponse | null) => {
if (!systemStatus) {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon unavailable",
};
}
if (systemStatus.daemon === "ok") {
return {
className: "bg-emerald-100 text-emerald-900",
label: "Daemon connected",
};
}
if (systemStatus.daemon === "unknown") {
return {
className: "bg-amber-100 text-amber-900",
label: "Daemon not configured",
};
}
return {
className: "bg-red-100 text-red-700",
label: "Daemon down",
};
};
const formatStatus = (status: InvoiceStatus) => { const formatStatus = (status: InvoiceStatus) => {
if (status === "payment_detected") { if (status === "payment_detected") {
return "Payment detected"; return "Payment detected";
@@ -161,6 +205,8 @@ export default async function BtcpayModalInvoicePage({
const createdTimestamp = formatTimestamp(invoice.created_at); const createdTimestamp = formatTimestamp(invoice.created_at);
const expiresTimestamp = formatTimestamp(invoice.expires_at); const expiresTimestamp = formatTimestamp(invoice.expires_at);
const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null); const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null);
const walletStatusBadge = walletRpcBadge(systemStatus);
const daemonStatusBadge = daemonBadge(systemStatus);
const isBtcpayInvoice = Boolean(invoice.btcpay_amount && invoice.btcpay_currency); const isBtcpayInvoice = Boolean(invoice.btcpay_amount && invoice.btcpay_currency);
const useClassicCheckout = const useClassicCheckout =
isBtcpayInvoice && invoice.btcpay_checkout_style === "btcpay_classic"; isBtcpayInvoice && invoice.btcpay_checkout_style === "btcpay_classic";
@@ -268,29 +314,14 @@ export default async function BtcpayModalInvoicePage({
</p> </p>
<div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]"> <div className="flex flex-wrap gap-2 text-[0.7rem] font-semibold uppercase tracking-[0.12em]">
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${walletStatusBadge.className}`}
systemStatus?.wallet_rpc === "ok"
? "bg-emerald-100 text-emerald-900"
: "bg-red-100 text-red-700"
}`}
> >
Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"} {walletStatusBadge.label}
</span> </span>
<span <span
className={`rounded-full px-3 py-1 ${ className={`rounded-full px-3 py-1 ${daemonStatusBadge.className}`}
systemStatus?.daemon === "ok"
? "bg-emerald-100 text-emerald-900"
: systemStatus?.daemon === "unknown"
? "bg-amber-100 text-amber-900"
: "bg-red-100 text-red-700"
}`}
> >
Daemon{" "} {daemonStatusBadge.label}
{systemStatus?.daemon === "ok"
? "connected"
: systemStatus?.daemon === "unknown"
? "not configured"
: "down"}
</span> </span>
</div> </div>
</div> </div>