/**
* app.js — Single-file leak checker webtool (Node built-in only)
* - Serves a simple HTML page at /
* - API: GET /api/hibp/breaches?email=you@example.com
*
* Deploy on Render:
* - Build Command: (leave empty) or "echo no build"
* - Start Command: node app.js
* - Env var: HIBP_API_KEY =
*
* Notes:
* - Uses HIBP official API (requires key)
* - Includes a button that opens the Odido checker page (no scraping)
*/
const http = require("http");
const { URL } = require("url");
const PORT = process.env.PORT || 3000;
function isLikelyEmail(email) {
return typeof email === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
}
function sendJson(res, status, obj) {
const body = JSON.stringify(obj);
res.writeHead(status, {
"content-type": "application/json; charset=utf-8",
"cache-control": "no-store",
});
res.end(body);
}
function sendHtml(res, status, html) {
res.writeHead(status, {
"content-type": "text/html; charset=utf-8",
"cache-control": "no-store",
});
res.end(html);
}
const HTML = `
Odido / Datalek Check Tool
`;
async function handleHibp(req, res, urlObj) {
const email = (urlObj.searchParams.get("email") || "").trim();
if (!isLikelyEmail(email)) {
return sendJson(res, 400, { ok: false, error: "Voer een geldig e-mailadres in." });
}
const apiKey = process.env.HIBP_API_KEY;
if (!apiKey) {
return sendJson(res, 500, {
ok: false,
error: "Server mist HIBP_API_KEY. Zet deze als environment variable op Render."
});
}
const hibpUrl =
"https://haveibeenpwned.com/api/v3/breachedaccount/" +
encodeURIComponent(email) +
"?truncateResponse=true";
// Node 18+ has global fetch
const hibpResp = await fetch(hibpUrl, {
headers: {
"hibp-api-key": apiKey,
"user-agent": "odido-checker-tool/1.0 (render-deploy)",
"accept": "application/json",
},
});
if (hibpResp.status === 404) {
return sendJson(res, 200, { ok: true, breached: false, breaches: [] });
}
if (hibpResp.status === 429) {
const retryAfter = hibpResp.headers.get("retry-after");
return sendJson(res, 429, {
ok: false,
error: "Rate limit geraakt bij HIBP. Probeer later opnieuw.",
retryAfterSeconds: retryAfter ? Number(retryAfter) : null,
});
}
if (!hibpResp.ok) {
const text = await hibpResp.text().catch(() => "");
return sendJson(res, hibpResp.status, {
ok: false,
error: "HIBP API error",
status: hibpResp.status,
details: text.slice(0, 500),
});
}
const breaches = await hibpResp.json();
return sendJson(res, 200, { ok: true, breached: true, breaches });
}
const server = http.createServer(async (req, res) => {
try {
const urlObj = new URL(req.url, `http://${req.headers.host || "localhost"}`);
const path = urlObj.pathname;
if (req.method === "GET" && path === "/") {
return sendHtml(res, 200, HTML);
}
if (req.method === "GET" && path === "/api/hibp/breaches") {
return await handleHibp(req, res, urlObj);
}
// health check
if (req.method === "GET" && path === "/healthz") {
return sendJson(res, 200, { ok: true });
}
return sendJson(res, 404, { ok: false, error: "Not found" });
} catch (err) {
return sendJson(res, 500, { ok: false, error: "Server error", details: String(err) });
}
});
server.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
Check: (mogelijk) gelekte gegevens
Deze tool checkt je e-mail op bekende datalekken via Have I Been Pwned (officiële API). Voor de Odido-specifieke check opent hij de publieke Odido-checker pagina (geen scraping).
1) HIBP e-mail check
Let op: “niet gevonden” betekent alleen “niet gevonden in bekende/verwerkte lekken”. Zet 2FA aan en gebruik unieke wachtwoorden.