// Danger zone -- destructive operations. // Currently: world wipe. Always type-to-confirm to prevent accidental clicks. "use strict"; export function setupDanger() { const btn = document.getElementById("wipeBtn"); const cb = document.getElementById("wipeBackup"); const msg = document.getElementById("wipeMsg"); const seedDisplay = document.getElementById("wipeCurrentSeed"); const customInput = document.getElementById("wipeCustomSeed"); if (!btn) return; // Enable the custom-seed text field only when its radio is selected. document.querySelectorAll('input[name="wipeSeedMode"]').forEach(radio => { radio.addEventListener("change", () => { const mode = document.querySelector('input[name="wipeSeedMode"]:checked')?.value; customInput.disabled = (mode !== "custom"); if (mode === "custom") customInput.focus(); }); }); // Fetch current seed each time the wipe modal becomes visible. Watching // the wipe section's ancestor modal works without coupling to the modal // module's open/close API. const refreshSeed = async () => { seedDisplay.textContent = "loading..."; try { const res = await fetch("/api/world/seed"); const body = await res.json(); seedDisplay.textContent = body.seed ? body.seed : "(unknown -- server stopped or seed not set)"; } catch (e) { seedDisplay.textContent = "(failed to read)"; } }; // Refresh on first load + whenever the modal becomes visible. Modal markup // uses a wrapping div with "[hidden]" attr, so we observe attribute changes. refreshSeed(); const modal = btn.closest(".modal"); if (modal) { new MutationObserver(muts => { for (const m of muts) { if (m.attributeName === "hidden" && !modal.hasAttribute("hidden")) { refreshSeed(); } } }).observe(modal, { attributes: true }); } btn.addEventListener("click", async () => { msg.className = "acct-msg"; msg.textContent = ""; const mode = document.querySelector('input[name="wipeSeedMode"]:checked')?.value || "random"; const customSeed = (customInput.value || "").trim(); if (mode === "custom" && !customSeed) { msg.textContent = "Custom seed selected but the field is empty."; return; } // Build a confirmation prompt that reflects the chosen seed strategy // so the user sees exactly what's about to happen. let seedNote = ""; if (mode === "keep") seedNote = `Same seed (${seedDisplay.textContent}) will be reused.\n`; else if (mode === "custom") seedNote = `Seed will be set to: ${customSeed}\n`; else seedNote = "A new random seed will be generated.\n"; const typed = prompt( "Type WIPE (uppercase, exactly) to confirm world wipe.\n" + "Server will stop, world will be replaced, server will restart.\n\n" + seedNote ); if (typed !== "WIPE") { if (typed != null) msg.textContent = "Confirmation didn't match -- nothing wiped."; return; } btn.disabled = true; msg.textContent = "Wiping..."; try { const res = await fetch("/api/world/wipe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ confirm: typed, backup: cb.checked, seedMode: mode, customSeed: mode === "custom" ? customSeed : null, }), }); const body = await res.json().catch(() => ({})); if (!res.ok || body.ok === false) { msg.textContent = body.error || `Error ${res.status}`; return; } msg.className = "acct-msg ok"; const parts = ["World wiped."]; if (body.seedUsed) parts.push(`Seed: ${body.seedUsed}.`); if (body.backupName) parts.push(`Backup: ${body.backupName}.`); parts.push("Server restarting..."); msg.textContent = parts.join(" "); // Refresh the seed display so user sees the new value once MC is back. setTimeout(refreshSeed, 5000); } catch (e) { msg.textContent = e.message; } finally { btn.disabled = false; } }); }