File size: 4,018 Bytes
96e86e5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | import os from "node:os";
function normalizeHost(value: string | null | undefined): string {
return (value ?? "").trim();
}
function isLoopbackHost(host: string): boolean {
const normalized = normalizeHost(host).toLowerCase();
return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1";
}
function isWildcardHost(host: string): boolean {
const normalized = normalizeHost(host).toLowerCase();
return normalized === "0.0.0.0" || normalized === "::";
}
function formatOrigin(protocol: string, host: string, port: number): string {
const normalizedHost = host.includes(":") && !host.startsWith("[") && !host.endsWith("]")
? `[${host}]`
: host;
return `${protocol}//${normalizedHost}:${port}`;
}
function pushCandidate(
candidates: string[],
seen: Set<string>,
rawUrl: string | null | undefined,
): void {
const trimmed = rawUrl?.trim();
if (!trimmed) return;
try {
const normalized = new URL(trimmed).origin;
if (seen.has(normalized)) return;
seen.add(normalized);
candidates.push(normalized);
} catch {
// Ignore malformed candidates.
}
}
export function choosePrimaryRuntimeApiUrl(input: {
authPublicBaseUrl?: string | null;
allowedHostnames: string[];
bindHost: string;
port: number;
}): string {
const explicitPublicBaseUrl = input.authPublicBaseUrl?.trim();
if (explicitPublicBaseUrl) {
try {
return new URL(explicitPublicBaseUrl).origin;
} catch {
// Fall through to derived candidates if config parsing drifted.
}
}
const allowedHostname = input.allowedHostnames
.map((value) => value.trim())
.find(Boolean);
if (allowedHostname) {
return formatOrigin("http:", allowedHostname, input.port);
}
const bindHost = normalizeHost(input.bindHost);
if (bindHost && !isWildcardHost(bindHost)) {
return formatOrigin("http:", bindHost, input.port);
}
return formatOrigin("http:", "localhost", input.port);
}
export function buildRuntimeApiCandidateUrls(input: {
authPublicBaseUrl?: string | null;
allowedHostnames: string[];
bindHost: string;
port: number;
networkInterfacesMap?: NodeJS.Dict<os.NetworkInterfaceInfo[]>;
}): string[] {
const candidates: string[] = [];
const seen = new Set<string>();
const explicitPublicBaseUrl = input.authPublicBaseUrl?.trim() ?? "";
const explicitOrigin = (() => {
if (!explicitPublicBaseUrl) return null;
try {
return new URL(explicitPublicBaseUrl).origin;
} catch {
return null;
}
})();
const protocol = explicitOrigin ? new URL(explicitOrigin).protocol : "http:";
pushCandidate(candidates, seen, explicitOrigin);
for (const rawHost of input.allowedHostnames) {
const host = normalizeHost(rawHost);
if (!host) continue;
pushCandidate(candidates, seen, formatOrigin(protocol, host, input.port));
}
const bindHost = normalizeHost(input.bindHost);
if (bindHost && !isWildcardHost(bindHost)) {
pushCandidate(candidates, seen, formatOrigin(protocol, bindHost, input.port));
}
if (explicitOrigin) {
const hostname = new URL(explicitOrigin).hostname;
if (isLoopbackHost(hostname)) {
pushCandidate(candidates, seen, formatOrigin(protocol, "host.docker.internal", input.port));
}
}
const interfaces = input.networkInterfacesMap ?? os.networkInterfaces();
for (const entries of Object.values(interfaces)) {
for (const entry of entries ?? []) {
if (entry.internal) continue;
const host = normalizeHost(entry.address);
if (!host || isLoopbackHost(host) || isWildcardHost(host)) continue;
pushCandidate(candidates, seen, formatOrigin(protocol, host, input.port));
}
}
if (candidates.length === 0) {
pushCandidate(
candidates,
seen,
choosePrimaryRuntimeApiUrl({
authPublicBaseUrl: input.authPublicBaseUrl,
allowedHostnames: input.allowedHostnames,
bindHost: input.bindHost,
port: input.port,
}),
);
}
return candidates;
}
|