better error in case geo-db download fails

Massimo Melina committed Sep 29, 2025 at 12:08 UTC eebc0708eb3db25de34c7b9e4bb8a142608ec00a
2 files changed +20 -15
src/geo.ts
+18 -13
@@ -41,20 +41,25 @@ async function checkFiles() {
41 const LOCAL_FILE = 'geo_ip.bin'
42 const TEMP = LOCAL_FILE + '.downloading'
43 const { mtime=0 } = await stat(LOCAL_FILE).catch(() => ({ mtime: 0 }))
44 + const name = 'geo-ip db'
45 const now = Date.now()
45 - if (+mtime < now - 31 * DAY) { // month-old or non-existing
46 - console.log('downloading geo-ip db')
47 - await unzip(await httpStream(URL), path =>
48 - path.toUpperCase().endsWith(ZIP_FILE) && TEMP)
49 - if (await stat(TEMP))
50 - if (isOpen())
51 - ip2location.close()
52 - await unlink(LOCAL_FILE).catch(() => {})
53 - await rename(TEMP, LOCAL_FILE)
54 - ip2country.cache.clear?.()
55 - console.log('download geo-ip db completed')
56 - }
46 + if (+mtime < now - 31 * DAY) // month-old or non-existing
47 + try {
48 + const req = await httpStream(URL)
49 + console.log(`downloading ${name}`)
50 + await unzip(req, path => path.toUpperCase().endsWith(ZIP_FILE) && TEMP)
51 + await stat(TEMP) // check existence
52 + if (isOpen())
53 + ip2location.close()
54 + await unlink(LOCAL_FILE).catch(() => {})
55 + await rename(TEMP, LOCAL_FILE)
56 + ip2country.cache.clear?.()
57 + console.log(`${name} download completed`)
58 + }
59 + catch (e: any) {
60 + console.error(`Failed to download ${name}${mtime ? ", falling back on old data" : ''}:`, e?.message || String(e))
61 + }
62 else if (isOpen()) return
58 - console.debug('loading geo-ip db')
63 + console.debug(`loading ${name}`)
64 ip2location.open(LOCAL_FILE) // using openAsync causes a DEP0137 error within 10 seconds
65 }
src/util-http.ts
+2 -2
@@ -32,7 +32,7 @@ export interface XRequestOptions extends https.RequestOptions {
32 }
33
34 export declare namespace httpStream { let defaultProxy: string | undefined }
35 -export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThrow, ...options }: XRequestOptions ={}) {
35 +export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThrow=true, ...options }: XRequestOptions ={}) {
36 const controller = new AbortController()
37 options.signal ??= controller.signal
38 return Object.assign(new Promise<IncomingMessage>(async (resolve, reject) => {
@@ -78,7 +78,7 @@ export function httpStream(url: string, { body, proxy, jar, noRedirect, httpThro
78 if (v) jar[k] = v
79 else delete jar[k]
80 }
81 - if (!res.statusCode || (httpThrow ?? true) && res.statusCode >= 400)
81 + if (!res.statusCode || httpThrow && res.statusCode >= 400)
82 return reject(new Error(String(res.statusCode), { cause: res }))
83 let r = res.headers.location
84 if (r && !noRedirect) {