refactor: node tools and canvas host url

This commit is contained in:
Peter Steinberger
2025-12-27 01:36:24 +01:00
parent 52ca5c4aa2
commit c54e4d0900
19 changed files with 448 additions and 128 deletions

View File

@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import net from "node:net";
import os from "node:os";
import { resolveCanvasHostUrl } from "../canvas-host-url.js";
import {
getPairedNode,
listNodePairing,
@@ -188,23 +189,13 @@ export async function startNodeBridgeServer(
? opts.serverName.trim()
: os.hostname();
const isLoopbackHost = (host: string) => {
const normalized = host.trim().toLowerCase();
if (normalized === "localhost") return true;
if (normalized === "::1") return true;
if (normalized === "0.0.0.0" || normalized === "::") return true;
return normalized.startsWith("127.");
};
const buildCanvasHostUrl = (socket: net.Socket) => {
const port = opts.canvasHostPort;
if (!port) return undefined;
const localHost = socket.localAddress?.trim() ?? "";
const override = opts.canvasHostHost?.trim() ?? "";
const host = !localHost || isLoopbackHost(localHost) ? override : localHost;
if (!host) return undefined;
const formatted = host.includes(":") ? `[${host}]` : host;
return `http://${formatted}:${port}`;
return resolveCanvasHostUrl({
canvasPort: opts.canvasHostPort,
hostOverride: opts.canvasHostHost,
localAddress: socket.localAddress,
scheme: "http",
});
};
type ConnectionState = {

View File

@@ -0,0 +1,64 @@
type HostSource = string | null | undefined;
type CanvasHostUrlParams = {
canvasPort?: number;
hostOverride?: HostSource;
requestHost?: HostSource;
forwardedProto?: HostSource | HostSource[];
localAddress?: HostSource;
scheme?: "http" | "https";
};
const isLoopbackHost = (value: string) => {
const normalized = value.trim().toLowerCase();
if (!normalized) return false;
if (normalized === "localhost") return true;
if (normalized === "::1") return true;
if (normalized === "0.0.0.0" || normalized === "::") return true;
return normalized.startsWith("127.");
};
const normalizeHost = (value: HostSource, rejectLoopback: boolean) => {
if (!value) return "";
const trimmed = value.trim();
if (!trimmed) return "";
if (rejectLoopback && isLoopbackHost(trimmed)) return "";
return trimmed;
};
const parseHostHeader = (value: HostSource) => {
if (!value) return "";
try {
return new URL(`http://${String(value).trim()}`).hostname;
} catch {
return "";
}
};
const parseForwardedProto = (value: HostSource | HostSource[]) => {
if (Array.isArray(value)) return value[0];
return value;
};
export function resolveCanvasHostUrl(params: CanvasHostUrlParams) {
const port = params.canvasPort;
if (!port) return undefined;
const scheme =
params.scheme ??
(parseForwardedProto(params.forwardedProto)?.trim() === "https"
? "https"
: "http");
const override = normalizeHost(params.hostOverride, true);
const requestHost = normalizeHost(parseHostHeader(params.requestHost), !!override);
const localAddress = normalizeHost(
params.localAddress,
Boolean(override || requestHost),
);
const host = override || requestHost || localAddress;
if (!host) return undefined;
const formatted = host.includes(":") ? `[${host}]` : host;
return `${scheme}://${formatted}:${port}`;
}

View File

@@ -292,3 +292,22 @@ export async function updatePairedNodeMetadata(
await persistState(state, baseDir);
});
}
export async function renamePairedNode(
nodeId: string,
displayName: string,
baseDir?: string,
): Promise<NodePairingPairedNode | null> {
return await withLock(async () => {
const state = await loadState(baseDir);
const normalized = normalizeNodeId(nodeId);
const existing = state.pairedByNodeId[normalized];
if (!existing) return null;
const trimmed = displayName.trim();
if (!trimmed) throw new Error("displayName required");
const next: NodePairingPairedNode = { ...existing, displayName: trimmed };
state.pairedByNodeId[normalized] = next;
await persistState(state, baseDir);
return next;
});
}