mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
The seven geo routes let a reserved address through to their upstream, which could only answer "no data" — a visitor querying their own LAN IP cost a real API call at every source. requireValidIP tightens into requirePublicIP and rejects that address before the handler runs, so geo-handler, maxmind and ipcheck-ing need no check of their own; the guard was already their sole validation layer. Malformed input and reserved space keep distinct messages — the two failures point at different fixes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.1 KiB
JavaScript
47 lines
2.1 KiB
JavaScript
// Factory for IP-geolocation source handlers.
|
|
//
|
|
// Every geo source (ipinfo.io, ip-api.com, ipapi.is, ip2location.io, ip.sb)
|
|
// shares an identical Express shell: read the (already-validated) ?ip,
|
|
// build a source-specific URL, fetch it through fetchUpstream, normalize the
|
|
// upstream JSON into the canonical response shape, and respond — with a
|
|
// uniform try/catch that logs the error and returns a 500.
|
|
//
|
|
// makeGeoHandler captures that shell. Each source supplies only:
|
|
// - name : short id used in the error log message
|
|
// - buildUrl : (req) => string URL OR { url, logContext } when the
|
|
// handler wants extra fields (e.g. lang) on the error log
|
|
// - normalize : (json) => canonical response object
|
|
//
|
|
// buildUrl runs before the try block so any env-key selection it performs
|
|
// keeps its current behavior (e.g. throwing on a missing key surfaces the
|
|
// same way the original inline handlers did).
|
|
|
|
import { fetchUpstream } from './fetch-with-timeout.js';
|
|
import logger from './logger.js';
|
|
|
|
export function makeGeoHandler({ name, buildUrl, normalize }) {
|
|
return async (req, res) => {
|
|
// Presence, validity and public routability guaranteed by the
|
|
// requirePublicIP middleware — a reserved address never reaches here.
|
|
const ipAddress = req.query.ip;
|
|
|
|
const built = buildUrl(req);
|
|
const url = typeof built === 'string' ? built : built.url;
|
|
const logContext = typeof built === 'string' ? {} : (built.logContext || {});
|
|
|
|
try {
|
|
const apiRes = await fetchUpstream(url);
|
|
// Outage / gateway pages come back as HTML — fail on status
|
|
// instead of letting JSON.parse throw on "<html>".
|
|
if (!apiRes.ok) {
|
|
throw new Error(`Upstream responded ${apiRes.status}`);
|
|
}
|
|
const json = await apiRes.json();
|
|
res.json(normalize(json));
|
|
} catch (e) {
|
|
logger.error({ err: e, ip: ipAddress, ...logContext }, `${name} handler failed`);
|
|
res.status(500).json({ error: e.message });
|
|
}
|
|
};
|
|
}
|