| 251 | } |
| 252 | |
| 253 | async function getIPInfo(ip: string): Promise<IPInfo> { |
| 254 | if (!ip || ip === '127.0.0.1' || ip === '::1') { |
| 255 | return { isp: null, asn: null, organization: null }; |
| 256 | } |
| 257 | |
| 258 | try { |
| 259 | const controller = new AbortController(); |
| 260 | const timeout = setTimeout(() => controller.abort(), 3000); |
| 261 | |
| 262 | const response = await fetch( |
| 263 | `https://ip-api.com/json/${ip}?fields=isp,as,org,query,status`, |
| 264 | { signal: controller.signal }, |
| 265 | ); |
| 266 | |
| 267 | clearTimeout(timeout); |
| 268 | |
| 269 | if (!response.ok) { |
| 270 | return { isp: null, asn: null, organization: null }; |
| 271 | } |
| 272 | |
| 273 | const data = (await response.json()) as { |
| 274 | status?: string; |
| 275 | isp?: string; |
| 276 | as?: string; |
| 277 | org?: string; |
| 278 | }; |
| 279 | |
| 280 | if (data.status === 'fail') { |
| 281 | return { isp: null, asn: null, organization: null }; |
| 282 | } |
| 283 | |
| 284 | return { |
| 285 | isp: data.isp || null, |
| 286 | asn: data.as ? `AS${data.as.split(' ')[0]}` : null, |
| 287 | organization: data.org || null, |
| 288 | }; |
| 289 | } catch { |
| 290 | return { isp: null, asn: null, organization: null }; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | async function measureConnectionTime( |
| 295 | hostname: string, |