Add relay health summary banner

This commit is contained in:
thePR0M3TH3AN committed 2026-01-30 11:08:02 -05:00
1 parent 9c8cc465ce
commit 09c0ac8127
3 files changed
+76 -2

No files matched your search

+12
View File
@@ -716,6 +716,18 @@
Refresh
</button>
</div>
<div
id="relayHealthSummary"
class="rounded-md border border-border/70 bg-surface/40 p-3"
>
<div class="flex flex-wrap items-center justify-between gap-2">
<p class="text-2xs font-semibold uppercase tracking-extra-wide text-muted-strong">
Relay status
</p>
<p id="relayHealthCounts" class="text-xs font-semibold text-text"></p>
</div>
<p id="relayHealthMessage" class="mt-1 text-xs text-muted"></p>
</div>
<label
for="relayHealthTelemetryOptIn"
class="inline-flex items-center gap-2 text-xs font-medium text-muted-strong"
+21 -2
View File
@@ -1,6 +1,8 @@
import { devLogger, userLogger } from "../utils/logger.js";
const TELEMETRY_STORAGE_KEY = "bitvid:relay-health-telemetry-opt-in";
const PERSISTENT_FAILURE_THRESHOLD = 3;
const USER_LOG_COOLDOWN_MS = 5 * 60 * 1000;
function resolveLogger(logger) {
if (logger && logger.dev && logger.user) {
@@ -90,8 +92,10 @@ class RelayHealthService {
connected: false,
lastLatencyMs: null,
errorCount: 0,
consecutiveFailures: 0,
lastCheckedAt: null,
lastErrorAt: null,
lastUserLogAt: null,
});
}
@@ -129,6 +133,7 @@ class RelayHealthService {
const state = this.ensureRelayState(relayUrl);
state.connected = true;
state.lastCheckedAt = Date.now();
state.consecutiveFailures = 0;
if (Number.isFinite(latencyMs)) {
state.lastLatencyMs = Math.max(0, Math.round(latencyMs));
}
@@ -140,10 +145,12 @@ class RelayHealthService {
recordRelayFailure(relayUrl, error) {
const state = this.ensureRelayState(relayUrl);
const now = Date.now();
state.connected = false;
state.errorCount += 1;
state.lastCheckedAt = Date.now();
state.lastErrorAt = Date.now();
state.consecutiveFailures += 1;
state.lastCheckedAt = now;
state.lastErrorAt = now;
if (this.nostrClient && typeof this.nostrClient.markRelayUnreachable === "function") {
this.nostrClient.markRelayUnreachable(relayUrl, 60000, {
@@ -155,6 +162,18 @@ class RelayHealthService {
relayUrl,
error,
});
if (
state.consecutiveFailures >= PERSISTENT_FAILURE_THRESHOLD &&
(!state.lastUserLogAt || now - state.lastUserLogAt >= USER_LOG_COOLDOWN_MS)
) {
state.lastUserLogAt = now;
this.logger.user.warn("[relayHealth] Relay unreachable after repeated failures.", {
relayUrl,
errorCount: state.consecutiveFailures,
lastError: error,
});
}
}
async checkRelay(relayUrl) {
+43
View File
@@ -1252,6 +1252,9 @@ export class ProfileModalController {
this.addRelayButton = null;
this.restoreRelaysButton = null;
this.relayHealthPanel = null;
this.relayHealthSummary = null;
this.relayHealthCounts = null;
this.relayHealthMessage = null;
this.relayHealthList = null;
this.relayHealthStatus = null;
this.relayHealthRefreshButton = null;
@@ -1579,6 +1582,12 @@ export class ProfileModalController {
document.getElementById("restoreRelaysBtn") || null;
this.relayHealthPanel =
document.getElementById("relayHealthPanel") || null;
this.relayHealthSummary =
document.getElementById("relayHealthSummary") || null;
this.relayHealthCounts =
document.getElementById("relayHealthCounts") || null;
this.relayHealthMessage =
document.getElementById("relayHealthMessage") || null;
this.relayHealthList =
document.getElementById("relayHealthList") || null;
this.relayHealthStatus =
@@ -7294,11 +7303,45 @@ export class ProfileModalController {
this.relayHealthStatus.textContent = text;
}
updateRelayHealthSummary(snapshot = []) {
if (!this.relayHealthCounts || !this.relayHealthMessage) {
return;
}
const entries = Array.isArray(snapshot) ? snapshot : [];
const total = entries.length;
const healthy = entries.filter((entry) => entry.connected).length;
const failed = Math.max(0, total - healthy);
this.relayHealthCounts.textContent = `${healthy} healthy • ${failed} failed`;
let message = "";
if (!total) {
message = "No relays configured.";
} else if (failed > 0) {
message =
failed === total
? "All relays are unreachable. Check your connection or update your relay list."
: `${failed} relay${failed === 1 ? "" : "s"} unreachable. Playback and publishing may be delayed.`;
} else {
message = "All relays reachable.";
}
this.relayHealthMessage.className = "mt-1 text-xs text-muted";
if (failed > 0) {
this.relayHealthMessage.classList.add("text-status-warning");
} else if (total > 0) {
this.relayHealthMessage.classList.add("text-status-success");
}
this.relayHealthMessage.textContent = message;
}
renderRelayHealthSnapshot(snapshot = []) {
if (!this.relayHealthList) {
return;
}
this.updateRelayHealthSummary(snapshot);
this.relayHealthList.innerHTML = "";
if (!snapshot.length) {