6edf8991fa
Hop-State: A_06FP90SDWE5709BB1G6J4B8 Hop-Proposal: R_06FP90RS27BNBV5H5N053A0 Hop-Task: T_06FP905Q50D1ZFPC56N3DZG Hop-Attempt: AT_06FP905Q50E6P72ZMVT4038
70 lines
1.9 KiB
TypeScript
70 lines
1.9 KiB
TypeScript
import { parse } from 'tldts';
|
|
|
|
const LEGACY_SWARM_SEED_DOMAINS: Readonly<Record<string, string>> = {
|
|
'node.synapsis.social': 'synapsis.social',
|
|
};
|
|
|
|
export function normalizeNodeDomain(domain: string): string {
|
|
return domain
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/^https?:\/\//, '')
|
|
.replace(/\/.*$/, '')
|
|
.replace(/^@/, '');
|
|
}
|
|
|
|
/**
|
|
* Return the canonical host for a publicly addressable swarm node.
|
|
*
|
|
* The swarm is a public network, so local development hosts, IP addresses,
|
|
* special-use names, and made-up TLDs must never enter the node registry.
|
|
* Ports are retained because a public hostname may intentionally expose
|
|
* Synapsis on a non-standard port.
|
|
*/
|
|
export function getPublicSwarmDomain(value: string | null | undefined): string | null {
|
|
if (!value) return null;
|
|
|
|
const normalized = normalizeNodeDomain(value);
|
|
if (!normalized) return null;
|
|
|
|
try {
|
|
const url = new URL(`https://${normalized}`);
|
|
if (url.username || url.password) return null;
|
|
|
|
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
const result = parse(hostname, {
|
|
allowPrivateDomains: false,
|
|
detectSpecialUse: true,
|
|
});
|
|
|
|
if (
|
|
!result.domain ||
|
|
result.isIcann !== true ||
|
|
result.isIp ||
|
|
result.isSpecialUse
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return url.port ? `${hostname}:${url.port}` : hostname;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function isPublicSwarmDomain(value: string | null | undefined): boolean {
|
|
return getPublicSwarmDomain(value) !== null;
|
|
}
|
|
|
|
/**
|
|
* Resolve retired bootstrap hostnames to the node identity they represent.
|
|
* This keeps upgraded nodes working even before their persisted seed rows are
|
|
* rewritten by the accompanying data migration.
|
|
*/
|
|
export function getCanonicalSwarmSeedDomain(value: string | null | undefined): string | null {
|
|
const domain = getPublicSwarmDomain(value);
|
|
if (!domain) return null;
|
|
|
|
return LEGACY_SWARM_SEED_DOMAINS[domain] ?? domain;
|
|
}
|