This repository has been archived on 2026-10-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
karldns.de/karldns/assets/visitor-ip.js
T

99 lines
3.4 KiB
JavaScript
Raw Normal View History

(() => {
const ipv4Placeholder = "203.0.113.10";
const ipv6Placeholder = "2001:db8::10";
const needsIpv4 = () => (
document.body.textContent.includes(ipv4Placeholder)
|| document.querySelector(`[data-copy*="${ipv4Placeholder}"]`)
);
const needsIpv6 = () => (
document.body.textContent.includes(ipv6Placeholder)
|| document.querySelector(`[data-copy*="${ipv6Placeholder}"]`)
);
const fetchIp = async (url) => {
try {
const response = await fetch(url, { cache: "no-store", mode: "cors" });
if (!response.ok) return "";
const payload = await response.json();
return typeof payload.ip === "string" ? payload.ip : "";
} catch (_) {
return "";
}
};
const replaceInTextNodes = (search, replacement) => {
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
const nodes = [];
while (walker.nextNode()) {
if (walker.currentNode.nodeValue.includes(search)) nodes.push(walker.currentNode);
}
nodes.forEach((node) => {
node.nodeValue = node.nodeValue.split(search).join(replacement);
});
};
const replaceInCopyValues = (search, replacement) => {
document.querySelectorAll("[data-copy]").forEach((element) => {
const value = element.dataset.copy || "";
if (value.includes(search)) element.dataset.copy = value.split(search).join(replacement);
});
};
const replaceEverywhere = (search, replacement) => {
if (!replacement || replacement === search) return;
replaceInTextNodes(search, replacement);
replaceInCopyValues(search, replacement);
};
const updateCopyCommand = (button, ipv6Command) => {
const value = button.dataset.copy || "";
if (!value || value.includes("?myipv6=")) return;
const lines = value.split("\n");
const output = [];
let inserted = false;
lines.forEach((line) => {
output.push(line);
if (!inserted && line.includes("/api/v1/router/update/") && line.includes("?myip=")) {
output.push(ipv6Command);
inserted = true;
}
});
if (inserted) button.dataset.copy = output.join("\n");
};
const insertIpv6Examples = (ip) => {
if (!ip) return;
document.querySelectorAll(".terminal-card.manual-code").forEach((card) => {
if (card.textContent.includes("?myipv6=")) return;
const lines = [...card.querySelectorAll("code")];
const ipv4Line = lines.find((line) => (
line.textContent.includes("/api/v1/router/update/")
&& line.textContent.includes("?myip=")
));
if (!ipv4Line) return;
const base = ipv4Line.textContent.split("?myip=", 1)[0].replace(/'$/, "");
const ipv6Command = `${base}?myipv6=${ip}'`;
const code = document.createElement("code");
code.textContent = ipv6Command;
ipv4Line.after(code);
const copyButton = card.querySelector("[data-copy]");
if (copyButton) updateCopyCommand(copyButton, ipv6Command);
});
};
document.addEventListener("DOMContentLoaded", async () => {
const tasks = [];
if (needsIpv4()) {
tasks.push(fetchIp("https://api4.ipify.org?format=json").then((ip) => {
replaceEverywhere(ipv4Placeholder, ip);
}));
}
if (needsIpv6()) {
tasks.push(fetchIp("https://api6.ipify.org?format=json").then((ip) => {
replaceEverywhere(ipv6Placeholder, ip);
insertIpv6Examples(ip);
}));
}
await Promise.allSettled(tasks);
});
})();