// Dankclaw homepage status checker. // Loads services.json, renders cards, probes each service URL for up/down. (function () { "use strict"; function probe(url, timeoutMs) { // Same-origin fetch only; cross-origin will throw -> counted as checking // via proxy. For LAN hostnames Caddy proxies everything, so URLs here are // Caddy hostnames and fetch works same-origin. var controller = new AbortController(); var timer = setTimeout(function () { controller.abort(); }, timeoutMs); return fetch(url, { signal: controller.signal, cache: "no-store" }) .then(function (r) { return r.ok; }) .catch(function () { return false; }) .finally(function () { clearTimeout(timer); }); } function card(svc) { var el = document.createElement("article"); el.className = "card"; var top = document.createElement("div"); top.className = "top"; var badge = document.createElement("span"); badge.className = "badge checking"; badge.title = "checking"; var h2 = document.createElement("h2"); h2.textContent = svc.name; top.appendChild(badge); top.appendChild(h2); var desc = document.createElement("p"); desc.textContent = svc.description; var links = document.createElement("div"); links.className = "links"; var aOpen = document.createElement("a"); aOpen.href = svc.url; aOpen.target = "_blank"; aOpen.rel = "noopener"; aOpen.textContent = "Open"; var aDocs = document.createElement("a"); aDocs.href = svc.docs; aDocs.textContent = "Docs"; links.appendChild(aOpen); links.appendChild(aDocs); el.appendChild(top); el.appendChild(desc); el.appendChild(links); return { el: el, badge: badge }; } fetch("services.json") .then(function (r) { return r.json(); }) .then(function (data) { var grid = document.getElementById("services"); data.services.forEach(function (svc) { var c = card(svc); grid.appendChild(c.el); // For the homepage itself, mark up immediately. if (svc.url === "/") { c.badge.className = "badge up"; c.badge.title = "up"; return; } probe(svc.url, 6000).then(function (ok) { c.badge.className = "badge " + (ok ? "up" : "down"); c.badge.title = ok ? "up" : "down"; }); }); }) .catch(function (err) { var grid = document.getElementById("services"); var p = document.createElement("p"); p.textContent = "Failed to load services.json: " + err.message; grid.appendChild(p); }); })();