winupdate-neo/lib/geodata.ts
2026-06-09 18:43:54 +08:00

75 lines
1.8 KiB
TypeScript

import IP2Region from 'ip2region'
import { existsSync } from 'fs'
import { join } from 'path'
import { isPrivateIp, normalizeIpCandidate } from './clientIp'
interface IpGeodata {
country: string
province: string
city: string
isp: string
}
const ipLocationLabelCache = new Map<string, string>()
let ip2Region: IP2Region | null | undefined
export function getIpGeodataLabel(ip?: string | null) {
const normalizedIp = normalizeIpCandidate(ip)
if (!normalizedIp) {
return null
}
const cachedLabel = ipLocationLabelCache.get(normalizedIp)
if (cachedLabel) {
return cachedLabel
}
const label = resolveIpGeodataLabel(normalizedIp)
ipLocationLabelCache.set(normalizedIp, label)
return label
}
function resolveIpGeodataLabel(ip: string) {
if (isPrivateIp(ip)) {
return '内网地址'
}
try {
const geodata = getIp2Region()?.search(ip) as IpGeodata | null
const parts = uniqueNonEmptyValues([
geodata?.country,
geodata?.province,
geodata?.city,
geodata?.isp
])
return parts.length > 0 ? parts.join(' ') : '未知归属地'
} catch {
return '未知归属地'
}
}
function getIp2Region() {
if (ip2Region !== undefined) {
return ip2Region
}
const dataDir = join(process.cwd(), 'node_modules', 'ip2region', 'data')
const ipv4db = join(dataDir, 'ip2region.db')
const ipv6db = join(dataDir, 'ipv6wry.db')
if (!existsSync(ipv4db) || !existsSync(ipv6db)) {
console.warn(`[geodata] ip2region database files not found in ${dataDir}`)
ip2Region = null
return ip2Region
}
ip2Region = new IP2Region({ ipv4db, ipv6db })
return ip2Region
}
function uniqueNonEmptyValues(values: Array<string | undefined>) {
return Array.from(new Set(values
.map(value => value?.trim())
.filter((value): value is string => !!value && value !== '0')))
}