Relay pairing offer v2 (E2EE) + host connections
This commit is contained in:
@@ -1,31 +1,35 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { useDaemonRegistry, type DaemonProfile } from "./daemon-registry-context";
|
||||
import { useDaemonRegistry, type HostProfile } from "./daemon-registry-context";
|
||||
|
||||
export type ActiveConnection =
|
||||
| { type: "direct"; endpoint: string; display: string }
|
||||
| { type: "relay"; endpoint: string; display: "relay" };
|
||||
|
||||
export type ConnectionState =
|
||||
| { status: "idle"; lastError: null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: false }
|
||||
| { status: "connecting"; lastError: null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean }
|
||||
| { status: "online"; lastError: null; lastOnlineAt: string; agentListReady: boolean; hasEverReceivedAgentList: boolean }
|
||||
| { status: "offline"; lastError: string | null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean }
|
||||
| { status: "error"; lastError: string; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean };
|
||||
| { status: "idle"; activeConnection: ActiveConnection | null; lastError: null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: false }
|
||||
| { status: "connecting"; activeConnection: ActiveConnection | null; lastError: null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean }
|
||||
| { status: "online"; activeConnection: ActiveConnection | null; lastError: null; lastOnlineAt: string; agentListReady: boolean; hasEverReceivedAgentList: boolean }
|
||||
| { status: "offline"; activeConnection: ActiveConnection | null; lastError: string | null; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean }
|
||||
| { status: "error"; activeConnection: ActiveConnection | null; lastError: string; lastOnlineAt: string | null; agentListReady: false; hasEverReceivedAgentList: boolean };
|
||||
|
||||
export type ConnectionStatus = ConnectionState["status"];
|
||||
|
||||
type ConnectionStateUpdate =
|
||||
| { status: "idle" }
|
||||
| { status: "connecting"; lastOnlineAt?: string | null }
|
||||
| { status: "online"; lastOnlineAt: string; agentListReady?: boolean }
|
||||
| { status: "offline"; lastError?: string | null; lastOnlineAt?: string | null }
|
||||
| { status: "error"; lastError: string; lastOnlineAt?: string | null };
|
||||
| { status: "connecting"; activeConnection?: ActiveConnection | null; lastOnlineAt?: string | null }
|
||||
| { status: "online"; activeConnection?: ActiveConnection | null; lastOnlineAt: string; agentListReady?: boolean }
|
||||
| { status: "offline"; activeConnection?: ActiveConnection | null; lastError?: string | null; lastOnlineAt?: string | null }
|
||||
| { status: "error"; activeConnection?: ActiveConnection | null; lastError: string; lastOnlineAt?: string | null };
|
||||
|
||||
export type DaemonConnectionRecord = {
|
||||
daemon: DaemonProfile;
|
||||
daemon: HostProfile;
|
||||
} & ConnectionState;
|
||||
|
||||
interface DaemonConnectionsContextValue {
|
||||
connectionStates: Map<string, DaemonConnectionRecord>;
|
||||
isLoading: boolean;
|
||||
updateConnectionStatus: (daemonId: string, update: ConnectionStateUpdate) => void;
|
||||
updateConnectionStatus: (serverId: string, update: ConnectionStateUpdate) => void;
|
||||
}
|
||||
|
||||
const DaemonConnectionsContext = createContext<DaemonConnectionsContextValue | null>(null);
|
||||
@@ -33,6 +37,7 @@ const DaemonConnectionsContext = createContext<DaemonConnectionsContextValue | n
|
||||
function createDefaultConnectionState(): ConnectionState {
|
||||
return {
|
||||
status: "idle",
|
||||
activeConnection: null,
|
||||
lastError: null,
|
||||
lastOnlineAt: null,
|
||||
agentListReady: false,
|
||||
@@ -48,6 +53,7 @@ function resolveNextConnectionState(
|
||||
case "idle":
|
||||
return {
|
||||
status: "idle",
|
||||
activeConnection: existing.activeConnection ?? null,
|
||||
lastError: null,
|
||||
lastOnlineAt: existing.lastOnlineAt,
|
||||
agentListReady: false,
|
||||
@@ -56,6 +62,7 @@ function resolveNextConnectionState(
|
||||
case "connecting":
|
||||
return {
|
||||
status: "connecting",
|
||||
activeConnection: update.activeConnection ?? existing.activeConnection ?? null,
|
||||
lastError: null,
|
||||
lastOnlineAt: update.lastOnlineAt ?? existing.lastOnlineAt,
|
||||
agentListReady: false,
|
||||
@@ -66,6 +73,7 @@ function resolveNextConnectionState(
|
||||
|
||||
return {
|
||||
status: "online",
|
||||
activeConnection: update.activeConnection ?? existing.activeConnection ?? null,
|
||||
lastError: null,
|
||||
lastOnlineAt: update.lastOnlineAt,
|
||||
agentListReady: currentAgentListReady,
|
||||
@@ -75,6 +83,7 @@ function resolveNextConnectionState(
|
||||
case "offline":
|
||||
return {
|
||||
status: "offline",
|
||||
activeConnection: update.activeConnection ?? existing.activeConnection ?? null,
|
||||
lastError: update.lastError ?? null,
|
||||
lastOnlineAt: update.lastOnlineAt ?? existing.lastOnlineAt,
|
||||
agentListReady: false,
|
||||
@@ -83,6 +92,7 @@ function resolveNextConnectionState(
|
||||
case "error":
|
||||
return {
|
||||
status: "error",
|
||||
activeConnection: update.activeConnection ?? existing.activeConnection ?? null,
|
||||
lastError: update.lastError,
|
||||
lastOnlineAt: update.lastOnlineAt ?? existing.lastOnlineAt,
|
||||
agentListReady: false,
|
||||
@@ -91,13 +101,13 @@ function resolveNextConnectionState(
|
||||
}
|
||||
}
|
||||
|
||||
function logConnectionLifecycle(daemon: DaemonProfile, previous: ConnectionState, next: ConnectionState) {
|
||||
function logConnectionLifecycle(daemon: HostProfile, previous: ConnectionState, next: ConnectionState) {
|
||||
const severity: "info" | "warn" = next.status === "error" ? "warn" : "info";
|
||||
const logger = severity === "warn" ? console.warn : console.info;
|
||||
|
||||
const logPayload = {
|
||||
event: "daemon_connection_state",
|
||||
daemonId: daemon.id,
|
||||
serverId: daemon.serverId,
|
||||
label: daemon.label,
|
||||
from: previous.status,
|
||||
to: next.status,
|
||||
@@ -127,8 +137,8 @@ export function DaemonConnectionsProvider({ children }: { children: ReactNode })
|
||||
setConnectionStates((prev) => {
|
||||
const next = new Map<string, DaemonConnectionRecord>();
|
||||
for (const daemon of daemons) {
|
||||
const existing = prev.get(daemon.id);
|
||||
next.set(daemon.id, {
|
||||
const existing = prev.get(daemon.serverId);
|
||||
next.set(daemon.serverId, {
|
||||
daemon,
|
||||
...(existing ?? createDefaultConnectionState()),
|
||||
});
|
||||
@@ -139,9 +149,9 @@ export function DaemonConnectionsProvider({ children }: { children: ReactNode })
|
||||
|
||||
|
||||
const updateConnectionStatus = useCallback(
|
||||
(daemonId: string, update: ConnectionStateUpdate) => {
|
||||
(serverId: string, update: ConnectionStateUpdate) => {
|
||||
setConnectionStates((prev) => {
|
||||
const existing = prev.get(daemonId);
|
||||
const existing = prev.get(serverId);
|
||||
if (!existing) {
|
||||
return prev;
|
||||
}
|
||||
@@ -156,7 +166,7 @@ export function DaemonConnectionsProvider({ children }: { children: ReactNode })
|
||||
}
|
||||
|
||||
const next = new Map(prev);
|
||||
next.set(daemonId, { daemon: existing.daemon, ...nextState });
|
||||
next.set(serverId, { daemon: existing.daemon, ...nextState });
|
||||
return next;
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user