1import * as _ from 'lodash-es' 13let relayProbeCache = {ip: '', port: 0, result: false, ts: 0, warned: false} 14const tcpProbeRelay = async (host: string, port: number, timeoutMs = 2000): Promise<boolean> => { 15 const now = Date.now() 16 if (relayProbeCache.ip === host && relayProbeCache.port === port && now - relayProbeCache.ts < 30_000) { 17 return relayProbeCache.result 20 relayProbeCache = {ip: host, port, result, ts: now, warned: false} 30 needsKubePortFwd?: boolean 32 openPortNoA?: import('../sharedF/portInOpenAAI.mts').OpenPortNoA 35export const getClusterCfg = ({cluster_name}: {cluster_name: string}) => { 39 const cfg = nodePorts[cluster_name] 42 const {nodeIpsExtHost, lanHostIpsH, tailscaleIp, tsRelayIp, openPortNoA, ...nodePortH} = cfg 45 return {nodeIpsExtHost, lanHostIpsH, tailscaleIp, tsRelayIp, openPortNoA, nodePortH} 48export const getSvcInfo = async (svcName: string) => { 51 const svcInfos: Record<string, SvcDef> = {} 53 for (const cluster_name of Object.keys(nodePorts)) { 54 const {nodeIpsExtHost, lanHostIpsH, tailscaleIp, tsRelayIp, openPortNoA, nodePortH} = getClusterCfg({cluster_name}) 56 for (const [_svcName, svcDef] of Object.entries(nodePortH)) { 57 if (_svcName != svcName) continue 59 const isLocalDev = isDevPc || isOcWeb 60 let resolvedIps = [nodeIpsExtHost] 61 if (isLocalDev && lanHostIpsH?.clusterVip) { 62 const onLan = await isOnSameLan({cluster_name, nodeIpsExtHost, clusterVip: lanHostIpsH.clusterVip}) 64 resolvedIps = [lanHostIpsH.clusterVip] 67 if (!isLocalDev && tailscaleIp) { 68 resolvedIps = [tailscaleIp] 71 let newDef: SvcDef = { 72 cluster_name, ips: resolvedIps, openPortNoA 75 if (_.isInteger(svcDef)) newDef.nodePort = svcDef as number 76 else newDef = {...newDef, ...svcDef as object} 78 // Tailscale relay pod: dev PC on tailnet connects directly (bypass port-forward) 79 // Port stays as ptNodePortNo — the relay pod listens on each DB's nodeport. 80 // TCP probe prevents 75s hang when tailscale daemon is broken (CGNAT IP on iface but tunnel down) 81 const relayEligible = isLocalDev && !!tsRelayIp && !!newDef.isCnPg && isOnTailnet() 82 if (relayEligible && await tcpProbeRelay(tsRelayIp as string, newDef.ptNodePortNo!)) { 83 newDef.ips = [tsRelayIp as string] 84 newDef.needsKubePortFwd = false 85 } else if (relayEligible && !relayProbeCache.warned) { 86 betLog({tsRelay: 'unreachable — start tailscale: brew services restart tailscale', ip: tsRelayIp}) 87 relayProbeCache.warned = true 91 const localPort = portForwardMgr.getEndpointPort(_svcName) 93 newDef.nodePort = localPort 94 newDef.ips = ['127.0.0.1'] 98 const existingDef = svcInfos[_svcName] 99 throwIf(() => existingDef, {existingDef, newDef, _svcName}) 101 svcInfos[_svcName] = newDef 105 return svcInfos[svcName] 108export const getReqSvcInfo = async (svcName: string) => { 109 const svcInfo = await getSvcInfo(svcName) 111 throPtErr(`please define "export const ${svcName} = anOpenPortNo" within nodeports/[cluster_name].mjs`)