Add mDNS republish logic with exponential backoff

This commit is contained in:
Chris Wanstrath 2026-04-13 16:55:07 -07:00
parent c66a40df96
commit 2937fb2372

View File

@ -4,7 +4,12 @@ import { LOCAL_HOST } from '%config'
import { networkInterfaces } from 'os' import { networkInterfaces } from 'os'
import { hostLog } from './tui' import { hostLog } from './tui'
const MAX_REPUBLISH_DELAY = 30_000
const REPUBLISH_BASE_DELAY = 1_000
const _killed = new Set<string>()
const _publishers = new Map<string, Subprocess>() const _publishers = new Map<string, Subprocess>()
const _republishAttempts = new Map<string, number>()
const isEnabled = process.env.NODE_ENV === 'production' && process.platform === 'linux' const isEnabled = process.env.NODE_ENV === 'production' && process.platform === 'linux'
@ -51,10 +56,12 @@ export function publishApp(name: string) {
}) })
_publishers.set(name, proc) _publishers.set(name, proc)
_republishAttempts.delete(name)
hostLog(`mDNS: published ${host} -> ${ip}`) hostLog(`mDNS: published ${host} -> ${ip}`)
proc.exited.then(() => { proc.exited.then(() => {
_publishers.delete(name) _publishers.delete(name)
if (!_killed.delete(name)) republish(name)
}) })
} catch { } catch {
hostLog(`mDNS: failed to publish ${host}`) hostLog(`mDNS: failed to publish ${host}`)
@ -64,9 +71,11 @@ export function publishApp(name: string) {
export function unpublishApp(name: string) { export function unpublishApp(name: string) {
if (!isEnabled) return if (!isEnabled) return
_republishAttempts.delete(name)
const proc = _publishers.get(name) const proc = _publishers.get(name)
if (!proc) return if (!proc) return
_killed.add(name)
proc.kill() proc.kill()
_publishers.delete(name) _publishers.delete(name)
hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`) hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`)
@ -75,9 +84,20 @@ export function unpublishApp(name: string) {
export function unpublishAll() { export function unpublishAll() {
if (!isEnabled) return if (!isEnabled) return
_republishAttempts.clear()
for (const [name, proc] of _publishers) { for (const [name, proc] of _publishers) {
_killed.add(name)
proc.kill() proc.kill()
hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`) hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`)
} }
_publishers.clear() _publishers.clear()
} }
function republish(name: string) {
const attempts = _republishAttempts.get(name) ?? 0
const delay = Math.min(REPUBLISH_BASE_DELAY * 2 ** attempts, MAX_REPUBLISH_DELAY)
_republishAttempts.set(name, attempts + 1)
hostLog(`mDNS: ${toSubdomain(name)}.${LOCAL_HOST} exited unexpectedly, retrying in ${delay}ms`)
setTimeout(() => publishApp(name), delay)
}