Gateway: status route with per-route results, test push, reconnect and unpair
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 24s

This commit is contained in:
gib committed 2026-11-01 15:04:30 -05:00
1 parent e6fd2b8f51
commit 9707daf1fe
9 files changed
+398 -55

No files matched your search

+40 -1
View File
@@ -4,7 +4,17 @@
// the thread's `updatedAt` moves.
import { threadPhase, type Phase } from "./awareness.ts";
import type { Feed, FeedItem, HostSummary, Message, PendingRequest, Summary, ThreadDetail } from "./contract.ts";
import type {
Feed,
FeedItem,
HostStatusDetail,
HostSummary,
Message,
PendingRequest,
RouteKind,
Summary,
ThreadDetail,
} from "./contract.ts";
import type { T3Host } from "./t3/host.ts";
import {
isApprovalRequest,
@@ -141,6 +151,35 @@ export const hostSummary = (host: T3Host): HostSummary => ({
error: host.lastError,
});
/** A host with its routes, for the status screens. `tokenExpiresAt` comes from the hosts table. */
export function hostStatusDetail(host: T3Host, tokenExpiresAt: string | null): HostStatusDetail {
return {
...hostSummary(host),
activeUrl: host.status === "online" ? host.url : null,
routes: host.config.urls.map((url) => {
const attempt = host.routeAttempts.get(url);
return {
url,
kind: routeKind(url),
result: attempt ? (attempt.ok ? "ok" : "failed") : "untried",
error: attempt?.error ?? null,
checkedAt: attempt?.at ?? null,
};
}),
onlineSince: host.onlineSince,
lastOnlineAt: host.lastOnlineAt,
nextRetryAt: host.nextRetryAt,
tokenExpiresAt,
serverVersion: host.serverVersion,
};
}
export function routeKind(url: string): RouteKind {
const { protocol, hostname } = new URL(url);
if (protocol === "https:") return "public";
return hostname.includes("vpn") ? "vpn" : "direct";
}
function feedItem(host: T3Host, thread: ThreadShell, phase: Phase, summary: ThreadSummary | undefined): FeedItem {
return {
hostId: host.id,