Files
MyIP/api/ipapi-is.js
T
jason5ng32andClaude Opus 4.7 d51d575efd refactor(backend): unify upstream HTTP via fetchUpstream; kill https.get
All 16 handlers previously mixed two HTTP client styles:

- 7 used Node's built-in https.get()/http.get() with callback events
  (data/end/error) and 20+ lines of JSON accumulation
- 4 used modern fetch() but without any timeout — upstream hangs would
  pin the Express connection indefinitely

Every upstream request now goes through common/fetch-upstream.js, which
wraps fetch() with an 8s AbortController timeout. Handlers shrink from
~20 lines of request boilerplate to a 5-line try/catch:

    try {
        const apiRes = await fetchUpstream(url);
        const json = await apiRes.json();
        res.json(modifyJson(json));
    } catch (e) {
        res.status(500).json({ error: e.message });
    }

Converted handlers:
- ipinfo-io / ipapi-com / ipapi-is / ip2location-io / ip-sb / mac-checker
  (dropped `import { get } from 'https'/'http'`, rewrote the request)
- ipcheck-ing / invisibility-test / update-user-achievement / get-user-info
  (already on fetch, swapped to fetchUpstream to gain the timeout)
- cf-radar (inline fetchFromCloudflare helper swapped to fetchUpstream)

Special case: google-map.js streams a binary JPEG to the client rather
than parsing JSON. Uses Readable.fromWeb(apiRes.body).pipe(res) to
bridge the fetch ReadableStream to the Express response.

Net -150 lines of handler code; upstream hangs now cap at 8s instead
of 30-120s; consistent error handling across all handlers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:56:13 +08:00

39 lines
1.3 KiB
JavaScript

import { fetchUpstream } from '../common/fetch-upstream.js';
export default async (req, res) => {
// IP presence + validity guaranteed by requireValidIP middleware.
const ipAddress = req.query.ip;
const keys = (process.env.IPAPIIS_API_KEY).split(',');
const key = keys[Math.floor(Math.random() * keys.length)];
const url = `https://api.ipapi.is?q=${ipAddress}&key=${key}`;
try {
const apiRes = await fetchUpstream(url);
const json = await apiRes.json();
res.json(modifyJsonForIPAPI(json));
} catch (e) {
res.status(500).json({ error: e.message });
}
};
function modifyJsonForIPAPI(json) {
let asn = json.asn || {};
const { ip, location, is_datacenter, is_proxy, is_vpn, is_tor } = json;
return {
ip: ip,
city: location.city || 'N/A',
region: location.state || 'N/A',
country: location.country_code || 'N/A',
country_name: location.country || 'N/A',
country_code: location.country_code || 'N/A',
latitude: location.latitude || 'N/A',
longitude: location.longitude || 'N/A',
asn: asn.asn === undefined ? 'N/A' : 'AS' + asn.asn,
org: asn.org || 'N/A',
isHosting: is_datacenter || false,
isProxy: is_proxy || is_vpn || is_tor || false
};
}