Files
MyIP/api/dns-leak-test.js
T
jason5ng32andClaude Opus 5.5 c29e48687b Refactor(layout): split backend-only code out of common/, group frontend utils
common/ had become the backend's library: 30 of its 41 modules were
imported by nothing in the frontend, so the name no longer said what
belonged there. It now holds only what the frontend or the build imports
too; backend-only code moved to server/, flat except for datasets/ (the
dataset engine and its readers). The *-db/ dataset directories stay in
common/: self-hosting docs and Docker volumes mount those paths.

frontend/utils/ keeps its app-wide modules at the root and gains ip/
(IP / ASN shaping and validation), report/ (the report pipeline) and
features/ (the pure layer of a single feature). Paths only — no logic
changes. The Dockerfile copies server/, and the AGENTS.md files say where
a new module goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:10:02 +08:00

56 lines
2.1 KiB
JavaScript

// Enhanced DNS leak detection — thin proxy to the main IPCheck.ing API.
//
// - validates the 32-hex token locally (fail fast before any network hop),
// - forwards request headers (notably Authorization: Bearer <Firebase ID>),
// - attaches the apikey query param and forwards ?lang untouched,
// - passes the upstream status + JSON back to the caller verbatim so the
// frontend can surface "Sign in required" / "Invalid token" etc.
//
// GET /api/dnsleaktest/session/:token
import { fetchUpstream } from '../common/fetch-with-timeout.js';
import logger from '../server/logger.js';
const TOKEN_RE = /^[0-9a-f]{32}$/;
export async function getSessionResult(req, res) {
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method Not Allowed' });
}
const token = req.params?.token;
if (!token || !TOKEN_RE.test(token)) {
return res.status(400).json({ error: 'Invalid token' });
}
const apiKey = process.env.IPCHECKING_API_KEY;
const apiEndpoint = process.env.IPCHECKING_API_ENDPOINT;
if (!apiKey || !apiEndpoint) {
return res.status(500).json({ error: 'API key is missing' });
}
const url = new URL(`${apiEndpoint}/dnsleaktest/session/${token}`);
url.searchParams.set('apikey', apiKey);
// The upstream resolves any tag onto the languages it has, so the caller's
// value rides along untouched; omitting it lets the upstream default apply.
if (typeof req.query.lang === 'string' && req.query.lang) {
url.searchParams.set('lang', req.query.lang);
}
try {
const apiResponse = await fetchUpstream(url, {
headers: { ...req.headers },
});
// Parse as JSON if we can, otherwise just keep an empty object so the
// client still gets a proper status code.
const data = await apiResponse.json().catch(() => ({}));
res.set('Cache-Control', 'no-store');
res.status(apiResponse.status).json(data);
} catch (error) {
logger.error({ err: error }, 'dnsleaktest upstream fetch failed');
res.status(502).json({ error: 'Upstream fetch failed', detail: error.message });
}
}