function getCookie(name) { const match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)")); return match ? decodeURIComponent(match[1]) : ""; } async function apiFetch(url, options) { options = options || {}; options.headers = options.headers || {}; if (options.method && options.method !== "GET") { options.headers["X-CSRF-Token"] = getCookie("wgm_csrf"); } const res = await fetch(url, options); if (res.status === 401) { window.location.href = "/login"; throw new Error("unauthenticated"); } return res; } function serverIDFromPath() { const parts = window.location.pathname.split("/").filter(Boolean); return parts[1]; } const serverID = serverIDFromPath(); const errEl = document.getElementById("server-error"); async function loadServer() { errEl.textContent = ""; const res = await apiFetch("/api/servers/" + serverID); if (!res.ok) { const data = await res.json().catch(() => ({})); errEl.textContent = data.error || "Server konnte nicht geladen werden."; return; } const s = await res.json(); document.getElementById("server-name").textContent = s.Name; document.getElementById("d-interface").textContent = s.InterfaceName; document.getElementById("d-port").textContent = s.ListenPort; document.getElementById("d-address").textContent = s.AddressRange; document.getElementById("d-dns").textContent = s.DNS || "-"; document.getElementById("d-mtu").textContent = s.MTU; const statusRes = await apiFetch("/api/servers"); if (statusRes.ok) { const servers = await statusRes.json(); const match = servers.find((x) => String(x.ID) === String(serverID)); const statusTd = document.getElementById("d-status"); statusTd.innerHTML = ""; const badge = document.createElement("span"); const status = match ? match.status : "DOWN"; badge.className = "badge " + (status === "UP" ? "up" : "down"); badge.textContent = status; statusTd.appendChild(badge); } } async function loadPeers() { const tbody = document.querySelector("#peers tbody"); tbody.innerHTML = ""; const res = await apiFetch("/api/server/" + serverID + "/peers"); if (!res.ok) return; const peers = await res.json(); for (const p of peers) { const tr = document.createElement("tr"); const nameTd = document.createElement("td"); nameTd.textContent = p.name; const emailTd = document.createElement("td"); emailTd.textContent = p.email || "-"; const allowedTd = document.createElement("td"); allowedTd.textContent = p.allowed_ips; const enabledTd = document.createElement("td"); enabledTd.textContent = p.enabled ? "Ja" : "Nein"; const actionsTd = document.createElement("td"); const qrBtn = document.createElement("button"); qrBtn.textContent = "QR-Code"; qrBtn.className = "secondary"; qrBtn.addEventListener("click", () => showQRCode(p.id)); actionsTd.appendChild(qrBtn); const dlLink = document.createElement("a"); dlLink.href = "/api/server/" + serverID + "/peer/" + p.id + "/config?host=" + encodeURIComponent(window.location.hostname); dlLink.textContent = "Config"; dlLink.style.marginLeft = "0.5rem"; actionsTd.appendChild(dlLink); const delBtn = document.createElement("button"); delBtn.textContent = "Löschen"; delBtn.className = "danger"; delBtn.addEventListener("click", () => deletePeer(p.id)); actionsTd.appendChild(delBtn); tr.appendChild(nameTd); tr.appendChild(emailTd); tr.appendChild(allowedTd); tr.appendChild(enabledTd); tr.appendChild(actionsTd); tbody.appendChild(tr); } } function showQRCode(peerID) { const modal = document.getElementById("qrcode-modal"); const img = document.getElementById("qrcode-img"); img.src = "/api/server/" + serverID + "/peer/" + peerID + "/qrcode?host=" + encodeURIComponent(window.location.hostname) + "&t=" + Date.now(); modal.classList.remove("hidden"); } document.getElementById("qrcode-close").addEventListener("click", () => { document.getElementById("qrcode-modal").classList.add("hidden"); }); async function deletePeer(peerID) { if (!confirm("Peer wirklich löschen?")) return; await apiFetch("/api/server/" + serverID + "/peer/" + peerID, { method: "DELETE" }); loadPeers(); } document.getElementById("btn-start").addEventListener("click", async () => { await apiFetch("/api/servers/" + serverID + "/start", { method: "POST" }); loadServer(); }); document.getElementById("btn-stop").addEventListener("click", async () => { await apiFetch("/api/servers/" + serverID + "/stop", { method: "POST" }); loadServer(); }); document.getElementById("btn-reload").addEventListener("click", async () => { await apiFetch("/api/servers/" + serverID + "/reload", { method: "POST" }); loadServer(); }); document.getElementById("btn-download").addEventListener("click", () => { window.location.href = "/api/servers/" + serverID + "/config"; }); document.getElementById("peer-form").addEventListener("submit", async (e) => { e.preventDefault(); const errP = document.getElementById("peer-error"); errP.textContent = ""; const form = e.target; const body = { name: form.name.value, email: form.email.value, allowed_ips: form.allowed_ips.value, persistent_keepalive: parseInt(form.persistent_keepalive.value, 10) || 25, use_preshared_key: form.use_preshared_key.checked, }; const res = await apiFetch("/api/server/" + serverID + "/peer", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const data = await res.json().catch(() => ({})); errP.textContent = data.error || "Peer konnte nicht erstellt werden."; return; } form.reset(); form.persistent_keepalive.value = 25; loadPeers(); }); loadServer(); loadPeers();