// Global variables let isConfigured = false; let currentIP = ""; let domainName = "mz.uy"; // Helper functions function showToast(message, type = "success") { const toastContainer = document.querySelector(".toast-container"); const toast = document.createElement("div"); toast.className = `toast align-items-center text-white bg-${type}`; toast.setAttribute("role", "alert"); toast.setAttribute("aria-live", "assertive"); toast.setAttribute("aria-atomic", "true"); toast.innerHTML = `
${message}
`; toastContainer.appendChild(toast); const bsToast = new bootstrap.Toast(toast); bsToast.show(); // Remove the toast after it's hidden toast.addEventListener("hidden.bs.toast", () => { toast.remove(); }); } // Initialize the application async function initApp() { // Show loading indicator document.getElementById("loading").style.display = "block"; document.getElementById("config-warning").style.display = "none"; document.getElementById("config-status").style.display = "none"; document.getElementById("dns-records-section").style.display = "none"; // Load configuration await loadConfig(); // Load current IP await loadCurrentIP(); // Load update frequencies for the dropdown await loadUpdateFrequencies(); // If configured, load DNS records if (isConfigured) { await loadDNSRecords(); } // Hide loading indicator document.getElementById("loading").style.display = "none"; } // Load configuration async function loadConfig() { try { const response = await fetch("/api/config"); const data = await response.json(); isConfigured = data.is_configured; domainName = data.domain; if (isConfigured) { document.getElementById("config-warning").style.display = "none"; document.getElementById("config-status").style.display = "block"; document.getElementById("dns-records-section").style.display = "block"; document.getElementById("zone-id").textContent = data.zone_id; document.getElementById("domain-name").textContent = data.domain; document.getElementById("domain-suffix").textContent = "." + data.domain; document.getElementById("domain-input").value = data.domain; document.getElementById("zone-id-input").value = data.zone_id; // Set the update schedule display let scheduleDisplay = "Manual updates only"; if (data.update_period) { switch (data.update_period) { case "*/5 * * * *": scheduleDisplay = "Every 5 minutes"; break; case "*/30 * * * *": scheduleDisplay = "Every 30 minutes"; break; case "0 * * * *": scheduleDisplay = "Hourly"; break; case "0 */6 * * *": scheduleDisplay = "Every 6 hours"; break; case "0 0 * * *": scheduleDisplay = "Daily"; break; default: scheduleDisplay = data.update_period; } } document.getElementById("update-schedule").textContent = scheduleDisplay; } else { document.getElementById("config-warning").style.display = "block"; document.getElementById("config-status").style.display = "none"; document.getElementById("dns-records-section").style.display = "none"; } } catch (error) { console.error("Failed to load configuration:", error); showToast("Failed to load configuration: " + error.message, "danger"); } } // Load current IP async function loadCurrentIP() { try { const response = await fetch("/api/current-ip"); const data = await response.json(); currentIP = data.ip; document.getElementById("current-ip").textContent = currentIP; } catch (error) { console.error("Failed to load current IP:", error); document.getElementById("current-ip").textContent = "Failed to load"; } } // Load update frequencies async function loadUpdateFrequencies() { try { const response = await fetch("/api/update-frequencies"); const frequencies = await response.json(); const select = document.getElementById("update-period"); select.innerHTML = ""; frequencies.forEach((freq) => { const option = document.createElement("option"); option.value = freq.value; option.textContent = freq.label; select.appendChild(option); }); } catch (error) { console.error("Failed to load update frequencies:", error); } } // Load DNS records async function loadDNSRecords() { try { const response = await fetch("/api/records"); if (!response.ok) { throw new Error("Failed to fetch DNS records"); } const records = await response.json(); const tbody = document.getElementById("dns-records"); tbody.innerHTML = ""; if (records.length === 0) { const tr = document.createElement("tr"); tr.innerHTML = 'No DNS records found'; tbody.appendChild(tr); return; } records.forEach((record) => { const tr = document.createElement("tr"); // Highlight records that match the current IP const isCurrentIP = record.type === "A" && record.content === currentIP; const ipBadge = isCurrentIP ? 'Current IP' : record.type === "A" ? 'Outdated IP' : ""; tr.innerHTML = ` ${record.type} ${record.name} ${record.content} ${ipBadge} ${record.ttl === 1 ? "Auto" : record.ttl + "s"} ${record.proxied ? '' : ""} `; tbody.appendChild(tr); }); // Add event listeners for edit and delete buttons document.querySelectorAll(".edit-record").forEach((button) => { button.addEventListener("click", () => editRecord(button.dataset.id)); }); document.querySelectorAll(".delete-record").forEach((button) => { button.addEventListener("click", () => deleteRecord(button.dataset.id, button.dataset.name), ); }); } catch (error) { console.error("Failed to load DNS records:", error); showToast("Failed to load DNS records: " + error.message, "danger"); } } // Edit a DNS record async function editRecord(id) { try { const response = await fetch("/api/records"); const records = await response.json(); const record = records.find((r) => r.id === id); if (!record) { showToast("Record not found", "danger"); return; } // Open the modal const modal = new bootstrap.Modal(document.getElementById("recordModal")); modal.show(); // Update modal title document.getElementById("recordModalLabel").textContent = "Edit DNS Record"; // Fill the form document.getElementById("record-id").value = record.id; // Set the subdomain name without the domain suffix let name = record.name; if (name === domainName) { name = "@"; } else if (name.endsWith("." + domainName)) { name = name.substring(0, name.length - domainName.length - 1); } document.getElementById("record-name").value = name; document.getElementById("record-type").value = record.type; document.getElementById("record-content").value = record.content; document.getElementById("record-ttl").value = record.ttl; document.getElementById("record-proxied").checked = record.proxied; document.getElementById("use-my-ip").checked = false; // Show/hide the "Use my current IP" option based on record type toggleMyIPOption(); } catch (error) { console.error("Failed to load record:", error); showToast("Failed to load record: " + error.message, "danger"); } } // Delete a DNS record async function deleteRecord(id, name) { if (!confirm(`Are you sure you want to delete the record for "${name}"?`)) { return; } try { const response = await fetch(`/api/records/${id}`, { method: "DELETE", }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to delete record"); } showToast(`Record for "${name}" deleted successfully`); await loadDNSRecords(); } catch (error) { console.error("Failed to delete record:", error); showToast("Failed to delete record: " + error.message, "danger"); } } // Toggle the "Use my current IP" option based on record type function toggleMyIPOption() { const recordType = document.getElementById("record-type").value; const useMyIPGroup = document.getElementById("use-my-ip-group"); const contentGroup = document.getElementById("content-group"); if (recordType === "A") { useMyIPGroup.style.display = "block"; // If the checkbox is checked, hide the content field const useMyIP = document.getElementById("use-my-ip").checked; contentGroup.style.display = useMyIP ? "none" : "block"; } else { useMyIPGroup.style.display = "none"; contentGroup.style.display = "block"; } } // Event listeners document.addEventListener("DOMContentLoaded", function () { // Initialize the application initApp(); // Refresh IP button document .getElementById("refresh-ip") .addEventListener("click", async function () { await loadCurrentIP(); showToast("Current IP refreshed"); }); // Save configuration button document .getElementById("save-config") .addEventListener("click", async function () { const apiToken = document.getElementById("api-token").value; const zoneId = document.getElementById("zone-id-input").value; const domain = document.getElementById("domain-input").value; const updatePeriod = document.getElementById("update-period").value; if (!apiToken || !zoneId || !domain) { showToast("Please fill all required fields", "danger"); return; } try { const response = await fetch("/api/config", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ api_token: apiToken, zone_id: zoneId, domain: domain, update_period: updatePeriod, }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to save configuration"); } // Hide the modal const modal = bootstrap.Modal.getInstance( document.getElementById("configModal"), ); modal.hide(); showToast("Configuration saved successfully"); // Reload the app initApp(); } catch (error) { console.error("Failed to save configuration:", error); showToast("Failed to save configuration: " + error.message, "danger"); } }); // Record type change event document .getElementById("record-type") .addEventListener("change", toggleMyIPOption); // Use my IP checkbox change event document.getElementById("use-my-ip").addEventListener("change", function () { const contentGroup = document.getElementById("content-group"); contentGroup.style.display = this.checked ? "none" : "block"; if (this.checked) { document.getElementById("record-content").value = currentIP; } }); // Save record button document .getElementById("save-record") .addEventListener("click", async function () { const id = document.getElementById("record-id").value; let name = document.getElementById("record-name").value; const type = document.getElementById("record-type").value; const content = document.getElementById("record-content").value; const ttl = parseInt(document.getElementById("record-ttl").value); const proxied = document.getElementById("record-proxied").checked; const useMyIP = document.getElementById("use-my-ip").checked; // Validate the form if (!name) { showToast("Name is required", "danger"); return; } if (!useMyIP && !content) { showToast("Content is required", "danger"); return; } // Prepare the record data const recordData = { name: name, type: type, content: useMyIP ? "" : content, ttl: ttl, proxied: proxied, use_my_ip: useMyIP, }; try { let response; if (id) { // Update existing record response = await fetch(`/api/records/${id}`, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(recordData), }); } else { // Create new record response = await fetch("/api/records", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(recordData), }); } if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to save record"); } // Hide the modal const modal = bootstrap.Modal.getInstance( document.getElementById("recordModal"), ); modal.hide(); showToast( id ? "Record updated successfully" : "Record created successfully", ); // Reset the form document.getElementById("record-form").reset(); document.getElementById("record-id").value = ""; // Reload DNS records await loadDNSRecords(); } catch (error) { console.error("Failed to save record:", error); showToast("Failed to save record: " + error.message, "danger"); } }); // Update all records button document .getElementById("update-all-records") .addEventListener("click", async function () { if ( !confirm( "Are you sure you want to update all A records to your current IP?", ) ) { return; } try { const response = await fetch("/api/update-all", { method: "POST", }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to update records"); } showToast("All A records updated to current IP"); // Reload DNS records await loadDNSRecords(); } catch (error) { console.error("Failed to update records:", error); showToast("Failed to update records: " + error.message, "danger"); } }); // Reset record form when opening the modal document .getElementById("recordModal") .addEventListener("show.bs.modal", function (event) { const button = event.relatedTarget; // If opening from the "Add Record" button, reset the form if (button && button.textContent.trim().includes("Add Record")) { document.getElementById("recordModalLabel").textContent = "Add DNS Record"; document.getElementById("record-form").reset(); document.getElementById("record-id").value = ""; document.getElementById("record-type").value = "A"; // Show/hide the "Use my current IP" option based on record type toggleMyIPOption(); } }); });