mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-01 14:12:51 +00:00
The direct connection form treated any host containing a colon as a bare IPv6 address, so entering "100.66.69.36:6767" or "tcp://host:6767" built "tcp://[100.66.69.36:6767]:6767" and surfaced the browser's raw "Invalid URL" error. Normalize the host input first: strip a leading scheme and any path/query/fragment, split "host:port" (the inline port wins), and only treat unbracketed hosts with two or more colons as IPv6. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { serializeConnectionUriForStorage } from "@/utils/daemon-endpoints";
|
|
|
|
export interface DirectConnectionHostInput {
|
|
host: string;
|
|
port: string;
|
|
useTls: boolean;
|
|
password: string;
|
|
}
|
|
|
|
export interface DirectConnectionHostLabels {
|
|
hostRequired: string;
|
|
invalidPort: string;
|
|
}
|
|
|
|
export interface NormalizedDirectConnectionHost {
|
|
host: string;
|
|
/** Port typed into the host field, if any. Overrides the separate port field. */
|
|
port: string | null;
|
|
isIpv6: boolean;
|
|
}
|
|
|
|
const SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
const BRACKETED_IPV6_PATTERN = /^\[([^\]]*)\](?::(\d*))?$/;
|
|
const HOST_WITH_PORT_PATTERN = /^([^:]+):(\d+)$/;
|
|
|
|
/**
|
|
* Users often paste `host:port` or a full URL into the host field.
|
|
* Reduce it to a bare host (plus an optional port) before building the URI.
|
|
*/
|
|
export function normalizeDirectConnectionHost(input: string): NormalizedDirectConnectionHost {
|
|
let host = input.trim().replace(SCHEME_PATTERN, "");
|
|
const suffixIndex = host.search(/[/?#]/);
|
|
if (suffixIndex !== -1) {
|
|
host = host.slice(0, suffixIndex);
|
|
}
|
|
|
|
const bracketed = BRACKETED_IPV6_PATTERN.exec(host);
|
|
if (bracketed) {
|
|
return { host: bracketed[1] ?? "", port: bracketed[2] || null, isIpv6: true };
|
|
}
|
|
|
|
const hostWithPort = HOST_WITH_PORT_PATTERN.exec(host);
|
|
if (hostWithPort) {
|
|
return { host: hostWithPort[1] ?? "", port: hostWithPort[2] ?? null, isIpv6: false };
|
|
}
|
|
|
|
return { host, port: null, isIpv6: host.split(":").length > 2 };
|
|
}
|
|
|
|
export function buildDirectConnectionUri(
|
|
draft: DirectConnectionHostInput,
|
|
labels: DirectConnectionHostLabels,
|
|
): string {
|
|
const normalized = normalizeDirectConnectionHost(draft.host);
|
|
const port = Number((normalized.port ?? draft.port).trim());
|
|
if (!normalized.host) {
|
|
throw new Error(labels.hostRequired);
|
|
}
|
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
throw new Error(labels.invalidPort);
|
|
}
|
|
|
|
return serializeConnectionUriForStorage({
|
|
host: normalized.host,
|
|
port,
|
|
isIpv6: normalized.isIpv6,
|
|
useTls: draft.useTls,
|
|
...(draft.password ? { password: draft.password } : {}),
|
|
});
|
|
}
|