fix: admin/internet: changing dynamic-dns url was not issuing a new request

Massimo Melina committed Feb 8, 2025 at 13:29 UTC 9a6f41d1a6b13f6de36d08e3e95dd1334218960a
2 files changed +14 -8
src/ddns.ts
+10 -7
@@ -15,6 +15,7 @@ const dynamicDnsUrl = defineConfig(CFG.dynamic_dns_url, '')
15 const EVENT = 'publicIpsChanged'
16 let stopFetching: any
17 let lastIPs: any
18 +let lastMap: any
19 events.onListeners(EVENT, cbs => {
20 stopFetching?.()
21 if (!cbs?.size) return
@@ -22,7 +23,7 @@ events.onListeners(EVENT, cbs => {
23 const IPs = await getPublicIps()
24 if (_.isEqual(lastIPs, IPs)) return
25 lastIPs = IPs
25 - events.emit(EVENT, {
26 + events.emit(EVENT, lastMap = {
27 IPs,
28 IPX: IPs[0] || '',
29 IP4: _.find(IPs, isIPv4) || '',
@@ -32,15 +33,17 @@ events.onListeners(EVENT, cbs => {
33 })
34
35 export interface DynamicDnsResult { ts: string, error: string, url: string }
35 -let stopEvent: any
36 +let stopListening: any
37 let last: DynamicDnsResult | undefined
38 dynamicDnsUrl.sub(v => {
38 - stopEvent?.()
39 + stopListening?.()
40 if (!v) return
40 - stopEvent = events.on(EVENT, async map => {
41 - const all: DynamicDnsResult[] = await Promise.all(v.split('\n').map(async line => {
41 + stopListening = events.on(EVENT, async () => {
42 + if (!lastMap) return // called at start once, before first getPublicIps. Just skip it
43 + const lines = dynamicDnsUrl.get()
44 + const all: DynamicDnsResult[] = await Promise.all(lines.split('\n').map(async line => {
45 const [templateUrl, re] = splitAt('>', line)
43 - const url = replace(templateUrl, map, '$')
46 + const url = replace(templateUrl, lastMap, '$')
47 const error = await httpWithBody(url, { httpThrow: false, headers: { 'User-Agent': "HFS/" + VERSION } }) // UA specified as requested by no-ip guidelines
48 .then(async res => {
49 const str = String(res.body).trim()
@@ -51,7 +54,7 @@ dynamicDnsUrl.sub(v => {
54 last = _.find(all, 'error') || all[0] // the system is designed for just one result, and we give precedence to errors
55 events.emit('dynamicDnsError', last)
56 console.log('dynamic dns update', last?.error || 'ok')
54 - })
57 + }, { callNow: true })
58 })
59
60 export async function* get_dynamic_dns_error() {
src/events.ts
+4 -1
@@ -8,7 +8,7 @@ export class BetterEventEmitter {
8 protected listeners = new Map<string, Listeners>()
9 preventDefault = Symbol()
10 stop = this.preventDefault // legacy pre-0.54 (introduced in 0.53)
11 - on(event: string | string[], listener: Listener, { warnAfter=10 }={}) {
11 + on(event: string | string[], listener: Listener, { warnAfter=10, callNow=false }={}) {
12 if (typeof event === 'string')
13 event = [event]
14 for (const e of event) {
@@ -20,6 +20,9 @@ export class BetterEventEmitter {
20 console.warn("Warning: many event listeners for ", e)
21 this.emit(e + LISTENERS_SUFFIX, cbs)
22 }
23 + if (callNow)
24 + try { listener() }
25 + catch {}
26 return () => {
27 for (const e of event) {
28 const cbs = this.listeners.get(e)