dev: automated commit - 2025-06-17 16:15:21
This commit is contained in:
parent
73e91db78b
commit
27775e6b29
16 changed files with 1860 additions and 1240 deletions
512
assets/js/app.js
512
assets/js/app.js
|
@ -1,512 +0,0 @@
|
||||||
// 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 = `
|
|
||||||
<div class="d-flex">
|
|
||||||
<div class="toast-body">
|
|
||||||
${message}
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
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 =
|
|
||||||
'<td colspan="6" class="text-center">No DNS records found</td>';
|
|
||||||
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
|
|
||||||
? '<span class="badge bg-success update-badge">Current IP</span>'
|
|
||||||
: record.type === "A"
|
|
||||||
? '<span class="badge bg-warning update-badge">Outdated IP</span>'
|
|
||||||
: "";
|
|
||||||
|
|
||||||
tr.innerHTML = `
|
|
||||||
<td>${record.type}</td>
|
|
||||||
<td>${record.name}</td>
|
|
||||||
<td>${record.content} ${ipBadge}</td>
|
|
||||||
<td>${record.ttl === 1 ? "Auto" : record.ttl + "s"}</td>
|
|
||||||
<td>${record.proxied ? '<i class="bi bi-check-lg text-success"></i>' : ""}</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-sm btn-outline-primary me-1 edit-record" data-id="${record.id}">
|
|
||||||
<i class="bi bi-pencil"></i>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-outline-danger delete-record" data-id="${record.id}" data-name="${record.name}">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -10,23 +10,22 @@ ON CONFLICT DO UPDATE SET
|
||||||
domain = excluded.domain,
|
domain = excluded.domain,
|
||||||
update_period = excluded.update_period;
|
update_period = excluded.update_period;
|
||||||
|
|
||||||
|
-- name: DeleteAllConfig :exec
|
||||||
|
DELETE FROM config;
|
||||||
|
|
||||||
|
-- name: InsertConfig :exec
|
||||||
|
INSERT INTO config (api_token, zone_id, domain, update_period)
|
||||||
|
VALUES (?, ?, ?, ?);
|
||||||
|
|
||||||
-- name: InitSchema :exec
|
-- name: InitSchema :exec
|
||||||
-- This query is used to ensure the schema is set up properly
|
|
||||||
CREATE TABLE IF NOT EXISTS config (
|
CREATE TABLE IF NOT EXISTS config (
|
||||||
api_token TEXT,
|
api_token TEXT NOT NULL DEFAULT '',
|
||||||
zone_id TEXT,
|
zone_id TEXT NOT NULL DEFAULT '',
|
||||||
domain TEXT NOT NULL DEFAULT 'mz.uy',
|
domain TEXT NOT NULL DEFAULT 'mz.uy',
|
||||||
update_period TEXT NOT NULL DEFAULT '0 */6 * * *'
|
update_period TEXT NOT NULL DEFAULT '0 */6 * * *'
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS enforce_single_config
|
|
||||||
BEFORE INSERT ON config
|
|
||||||
WHEN (SELECT COUNT(*) FROM config) > 0
|
|
||||||
BEGIN
|
|
||||||
SELECT RAISE(FAIL, 'Only one config record allowed');
|
|
||||||
END;
|
|
||||||
|
|
||||||
-- Insert default config if none exists
|
-- Insert default config if none exists
|
||||||
INSERT OR IGNORE INTO config (domain, update_period)
|
INSERT OR IGNORE INTO config (api_token, zone_id, domain, update_period)
|
||||||
SELECT 'mz.uy', '0 */6 * * *'
|
SELECT '', '', 'mz.uy', '0 */6 * * *'
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM config);
|
WHERE NOT EXISTS (SELECT 1 FROM config);
|
||||||
|
|
|
@ -9,6 +9,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const deleteAllConfig = `-- name: DeleteAllConfig :exec
|
||||||
|
DELETE FROM config
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteAllConfig(ctx context.Context) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteAllConfig)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const getConfig = `-- name: GetConfig :one
|
const getConfig = `-- name: GetConfig :one
|
||||||
SELECT api_token, zone_id, domain, update_period FROM config LIMIT 1
|
SELECT api_token, zone_id, domain, update_period FROM config LIMIT 1
|
||||||
`
|
`
|
||||||
|
@ -27,19 +36,40 @@ func (q *Queries) GetConfig(ctx context.Context) (Config, error) {
|
||||||
|
|
||||||
const initSchema = `-- name: InitSchema :exec
|
const initSchema = `-- name: InitSchema :exec
|
||||||
CREATE TABLE IF NOT EXISTS config (
|
CREATE TABLE IF NOT EXISTS config (
|
||||||
api_token TEXT,
|
api_token TEXT NOT NULL DEFAULT '',
|
||||||
zone_id TEXT,
|
zone_id TEXT NOT NULL DEFAULT '',
|
||||||
domain TEXT NOT NULL DEFAULT 'mz.uy',
|
domain TEXT NOT NULL DEFAULT 'mz.uy',
|
||||||
update_period TEXT NOT NULL DEFAULT '0 */6 * * *'
|
update_period TEXT NOT NULL DEFAULT '0 */6 * * *'
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
|
|
||||||
// This query is used to ensure the schema is set up properly
|
|
||||||
func (q *Queries) InitSchema(ctx context.Context) error {
|
func (q *Queries) InitSchema(ctx context.Context) error {
|
||||||
_, err := q.db.ExecContext(ctx, initSchema)
|
_, err := q.db.ExecContext(ctx, initSchema)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const insertConfig = `-- name: InsertConfig :exec
|
||||||
|
INSERT INTO config (api_token, zone_id, domain, update_period)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertConfigParams struct {
|
||||||
|
ApiToken string `json:"api_token"`
|
||||||
|
ZoneID string `json:"zone_id"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
UpdatePeriod string `json:"update_period"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertConfig(ctx context.Context, arg InsertConfigParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, insertConfig,
|
||||||
|
arg.ApiToken,
|
||||||
|
arg.ZoneID,
|
||||||
|
arg.Domain,
|
||||||
|
arg.UpdatePeriod,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const upsertConfig = `-- name: UpsertConfig :exec
|
const upsertConfig = `-- name: UpsertConfig :exec
|
||||||
INSERT INTO config (api_token, zone_id, domain, update_period)
|
INSERT INTO config (api_token, zone_id, domain, update_period)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?)
|
||||||
|
|
50
go.mod
50
go.mod
|
@ -3,24 +3,72 @@ module ddns-manager
|
||||||
go 1.24.1
|
go 1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.3.865
|
github.com/a-h/templ v0.3.898
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0
|
github.com/cloudflare/cloudflare-go v0.115.0
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/labstack/echo/v4 v4.13.3
|
github.com/labstack/echo/v4 v4.13.3
|
||||||
github.com/mattn/go-sqlite3 v1.14.28
|
github.com/mattn/go-sqlite3 v1.14.28
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cel.dev/expr v0.19.1 // indirect
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||||
|
github.com/cubicdaiya/gonp v1.0.4 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/fatih/structtag v1.2.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/google/cel-go v0.24.1 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb // indirect
|
||||||
|
github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 // indirect
|
||||||
|
github.com/pingcap/log v1.1.0 // indirect
|
||||||
|
github.com/pingcap/tidb/pkg/parser v0.0.0-20250324122243-d51e00e5bbf0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/riza-io/grpc-go v0.2.0 // indirect
|
||||||
|
github.com/spf13/cobra v1.9.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
|
github.com/sqlc-dev/sqlc v1.29.0 // indirect
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
|
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 // indirect
|
||||||
|
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.37.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
|
golang.org/x/sync v0.13.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.24.0 // indirect
|
||||||
golang.org/x/time v0.9.0 // indirect
|
golang.org/x/time v0.9.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
|
google.golang.org/grpc v1.71.1 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.62.1 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.9.1 // indirect
|
||||||
|
modernc.org/sqlite v1.37.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tool github.com/sqlc-dev/sqlc/cmd/sqlc
|
||||||
|
|
137
go.sum
137
go.sum
|
@ -1,16 +1,56 @@
|
||||||
github.com/a-h/templ v0.3.865 h1:nYn5EWm9EiXaDgWcMQaKiKvrydqgxDUtT1+4zU2C43A=
|
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
|
||||||
github.com/a-h/templ v0.3.865/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
|
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s=
|
||||||
|
github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
|
||||||
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
|
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/cubicdaiya/gonp v1.0.4 h1:ky2uIAJh81WiLcGKBVD5R7KsM/36W6IqqTy6Bo6rGws=
|
||||||
|
github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYYACpOI0I=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||||
|
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI=
|
||||||
|
github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
@ -22,28 +62,121 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pganalyze/pg_query_go/v6 v6.1.0 h1:jG5ZLhcVgL1FAw4C/0VNQaVmX1SUJx71wBGdtTtBvls=
|
||||||
|
github.com/pganalyze/pg_query_go/v6 v6.1.0/go.mod h1:nvTHIuoud6e1SfrUaFwHqT0i4b5Nr+1rPWVds3B5+50=
|
||||||
|
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb h1:3pSi4EDG6hg0orE1ndHkXvX6Qdq2cZn8gAPir8ymKZk=
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
|
||||||
|
github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 h1:tdMsjOqUR7YXHoBitzdebTvOjs/swniBTOLy5XiMtuE=
|
||||||
|
github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86/go.mod h1:exzhVYca3WRtd6gclGNErRWb1qEgff3LYta0LvRmON4=
|
||||||
|
github.com/pingcap/log v1.1.0 h1:ELiPxACz7vdo1qAvvaWJg1NrYFoY6gqAh/+Uo6aXdD8=
|
||||||
|
github.com/pingcap/log v1.1.0/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4=
|
||||||
|
github.com/pingcap/tidb/pkg/parser v0.0.0-20250324122243-d51e00e5bbf0 h1:W3rpAI3bubR6VWOcwxDIG0Gz9G5rl5b3SL116T0vBt0=
|
||||||
|
github.com/pingcap/tidb/pkg/parser v0.0.0-20250324122243-d51e00e5bbf0/go.mod h1:+8feuexTKcXHZF/dkDfvCwEyBAmgb4paFc3/WeYV2eE=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ=
|
||||||
|
github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/sqlc-dev/sqlc v1.29.0 h1:HQctoD7y/i29Bao53qXO7CZ/BV9NcvpGpsJWvz9nKWs=
|
||||||
|
github.com/sqlc-dev/sqlc v1.29.0/go.mod h1:BavmYw11px5AdPOjAVHmb9fctP5A8GTziC38wBF9tp0=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||||
|
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo=
|
||||||
|
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM=
|
||||||
|
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4=
|
||||||
|
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||||
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
|
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||||
|
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||||
|
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
||||||
|
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
|
||||||
|
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
||||||
|
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||||
|
|
514
main.go
514
main.go
|
@ -6,8 +6,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -18,7 +20,7 @@ import (
|
||||||
"github.com/cloudflare/cloudflare-go"
|
"github.com/cloudflare/cloudflare-go"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
_ "github.com/mattn/go-sqlite3" // SQLite driver
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,54 +32,189 @@ var (
|
||||||
queries *db.Queries
|
queries *db.Queries
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Simple validation
|
||||||
|
func validateDNSRecord(name, recordType, content string) error {
|
||||||
|
if strings.TrimSpace(name) == "" {
|
||||||
|
return fmt.Errorf("name is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(content) == "" {
|
||||||
|
return fmt.Errorf("content is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate by type
|
||||||
|
switch recordType {
|
||||||
|
case "A":
|
||||||
|
if net.ParseIP(content) == nil {
|
||||||
|
return fmt.Errorf("invalid IP address")
|
||||||
|
}
|
||||||
|
case "CNAME":
|
||||||
|
if !regexp.MustCompile(`^[a-zA-Z0-9\-\.]+$`).MatchString(content) {
|
||||||
|
return fmt.Errorf("invalid domain name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean input sanitization
|
||||||
|
func sanitizeInput(input string) string {
|
||||||
|
return strings.TrimSpace(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced error responses
|
||||||
|
func errorResponse(c echo.Context, message string) error {
|
||||||
|
c.Response().WriteHeader(http.StatusBadRequest)
|
||||||
|
return templates.Render(c.Response(), templates.ErrorNotification(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func successResponse(c echo.Context, message string) error {
|
||||||
|
return templates.Render(c.Response(), templates.SuccessNotification(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improved createDNSRecord
|
||||||
|
func createDNSRecord(zoneID, domain, name, recordType, content string, ttl int, proxied bool) error {
|
||||||
|
if api == nil {
|
||||||
|
return fmt.Errorf("cloudflare API not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if err := validateDNSRecord(name, recordType, content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare full name
|
||||||
|
fullName := name
|
||||||
|
if name != "@" && !strings.HasSuffix(name, domain) {
|
||||||
|
fullName = name + "." + domain
|
||||||
|
}
|
||||||
|
if name == "@" {
|
||||||
|
fullName = domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create record
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rc := cloudflare.ZoneIdentifier(zoneID)
|
||||||
|
_, err := api.CreateDNSRecord(ctx, rc, cloudflare.CreateDNSRecordParams{
|
||||||
|
Type: recordType,
|
||||||
|
Name: fullName,
|
||||||
|
Content: content,
|
||||||
|
TTL: ttl,
|
||||||
|
Proxied: &proxied,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Simple error handling
|
||||||
|
if cfErr, ok := err.(*cloudflare.Error); ok {
|
||||||
|
switch cfErr.ErrorCodes[0] {
|
||||||
|
case 10000:
|
||||||
|
return fmt.Errorf("invalid API credentials")
|
||||||
|
case 81044:
|
||||||
|
return fmt.Errorf("record already exists")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cloudflare error: %s", cfErr.ErrorMessages[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to create record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDNSRecord(zoneID, id, name, recordType, content string, ttl int, proxied bool) error {
|
||||||
|
if api == nil {
|
||||||
|
return fmt.Errorf("cloudflare API not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateDNSRecord(name, recordType, content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rc := cloudflare.ZoneIdentifier(zoneID)
|
||||||
|
_, err := api.UpdateDNSRecord(ctx, rc, cloudflare.UpdateDNSRecordParams{
|
||||||
|
ID: id,
|
||||||
|
Type: recordType,
|
||||||
|
Name: name,
|
||||||
|
Content: content,
|
||||||
|
TTL: ttl,
|
||||||
|
Proxied: &proxied,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if cfErr, ok := err.(*cloudflare.Error); ok {
|
||||||
|
return fmt.Errorf("cloudflare error: %s", cfErr.ErrorMessages[0])
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to update record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteDNSRecord(zoneID, id string) error {
|
||||||
|
if api == nil {
|
||||||
|
return fmt.Errorf("cloudflare API not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rc := cloudflare.ZoneIdentifier(zoneID)
|
||||||
|
err := api.DeleteDNSRecord(ctx, rc, id)
|
||||||
|
if err != nil {
|
||||||
|
if cfErr, ok := err.(*cloudflare.Error); ok {
|
||||||
|
return fmt.Errorf("cloudflare error: %s", cfErr.ErrorMessages[0])
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to delete record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func initDatabase() (*sql.DB, error) {
|
func initDatabase() (*sql.DB, error) {
|
||||||
dbPath := os.Getenv("DB_PATH")
|
dbPath := os.Getenv("DB_PATH")
|
||||||
if dbPath == "" {
|
if dbPath == "" {
|
||||||
dbPath = "./ddns.db"
|
dbPath = "./ddns.db"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Using database path: %s", dbPath)
|
|
||||||
|
|
||||||
sqlDB, err := sql.Open("sqlite3", dbPath)
|
sqlDB, err := sql.Open("sqlite3", dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.InitSchema(sqlDB); err != nil {
|
if err := db.InitSchema(sqlDB); err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize schema: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
queries = db.New(sqlDB)
|
queries = db.New(sqlDB)
|
||||||
return sqlDB, nil
|
return sqlDB, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCloudflare(apiToken, zoneID string) error {
|
func initCloudflare(apiToken string) error {
|
||||||
if apiToken == "" || zoneID == "" {
|
if apiToken == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
api, err = cloudflare.NewWithAPIToken(apiToken)
|
api, err = cloudflare.NewWithAPIToken(apiToken)
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("failed to initialize Cloudflare API: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentIP() (string, error) {
|
func getCurrentIP() (string, error) {
|
||||||
resp, err := http.Get("https://api.ipify.org")
|
resp, err := http.Get("https://api.ipify.org")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get current IP: %w", err)
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
ip, err := io.ReadAll(resp.Body)
|
ip, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to read IP response: %w", err)
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(ip), nil
|
return strings.TrimSpace(string(ip)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) {
|
func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) {
|
||||||
|
@ -85,12 +222,13 @@ func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) {
|
||||||
return nil, fmt.Errorf("cloudflare API not initialized")
|
return nil, fmt.Errorf("cloudflare API not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
rc := cloudflare.ZoneIdentifier(zoneID)
|
defer cancel()
|
||||||
|
|
||||||
|
rc := cloudflare.ZoneIdentifier(zoneID)
|
||||||
recs, _, err := api.ListDNSRecords(ctx, rc, cloudflare.ListDNSRecordsParams{})
|
recs, _, err := api.ListDNSRecords(ctx, rc, cloudflare.ListDNSRecordsParams{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get DNS records: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var records []templates.DNSRecord
|
var records []templates.DNSRecord
|
||||||
|
@ -109,117 +247,39 @@ func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) {
|
||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDNSRecord(zoneID, domain, name, recordType, content string, ttl int, proxied bool) error {
|
|
||||||
if api == nil {
|
|
||||||
return fmt.Errorf("cloudflare API not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(name, domain) && name != "@" {
|
|
||||||
name = name + "." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "@" {
|
|
||||||
name = domain
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
rc := cloudflare.ZoneIdentifier(zoneID)
|
|
||||||
|
|
||||||
_, err := api.CreateDNSRecord(ctx, rc, cloudflare.CreateDNSRecordParams{
|
|
||||||
Type: recordType,
|
|
||||||
Name: name,
|
|
||||||
Content: content,
|
|
||||||
TTL: ttl,
|
|
||||||
Proxied: &proxied,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create DNS record: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateDNSRecord(zoneID, id, name, recordType, content string, ttl int, proxied bool) error {
|
|
||||||
if api == nil {
|
|
||||||
return fmt.Errorf("cloudflare API not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
rc := cloudflare.ZoneIdentifier(zoneID)
|
|
||||||
|
|
||||||
_, err := api.UpdateDNSRecord(ctx, rc, cloudflare.UpdateDNSRecordParams{
|
|
||||||
ID: id,
|
|
||||||
Type: recordType,
|
|
||||||
Name: name,
|
|
||||||
Content: content,
|
|
||||||
TTL: ttl,
|
|
||||||
Proxied: &proxied,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to update DNS record: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteDNSRecord(zoneID, id string) error {
|
|
||||||
if api == nil {
|
|
||||||
return fmt.Errorf("cloudflare API not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
rc := cloudflare.ZoneIdentifier(zoneID)
|
|
||||||
|
|
||||||
err := api.DeleteDNSRecord(ctx, rc, id)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete DNS record: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateAllRecordsWithCurrentIP(zoneID string) error {
|
func updateAllRecordsWithCurrentIP(zoneID string) error {
|
||||||
if api == nil {
|
|
||||||
return fmt.Errorf("cloudflare API not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
currentIP, err := getCurrentIP()
|
currentIP, err := getCurrentIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentIP == lastIP {
|
if currentIP == lastIP {
|
||||||
log.Println("IP hasn't changed, no updates needed")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIP = currentIP
|
lastIP = currentIP
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
rc := cloudflare.ZoneIdentifier(zoneID)
|
defer cancel()
|
||||||
|
|
||||||
records, _, err := api.ListDNSRecords(ctx, rc, cloudflare.ListDNSRecordsParams{
|
rc := cloudflare.ZoneIdentifier(zoneID)
|
||||||
Type: "A",
|
records, _, err := api.ListDNSRecords(ctx, rc, cloudflare.ListDNSRecordsParams{Type: "A"})
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get DNS records: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
if rec.Content != currentIP {
|
if rec.Content != currentIP {
|
||||||
proxied := rec.Proxied
|
|
||||||
_, err := api.UpdateDNSRecord(ctx, rc, cloudflare.UpdateDNSRecordParams{
|
_, err := api.UpdateDNSRecord(ctx, rc, cloudflare.UpdateDNSRecordParams{
|
||||||
ID: rec.ID,
|
ID: rec.ID,
|
||||||
Type: rec.Type,
|
Type: rec.Type,
|
||||||
Name: rec.Name,
|
Name: rec.Name,
|
||||||
Content: currentIP,
|
Content: currentIP,
|
||||||
TTL: rec.TTL,
|
TTL: rec.TTL,
|
||||||
Proxied: proxied,
|
Proxied: rec.Proxied,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to update record %s: %v", rec.Name, err)
|
log.Printf("Failed to update record %s: %v", rec.Name, err)
|
||||||
} else {
|
|
||||||
log.Printf("Updated record %s to %s", rec.Name, currentIP)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,26 +290,22 @@ func updateAllRecordsWithCurrentIP(zoneID string) error {
|
||||||
func scheduleUpdates(zoneID, updatePeriod string) error {
|
func scheduleUpdates(zoneID, updatePeriod string) error {
|
||||||
if jobID != 0 {
|
if jobID != 0 {
|
||||||
scheduler.Remove(jobID)
|
scheduler.Remove(jobID)
|
||||||
|
log.Println("Scheduled update removed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatePeriod == "" {
|
if updatePeriod == "" {
|
||||||
log.Println("Automatic updates disabled")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
jobID, err = scheduler.AddFunc(updatePeriod, func() {
|
jobID, err = scheduler.AddFunc(updatePeriod, func() {
|
||||||
log.Println("Running scheduled IP update")
|
|
||||||
if err := updateAllRecordsWithCurrentIP(zoneID); err != nil {
|
if err := updateAllRecordsWithCurrentIP(zoneID); err != nil {
|
||||||
log.Printf("Scheduled update failed: %v", err)
|
log.Printf("Scheduled update failed: %v", err)
|
||||||
}
|
}
|
||||||
|
log.Println("Scheduled update completed")
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to schedule updates: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Scheduled IP updates with cron: %s", updatePeriod)
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUpdateFrequencies() []templates.UpdateFrequency {
|
func getUpdateFrequencies() []templates.UpdateFrequency {
|
||||||
|
@ -265,44 +321,42 @@ func getUpdateFrequencies() []templates.UpdateFrequency {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Initialize database
|
||||||
sqlDB, err := initDatabase()
|
sqlDB, err := initDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to initialize database: %v", err)
|
log.Fatalf("Database init failed: %v", err)
|
||||||
}
|
}
|
||||||
defer sqlDB.Close()
|
defer sqlDB.Close()
|
||||||
|
|
||||||
|
// Load config
|
||||||
config, err := queries.GetConfig(context.Background())
|
config, err := queries.GetConfig(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Warning: Failed to load configuration: %v", err)
|
config = db.Config{Domain: "example.com", UpdatePeriod: "0 */6 * * *"}
|
||||||
config = db.Config{
|
|
||||||
Domain: "mz.uy",
|
|
||||||
UpdatePeriod: "0 */6 * * *",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initCloudflare(config.ApiToken, config.ZoneID); err != nil {
|
// Initialize Cloudflare
|
||||||
log.Printf("Warning: Cloudflare initialization failed: %v", err)
|
if err := initCloudflare(config.ApiToken); err != nil {
|
||||||
|
log.Printf("Cloudflare init failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize scheduler
|
||||||
scheduler = cron.New()
|
scheduler = cron.New()
|
||||||
scheduler.Start()
|
scheduler.Start()
|
||||||
defer scheduler.Stop()
|
defer scheduler.Stop()
|
||||||
|
|
||||||
if config.ApiToken != "" && config.ZoneID != "" && config.UpdatePeriod != "" {
|
if config.ApiToken != "" && config.ZoneID != "" && config.UpdatePeriod != "" {
|
||||||
if err := scheduleUpdates(config.ZoneID, config.UpdatePeriod); err != nil {
|
scheduleUpdates(config.ZoneID, config.UpdatePeriod)
|
||||||
log.Printf("Warning: Failed to schedule updates: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup Echo
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
e.Use(middleware.Logger())
|
e.Use(middleware.Logger())
|
||||||
e.Use(middleware.Recover())
|
e.Use(middleware.Recover())
|
||||||
e.Static("/assets", "assets")
|
e.Use(middleware.CORS())
|
||||||
|
|
||||||
// Main page
|
// Routes
|
||||||
e.GET("/", func(c echo.Context) error {
|
e.GET("/", func(c echo.Context) error {
|
||||||
currentIP, _ := getCurrentIP()
|
currentIP, _ := getCurrentIP()
|
||||||
|
|
||||||
var records []templates.DNSRecord
|
var records []templates.DNSRecord
|
||||||
isConfigured := config.ApiToken != "" && config.ZoneID != ""
|
isConfigured := config.ApiToken != "" && config.ZoneID != ""
|
||||||
|
|
||||||
|
@ -310,8 +364,8 @@ func main() {
|
||||||
records, _ = getDNSRecords(config.ZoneID)
|
records, _ = getDNSRecords(config.ZoneID)
|
||||||
}
|
}
|
||||||
|
|
||||||
component := templates.Index(templates.IndexProps{
|
return templates.Render(c.Response(), templates.Index(templates.IndexProps{
|
||||||
Title: "mz.uy DNS Manager",
|
Title: "DNS Manager",
|
||||||
IsConfigured: isConfigured,
|
IsConfigured: isConfigured,
|
||||||
CurrentIP: currentIP,
|
CurrentIP: currentIP,
|
||||||
Config: templates.ConfigData{
|
Config: templates.ConfigData{
|
||||||
|
@ -322,145 +376,126 @@ func main() {
|
||||||
},
|
},
|
||||||
Records: records,
|
Records: records,
|
||||||
UpdateFreqs: getUpdateFrequencies(),
|
UpdateFreqs: getUpdateFrequencies(),
|
||||||
})
|
}))
|
||||||
return templates.Render(c.Response(), component)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Refresh current IP
|
|
||||||
e.GET("/refresh-ip", func(c echo.Context) error {
|
e.GET("/refresh-ip", func(c echo.Context) error {
|
||||||
ip, err := getCurrentIP()
|
ip, err := getCurrentIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to get current IP")
|
return errorResponse(c, "Failed to get current IP")
|
||||||
return c.String(http.StatusInternalServerError, "Error")
|
|
||||||
}
|
}
|
||||||
return c.String(http.StatusOK, ip)
|
return c.HTML(http.StatusOK, fmt.Sprintf(`<span id="current-ip" class="fw-bold">%s</span>`, ip))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Configuration
|
|
||||||
e.POST("/config", func(c echo.Context) error {
|
e.POST("/config", func(c echo.Context) error {
|
||||||
apiToken := c.FormValue("api_token")
|
apiToken := sanitizeInput(c.FormValue("api_token"))
|
||||||
zoneID := c.FormValue("zone_id")
|
zoneID := sanitizeInput(c.FormValue("zone_id"))
|
||||||
domain := c.FormValue("domain")
|
domain := sanitizeInput(c.FormValue("domain"))
|
||||||
updatePeriod := c.FormValue("update_period")
|
updatePeriod := sanitizeInput(c.FormValue("update_period"))
|
||||||
|
|
||||||
if apiToken == "" || zoneID == "" || domain == "" {
|
if apiToken == "" || zoneID == "" || domain == "" {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Please fill all required fields")
|
return errorResponse(c, "Please fill all required fields")
|
||||||
return c.String(http.StatusBadRequest, "Invalid input")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := queries.UpsertConfig(context.Background(), db.UpsertConfigParams{
|
// Save config
|
||||||
|
queries.DeleteAllConfig(context.Background())
|
||||||
|
err := queries.InsertConfig(context.Background(), db.InsertConfigParams{
|
||||||
ApiToken: apiToken,
|
ApiToken: apiToken,
|
||||||
ZoneID: zoneID,
|
ZoneID: zoneID,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
UpdatePeriod: updatePeriod,
|
UpdatePeriod: updatePeriod,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to save configuration")
|
return errorResponse(c, "Failed to save configuration")
|
||||||
return c.String(http.StatusInternalServerError, "Database error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update global config
|
||||||
config.ApiToken = apiToken
|
config.ApiToken = apiToken
|
||||||
config.ZoneID = zoneID
|
config.ZoneID = zoneID
|
||||||
config.Domain = domain
|
config.Domain = domain
|
||||||
config.UpdatePeriod = updatePeriod
|
config.UpdatePeriod = updatePeriod
|
||||||
|
|
||||||
if err := initCloudflare(config.ApiToken, config.ZoneID); err != nil {
|
// Reinitialize Cloudflare
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to initialize Cloudflare client")
|
initCloudflare(apiToken)
|
||||||
return c.String(http.StatusInternalServerError, "API error")
|
scheduleUpdates(zoneID, updatePeriod)
|
||||||
}
|
|
||||||
|
|
||||||
if err := scheduleUpdates(config.ZoneID, config.UpdatePeriod); err != nil {
|
return templates.Render(c.Response(), templates.ConfigStatus(templates.ConfigData{
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to schedule updates")
|
ZoneID: zoneID,
|
||||||
return c.String(http.StatusInternalServerError, "Scheduler error")
|
Domain: domain,
|
||||||
}
|
UpdatePeriod: updatePeriod,
|
||||||
|
ApiToken: apiToken,
|
||||||
c.Response().Header().Set("HX-Success-Message", "Configuration saved successfully")
|
}))
|
||||||
return c.Redirect(http.StatusSeeOther, "/")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create DNS record
|
e.GET("/config", func(c echo.Context) error {
|
||||||
e.POST("/records", func(c echo.Context) error {
|
return templates.Render(c.Response(), templates.ConfigModal(templates.ConfigData{
|
||||||
if config.ApiToken == "" || config.ZoneID == "" {
|
ZoneID: config.ZoneID,
|
||||||
c.Response().Header().Set("HX-Error-Message", "API not configured")
|
Domain: config.Domain,
|
||||||
return c.String(http.StatusBadRequest, "Not configured")
|
UpdatePeriod: config.UpdatePeriod,
|
||||||
}
|
ApiToken: config.ApiToken,
|
||||||
|
}, getUpdateFrequencies()))
|
||||||
|
})
|
||||||
|
|
||||||
name := c.FormValue("name")
|
e.GET("/records/new", func(c echo.Context) error {
|
||||||
recordType := c.FormValue("type")
|
return templates.Render(c.Response(), templates.RecordForm("Add DNS Record", "", config.Domain, templates.DNSRecord{Type: "A", TTL: 1}))
|
||||||
content := c.FormValue("content")
|
})
|
||||||
ttlStr := c.FormValue("ttl")
|
|
||||||
|
e.POST("/records", func(c echo.Context) error {
|
||||||
|
name := sanitizeInput(c.FormValue("name"))
|
||||||
|
recordType := sanitizeInput(c.FormValue("type"))
|
||||||
|
content := sanitizeInput(c.FormValue("content"))
|
||||||
|
ttlStr := sanitizeInput(c.FormValue("ttl"))
|
||||||
proxied := c.FormValue("proxied") == "on"
|
proxied := c.FormValue("proxied") == "on"
|
||||||
useMyIP := c.FormValue("use_my_ip") == "on"
|
useMyIP := c.FormValue("use_my_ip") == "on"
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
c.Response().Header().Set("HX-Error-Message", "Name is required")
|
|
||||||
return c.String(http.StatusBadRequest, "Invalid input")
|
|
||||||
}
|
|
||||||
|
|
||||||
ttl, err := strconv.Atoi(ttlStr)
|
|
||||||
if err != nil {
|
|
||||||
ttl = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if useMyIP {
|
if useMyIP {
|
||||||
currentIP, err := getCurrentIP()
|
currentIP, err := getCurrentIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to get current IP")
|
return errorResponse(c, "Failed to get current IP")
|
||||||
return c.String(http.StatusInternalServerError, "IP error")
|
|
||||||
}
|
}
|
||||||
content = currentIP
|
content = currentIP
|
||||||
}
|
}
|
||||||
|
|
||||||
if content == "" {
|
ttl, _ := strconv.Atoi(ttlStr)
|
||||||
c.Response().Header().Set("HX-Error-Message", "Content is required")
|
if ttl == 0 {
|
||||||
return c.String(http.StatusBadRequest, "Invalid input")
|
ttl = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err = createDNSRecord(config.ZoneID, config.Domain, name, recordType, content, ttl, proxied)
|
if err := createDNSRecord(config.ZoneID, config.Domain, name, recordType, content, ttl, proxied); err != nil {
|
||||||
if err != nil {
|
return errorResponse(c, err.Error())
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to create DNS record")
|
|
||||||
return c.String(http.StatusInternalServerError, "DNS error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Response().Header().Set("HX-Success-Message", "DNS record created successfully")
|
// Return updated table
|
||||||
|
|
||||||
// Return updated records table
|
|
||||||
records, _ := getDNSRecords(config.ZoneID)
|
records, _ := getDNSRecords(config.ZoneID)
|
||||||
currentIP, _ := getCurrentIP()
|
currentIP, _ := getCurrentIP()
|
||||||
component := templates.DNSRecordsTable(records, currentIP)
|
notification := templates.SuccessNotification("DNS record created")
|
||||||
return templates.Render(c.Response(), component)
|
table := templates.DNSRecordsTable(records, currentIP)
|
||||||
|
return templates.RenderMultiple(c.Response().Writer, notification, table)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update DNS record
|
|
||||||
e.PUT("/records/:id", func(c echo.Context) error {
|
e.PUT("/records/:id", func(c echo.Context) error {
|
||||||
if config.ApiToken == "" || config.ZoneID == "" {
|
|
||||||
c.Response().Header().Set("HX-Error-Message", "API not configured")
|
|
||||||
return c.String(http.StatusBadRequest, "Not configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
name := c.FormValue("name")
|
name := sanitizeInput(c.FormValue("name"))
|
||||||
recordType := c.FormValue("type")
|
recordType := sanitizeInput(c.FormValue("type"))
|
||||||
content := c.FormValue("content")
|
content := sanitizeInput(c.FormValue("content"))
|
||||||
ttlStr := c.FormValue("ttl")
|
ttlStr := sanitizeInput(c.FormValue("ttl"))
|
||||||
proxied := c.FormValue("proxied") == "on"
|
proxied := c.FormValue("proxied") == "on"
|
||||||
useMyIP := c.FormValue("use_my_ip") == "on"
|
useMyIP := c.FormValue("use_my_ip") == "on"
|
||||||
|
|
||||||
ttl, err := strconv.Atoi(ttlStr)
|
|
||||||
if err != nil {
|
|
||||||
ttl = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if useMyIP {
|
if useMyIP {
|
||||||
currentIP, err := getCurrentIP()
|
currentIP, err := getCurrentIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to get current IP")
|
return errorResponse(c, "Failed to get current IP")
|
||||||
return c.String(http.StatusInternalServerError, "IP error")
|
|
||||||
}
|
}
|
||||||
content = currentIP
|
content = currentIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert name to full domain name if needed
|
ttl, _ := strconv.Atoi(ttlStr)
|
||||||
|
if ttl == 0 {
|
||||||
|
ttl = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert name to full domain
|
||||||
fullName := name
|
fullName := name
|
||||||
if name != "@" && !strings.HasSuffix(name, config.Domain) {
|
if name != "@" && !strings.HasSuffix(name, config.Domain) {
|
||||||
fullName = name + "." + config.Domain
|
fullName = name + "." + config.Domain
|
||||||
|
@ -469,51 +504,36 @@ func main() {
|
||||||
fullName = config.Domain
|
fullName = config.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
err = updateDNSRecord(config.ZoneID, id, fullName, recordType, content, ttl, proxied)
|
if err := updateDNSRecord(config.ZoneID, id, fullName, recordType, content, ttl, proxied); err != nil {
|
||||||
if err != nil {
|
return errorResponse(c, err.Error())
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to update DNS record")
|
|
||||||
return c.String(http.StatusInternalServerError, "DNS error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Response().Header().Set("HX-Success-Message", "DNS record updated successfully")
|
|
||||||
|
|
||||||
// Return updated records table
|
|
||||||
records, _ := getDNSRecords(config.ZoneID)
|
records, _ := getDNSRecords(config.ZoneID)
|
||||||
currentIP, _ := getCurrentIP()
|
currentIP, _ := getCurrentIP()
|
||||||
component := templates.DNSRecordsTable(records, currentIP)
|
notification := templates.SuccessNotification("DNS record updated")
|
||||||
return templates.Render(c.Response(), component)
|
table := templates.DNSRecordsTable(records, currentIP)
|
||||||
|
return templates.RenderMultiple(c.Response().Writer, notification, table)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete DNS record
|
|
||||||
e.DELETE("/records/:id", func(c echo.Context) error {
|
e.DELETE("/records/:id", func(c echo.Context) error {
|
||||||
if config.ApiToken == "" || config.ZoneID == "" {
|
|
||||||
c.Response().Header().Set("HX-Error-Message", "API not configured")
|
|
||||||
return c.String(http.StatusBadRequest, "Not configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
err := deleteDNSRecord(config.ZoneID, id)
|
|
||||||
if err != nil {
|
if err := deleteDNSRecord(config.ZoneID, id); err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to delete DNS record")
|
return errorResponse(c, "Failed to delete record")
|
||||||
return c.String(http.StatusInternalServerError, "DNS error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Response().Header().Set("HX-Success-Message", "DNS record deleted successfully")
|
records, _ := getDNSRecords(config.ZoneID)
|
||||||
return c.String(http.StatusOK, "")
|
currentIP, _ := getCurrentIP()
|
||||||
|
notification := templates.SuccessNotification("DNS record deleted")
|
||||||
|
table := templates.DNSRecordsTable(records, currentIP)
|
||||||
|
return templates.RenderMultiple(c.Response().Writer, notification, table)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Edit record form
|
|
||||||
e.GET("/edit-record/:id", func(c echo.Context) error {
|
e.GET("/edit-record/:id", func(c echo.Context) error {
|
||||||
if config.ApiToken == "" || config.ZoneID == "" {
|
|
||||||
c.Response().Header().Set("HX-Error-Message", "API not configured")
|
|
||||||
return c.String(http.StatusBadRequest, "Not configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
records, err := getDNSRecords(config.ZoneID)
|
records, err := getDNSRecords(config.ZoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to load DNS records")
|
return errorResponse(c, "Failed to load records")
|
||||||
return c.String(http.StatusInternalServerError, "DNS error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var record templates.DNSRecord
|
var record templates.DNSRecord
|
||||||
|
@ -525,36 +545,24 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if record.ID == "" {
|
if record.ID == "" {
|
||||||
c.Response().Header().Set("HX-Error-Message", "Record not found")
|
return errorResponse(c, "Record not found")
|
||||||
return c.String(http.StatusNotFound, "Not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
component := templates.RecordForm("Edit DNS Record", id, config.Domain, record)
|
return templates.Render(c.Response(), templates.RecordForm("Edit DNS Record", id, config.Domain, record))
|
||||||
return templates.Render(c.Response(), component)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update all records with current IP
|
|
||||||
e.POST("/update-all-records", func(c echo.Context) error {
|
e.POST("/update-all-records", func(c echo.Context) error {
|
||||||
if config.ApiToken == "" || config.ZoneID == "" {
|
if err := updateAllRecordsWithCurrentIP(config.ZoneID); err != nil {
|
||||||
c.Response().Header().Set("HX-Error-Message", "API not configured")
|
return errorResponse(c, "Failed to update records")
|
||||||
return c.String(http.StatusBadRequest, "Not configured")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := updateAllRecordsWithCurrentIP(config.ZoneID)
|
|
||||||
if err != nil {
|
|
||||||
c.Response().Header().Set("HX-Error-Message", "Failed to update records")
|
|
||||||
return c.String(http.StatusInternalServerError, "Update error")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Response().Header().Set("HX-Success-Message", "All A records updated with current IP")
|
|
||||||
|
|
||||||
// Return updated records table
|
|
||||||
records, _ := getDNSRecords(config.ZoneID)
|
records, _ := getDNSRecords(config.ZoneID)
|
||||||
currentIP, _ := getCurrentIP()
|
currentIP, _ := getCurrentIP()
|
||||||
component := templates.DNSRecordsTable(records, currentIP)
|
notification := templates.SuccessNotification("All A records updated")
|
||||||
return templates.Render(c.Response(), component)
|
table := templates.DNSRecordsTable(records, currentIP)
|
||||||
|
return templates.RenderMultiple(c.Response().Writer, notification, table)
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Println("Starting server on http://localhost:3000")
|
log.Println("Starting server on :3000")
|
||||||
log.Fatal(e.Start(":3000"))
|
log.Fatal(e.Start(":3000"))
|
||||||
}
|
}
|
||||||
|
|
34
templates/alert.templ
Normal file
34
templates/alert.templ
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
package templates
|
||||||
|
|
||||||
|
templ Alert(id, alertType, message string, dismissible bool) {
|
||||||
|
<div
|
||||||
|
id={ id }
|
||||||
|
class={ "alert", "alert-" + alertType, templ.KV("alert-dismissible", dismissible), templ.KV("d-none", message == "") }
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
if message != "" {
|
||||||
|
{ message }
|
||||||
|
}
|
||||||
|
if dismissible {
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper components for common alert types
|
||||||
|
templ ErrorAlert(id, message string) {
|
||||||
|
@Alert(id, "danger", message, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ SuccessAlert(id, message string) {
|
||||||
|
@Alert(id, "success", message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ WarningAlert(id, message string) {
|
||||||
|
@Alert(id, "warning", message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ InfoAlert(id, message string) {
|
||||||
|
@Alert(id, "info", message, true)
|
||||||
|
}
|
214
templates/alert_templ.go
Normal file
214
templates/alert_templ.go
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.898
|
||||||
|
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
func Alert(id, alertType, message string, dismissible bool) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
var templ_7745c5c3_Var2 = []any{"alert", "alert-" + alertType, templ.KV("alert-dismissible", dismissible), templ.KV("d-none", message == "")}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/alert.templ`, Line: 6, Col: 9}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/alert.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" role=\"alert\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if message != "" {
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(message)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/alert.templ`, Line: 11, Col: 12}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dismissible {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper components for common alert types
|
||||||
|
func ErrorAlert(id, message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var6 == nil {
|
||||||
|
templ_7745c5c3_Var6 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = Alert(id, "danger", message, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuccessAlert(id, message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var7 == nil {
|
||||||
|
templ_7745c5c3_Var7 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = Alert(id, "success", message, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarningAlert(id, message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var8 == nil {
|
||||||
|
templ_7745c5c3_Var8 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = Alert(id, "warning", message, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func InfoAlert(id, message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var9 == nil {
|
||||||
|
templ_7745c5c3_Var9 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = Alert(id, "info", message, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
|
@ -43,17 +43,16 @@ templ Index(props IndexProps) {
|
||||||
<h1>{ props.Title }</h1>
|
<h1>{ props.Title }</h1>
|
||||||
<div class="current-ip d-flex align-items-center">
|
<div class="current-ip d-flex align-items-center">
|
||||||
<span class="me-2">Current IP:</span>
|
<span class="me-2">Current IP:</span>
|
||||||
<span id="current-ip" class="fw-bold">{ props.CurrentIP }</span>
|
<span id="current-ip" x-init class="fw-bold">{ props.CurrentIP }</span>
|
||||||
<button
|
<a
|
||||||
class="btn btn-sm btn-outline-secondary ms-2"
|
href="/refresh-ip"
|
||||||
title="Refresh current IP"
|
x-target="current-ip"
|
||||||
hx-get="/refresh-ip"
|
class="btn btn-sm btn-outline-secondary ms-2 d-inline-flex align-items-center"
|
||||||
hx-target="#current-ip"
|
@ajax:before="$el.querySelector('.bi-arrow-clockwise').classList.add('spin')"
|
||||||
hx-indicator="#refresh-spinner"
|
@ajax:after="$el.querySelector('.bi-arrow-clockwise').classList.remove('spin')"
|
||||||
>
|
>
|
||||||
<i class="bi bi-arrow-clockwise"></i>
|
<i class="bi bi-arrow-clockwise"></i>
|
||||||
<span id="refresh-spinner" class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
if !props.IsConfigured {
|
if !props.IsConfigured {
|
||||||
|
@ -62,12 +61,9 @@ templ Index(props IndexProps) {
|
||||||
@ConfigStatus(props.Config)
|
@ConfigStatus(props.Config)
|
||||||
@DNSRecordsSection(props.Records, props.CurrentIP)
|
@DNSRecordsSection(props.Records, props.CurrentIP)
|
||||||
}
|
}
|
||||||
@ConfigModal(props.Config, props.UpdateFreqs)
|
|
||||||
@RecordModal(props.Config.Domain)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-container"></div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,16 +82,34 @@ templ ConfigWarning() {
|
||||||
}
|
}
|
||||||
|
|
||||||
templ ConfigStatus(config ConfigData) {
|
templ ConfigStatus(config ConfigData) {
|
||||||
<div class="card mb-4">
|
<div class="card mb-4" id="config-status">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div
|
||||||
<h5 class="mb-0">Configuration</h5>
|
class="card-header d-flex justify-content-between align-items-center"
|
||||||
<button
|
x-init
|
||||||
class="btn btn-sm btn-outline-primary"
|
x-data="{ editLoading: false, deleteLoading: false }"
|
||||||
data-bs-toggle="modal"
|
@ajax:success="$dispatch('dialog:open')"
|
||||||
data-bs-target="#configModal"
|
|
||||||
>
|
>
|
||||||
|
<h5 class="mb-0">Configuration</h5>
|
||||||
|
<a
|
||||||
|
href="/config"
|
||||||
|
@ajax:before="editLoading = true"
|
||||||
|
@ajax:success="$dispatch('dialog:open')"
|
||||||
|
@ajax:after="editLoading = false"
|
||||||
|
@ajax:error="editLoading = false"
|
||||||
|
x-target="contact"
|
||||||
|
class="btn btn-sm btn-outline-primary me-2"
|
||||||
|
:disabled="editLoading || deleteLoading"
|
||||||
|
>
|
||||||
|
<template x-if="!editLoading">
|
||||||
|
<span class="ms-1">
|
||||||
Edit
|
Edit
|
||||||
</button>
|
<i class="bi bi-pencil"></i>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="editLoading">
|
||||||
|
<span class="spinner-border spinner-border-sm"></span>
|
||||||
|
</template>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -118,25 +132,57 @@ templ DNSRecordsSection(records []DNSRecord, currentIP string) {
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h5 class="mb-0">DNS Records</h5>
|
<h5 class="mb-0">DNS Records</h5>
|
||||||
<div>
|
<div x-data="{ updating: false, addingRecord: false }">
|
||||||
|
<form
|
||||||
|
method="post"
|
||||||
|
action="/update-all-records"
|
||||||
|
x-target="dns-records-table"
|
||||||
|
@ajax:before="confirm('Are you sure you want to update all A records to your current IP?') || $event.preventDefault(); updating = true"
|
||||||
|
@ajax:after="updating = false"
|
||||||
|
@ajax:error="updating = false"
|
||||||
|
style="display: inline;"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-success me-2"
|
class="btn btn-sm btn-success me-2"
|
||||||
hx-post="/update-all-records"
|
type="submit"
|
||||||
hx-target="#dns-records-table"
|
:disabled="updating || addingRecord"
|
||||||
hx-confirm="Are you sure you want to update all A records to your current IP?"
|
|
||||||
hx-indicator="#update-all-spinner"
|
|
||||||
>
|
>
|
||||||
<i class="bi bi-arrow-repeat"></i> Update All to Current IP
|
<template x-if="!updating">
|
||||||
<span id="update-all-spinner" class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
<span class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-arrow-repeat me-1"></i>
|
||||||
|
Update All to Current IP
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="updating">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||||
|
Updating All Records...
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</button>
|
</button>
|
||||||
<button
|
</form>
|
||||||
class="btn btn-sm btn-primary"
|
<a
|
||||||
data-bs-toggle="modal"
|
href="/records/new"
|
||||||
data-bs-target="#recordModal"
|
x-target="contact"
|
||||||
onclick="resetRecordForm()"
|
@ajax:before="addingRecord = true; $dispatch('dialog:open')"
|
||||||
|
@ajax:after="addingRecord = false"
|
||||||
|
@ajax:error="addingRecord = false"
|
||||||
|
class="btn btn-primary"
|
||||||
|
:disabled="updating || addingRecord"
|
||||||
>
|
>
|
||||||
<i class="bi bi-plus-lg"></i> Add Record
|
<template x-if="!addingRecord">
|
||||||
</button>
|
<span class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>
|
||||||
|
Add Record
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="addingRecord">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
|
@ -174,7 +220,7 @@ templ DNSRecordsTable(records []DNSRecord, currentIP string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
templ DNSRecordRow(record DNSRecord, currentIP string) {
|
templ DNSRecordRow(record DNSRecord, currentIP string) {
|
||||||
<tr>
|
<tr x-data="{ editLoading: false, deleteLoading: false }">
|
||||||
<td>{ record.Type }</td>
|
<td>{ record.Type }</td>
|
||||||
<td>{ record.Name }</td>
|
<td>{ record.Name }</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -200,23 +246,46 @@ templ DNSRecordRow(record DNSRecord, currentIP string) {
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<a
|
||||||
class="btn btn-sm btn-outline-primary me-1"
|
href={ templ.URL(fmt.Sprintf("/edit-record/%s", record.ID)) }
|
||||||
hx-get={ fmt.Sprintf("/edit-record/%s", record.ID) }
|
@ajax:before="editLoading = true"
|
||||||
hx-target="#record-modal-content"
|
@ajax:success="$dispatch('dialog:open')"
|
||||||
hx-on::after-request="if(event.detail.successful) bootstrap.Modal.getOrCreateInstance(document.getElementById('recordModal')).show()"
|
@ajax:after="editLoading = false"
|
||||||
|
@ajax:error="editLoading = false"
|
||||||
|
x-target="contact"
|
||||||
|
class="btn btn-sm btn-outline-primary me-2"
|
||||||
|
:disabled="editLoading || deleteLoading"
|
||||||
>
|
>
|
||||||
|
<template x-if="!editLoading">
|
||||||
<i class="bi bi-pencil"></i>
|
<i class="bi bi-pencil"></i>
|
||||||
</button>
|
</template>
|
||||||
|
<template x-if="editLoading">
|
||||||
|
<span class="spinner-border spinner-border-sm"></span>
|
||||||
|
</template>
|
||||||
|
</a>
|
||||||
|
<form
|
||||||
|
method="delete"
|
||||||
|
action={ templ.URL(fmt.Sprintf("/records/%s", record.ID)) }
|
||||||
|
x-target="closest tr"
|
||||||
|
@ajax:before={ fmt.Sprintf(`confirm('Are you sure you want to delete the record for "%s"?') || $event.preventDefault(); deleteLoading = true`, record.Name) }
|
||||||
|
@ajax:after="deleteLoading = false"
|
||||||
|
@ajax:error="deleteLoading = false"
|
||||||
|
@ajax:success="$el.closest('tr').remove()"
|
||||||
|
style="display: inline;"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-outline-danger"
|
class="btn btn-sm btn-outline-danger"
|
||||||
hx-delete={ fmt.Sprintf("/records/%s", record.ID) }
|
type="submit"
|
||||||
hx-target="closest tr"
|
:disabled="editLoading || deleteLoading"
|
||||||
hx-swap="outerHTML"
|
|
||||||
hx-confirm={ fmt.Sprintf("Are you sure you want to delete the record for \"%s\"?", record.Name) }
|
|
||||||
>
|
>
|
||||||
|
<template x-if="!deleteLoading">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
|
</template>
|
||||||
|
<template x-if="deleteLoading">
|
||||||
|
<span class="spinner-border spinner-border-sm"></span>
|
||||||
|
</template>
|
||||||
</button>
|
</button>
|
||||||
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,20 +88,20 @@ func Index(props IndexProps) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</h1><div class=\"current-ip d-flex align-items-center\"><span class=\"me-2\">Current IP:</span> <span id=\"current-ip\" class=\"fw-bold\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</h1><div class=\"current-ip d-flex align-items-center\"><span class=\"me-2\">Current IP:</span> <span id=\"current-ip\" x-init class=\"fw-bold\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(props.CurrentIP)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(props.CurrentIP)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 46, Col: 62}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 46, Col: 69}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span> <button class=\"btn btn-sm btn-outline-secondary ms-2\" title=\"Refresh current IP\" hx-get=\"/refresh-ip\" hx-target=\"#current-ip\" hx-indicator=\"#refresh-spinner\"><i class=\"bi bi-arrow-clockwise\"></i> <span id=\"refresh-spinner\" class=\"htmx-indicator spinner-border spinner-border-sm ms-1\"></span></button></div></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span> <a href=\"/refresh-ip\" x-target=\"current-ip\" class=\"btn btn-sm btn-outline-secondary ms-2 d-inline-flex align-items-center\" @ajax:before=\"$el.querySelector('.bi-arrow-clockwise').classList.add('spin')\" @ajax:after=\"$el.querySelector('.bi-arrow-clockwise').classList.remove('spin')\"><i class=\"bi bi-arrow-clockwise\"></i></a></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -124,15 +124,7 @@ func Index(props IndexProps) templ.Component {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = ConfigModal(props.Config, props.UpdateFreqs).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = RecordModal(props.Config.Domain).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div></div></div><div class=\"toast-container\"></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -196,14 +188,14 @@ func ConfigStatus(config ConfigData) templ.Component {
|
||||||
templ_7745c5c3_Var6 = templ.NopComponent
|
templ_7745c5c3_Var6 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"card mb-4\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\">Configuration</h5><button class=\"btn btn-sm btn-outline-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#configModal\">Edit</button></div><div class=\"card-body\"><div class=\"row\"><div class=\"col-md-4\"><strong>Domain:</strong> <span>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"card mb-4\" id=\"config-status\"><div class=\"card-header d-flex justify-content-between align-items-center\" x-init x-data=\"{ editLoading: false, deleteLoading: false }\" @ajax:success=\"$dispatch('dialog:open')\"><h5 class=\"mb-0\">Configuration</h5><a href=\"/config\" @ajax:before=\"editLoading = true\" @ajax:success=\"$dispatch('dialog:open')\" @ajax:after=\"editLoading = false\" @ajax:error=\"editLoading = false\" x-target=\"contact\" class=\"btn btn-sm btn-outline-primary me-2\" :disabled=\"editLoading || deleteLoading\"><template x-if=\"!editLoading\"><span class=\"ms-1\">Edit <i class=\"bi bi-pencil\"></i></span></template><template x-if=\"editLoading\"><span class=\"spinner-border spinner-border-sm\"></span></template></a></div><div class=\"card-body\"><div class=\"row\"><div class=\"col-md-4\"><strong>Domain:</strong> <span>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(config.Domain)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(config.Domain)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 103, Col: 51}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 117, Col: 51}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -216,7 +208,7 @@ func ConfigStatus(config ConfigData) templ.Component {
|
||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(config.ZoneID)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(config.ZoneID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 106, Col: 52}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 120, Col: 52}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -229,7 +221,7 @@ func ConfigStatus(config ConfigData) templ.Component {
|
||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(formatUpdateSchedule(config.UpdatePeriod))
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(formatUpdateSchedule(config.UpdatePeriod))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 110, Col: 54}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 124, Col: 54}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -264,7 +256,7 @@ func DNSRecordsSection(records []DNSRecord, currentIP string) templ.Component {
|
||||||
templ_7745c5c3_Var10 = templ.NopComponent
|
templ_7745c5c3_Var10 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"card\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\">DNS Records</h5><div><button class=\"btn btn-sm btn-success me-2\" hx-post=\"/update-all-records\" hx-target=\"#dns-records-table\" hx-confirm=\"Are you sure you want to update all A records to your current IP?\" hx-indicator=\"#update-all-spinner\"><i class=\"bi bi-arrow-repeat\"></i> Update All to Current IP <span id=\"update-all-spinner\" class=\"htmx-indicator spinner-border spinner-border-sm ms-1\"></span></button> <button class=\"btn btn-sm btn-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#recordModal\" onclick=\"resetRecordForm()\"><i class=\"bi bi-plus-lg\"></i> Add Record</button></div></div><div class=\"card-body p-0\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"card\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\">DNS Records</h5><div x-data=\"{ updating: false, addingRecord: false }\"><form method=\"post\" action=\"/update-all-records\" x-target=\"dns-records-table\" @ajax:before=\"confirm('Are you sure you want to update all A records to your current IP?') || $event.preventDefault(); updating = true\" @ajax:after=\"updating = false\" @ajax:error=\"updating = false\" style=\"display: inline;\"><button class=\"btn btn-sm btn-success me-2\" type=\"submit\" :disabled=\"updating || addingRecord\"><template x-if=\"!updating\"><span class=\"d-flex align-items-center\"><i class=\"bi bi-arrow-repeat me-1\"></i> Update All to Current IP</span></template><template x-if=\"updating\"><span class=\"d-flex align-items-center\"><span class=\"spinner-border spinner-border-sm me-2\"></span> Updating All Records...</span></template></button></form><a href=\"/records/new\" x-target=\"contact\" @ajax:before=\"addingRecord = true; $dispatch('dialog:open')\" @ajax:after=\"addingRecord = false\" @ajax:error=\"addingRecord = false\" class=\"btn btn-primary\" :disabled=\"updating || addingRecord\"><template x-if=\"!addingRecord\"><span class=\"d-flex align-items-center\"><i class=\"bi bi-plus-lg me-1\"></i> Add Record</span></template><template x-if=\"addingRecord\"><span class=\"d-flex align-items-center\"><span class=\"spinner-border spinner-border-sm me-2\"></span> Loading...</span></template></a></div></div><div class=\"card-body p-0\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -347,14 +339,14 @@ func DNSRecordRow(record DNSRecord, currentIP string) templ.Component {
|
||||||
templ_7745c5c3_Var12 = templ.NopComponent
|
templ_7745c5c3_Var12 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<tr><td>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<tr x-data=\"{ editLoading: false, deleteLoading: false }\"><td>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var13 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(record.Type)
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(record.Type)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 178, Col: 19}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 224, Col: 19}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -367,7 +359,7 @@ func DNSRecordRow(record DNSRecord, currentIP string) templ.Component {
|
||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(record.Name)
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(record.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 179, Col: 19}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 225, Col: 19}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -380,7 +372,7 @@ func DNSRecordRow(record DNSRecord, currentIP string) templ.Component {
|
||||||
var templ_7745c5c3_Var15 string
|
var templ_7745c5c3_Var15 string
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(record.Content)
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(record.Content)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 181, Col: 19}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 227, Col: 19}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -416,7 +408,7 @@ func DNSRecordRow(record DNSRecord, currentIP string) templ.Component {
|
||||||
var templ_7745c5c3_Var16 string
|
var templ_7745c5c3_Var16 string
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%ds", record.TTL))
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%ds", record.TTL))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 194, Col: 36}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 240, Col: 36}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -433,46 +425,46 @@ func DNSRecordRow(record DNSRecord, currentIP string) templ.Component {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</td><td><button class=\"btn btn-sm btn-outline-primary me-1\" hx-get=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</td><td><a href=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var17 string
|
var templ_7745c5c3_Var17 templ.SafeURL
|
||||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/edit-record/%s", record.ID))
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(templ.URL(fmt.Sprintf("/edit-record/%s", record.ID)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 205, Col: 54}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 250, Col: 63}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" hx-target=\"#record-modal-content\" hx-on::after-request=\"if(event.detail.successful) bootstrap.Modal.getOrCreateInstance(document.getElementById('recordModal')).show()\"><i class=\"bi bi-pencil\"></i></button> <button class=\"btn btn-sm btn-outline-danger\" hx-delete=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" @ajax:before=\"editLoading = true\" @ajax:success=\"$dispatch('dialog:open')\" @ajax:after=\"editLoading = false\" @ajax:error=\"editLoading = false\" x-target=\"contact\" class=\"btn btn-sm btn-outline-primary me-2\" :disabled=\"editLoading || deleteLoading\"><template x-if=\"!editLoading\"><i class=\"bi bi-pencil\"></i></template><template x-if=\"editLoading\"><span class=\"spinner-border spinner-border-sm\"></span></template></a><form method=\"delete\" action=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var18 string
|
var templ_7745c5c3_Var18 templ.SafeURL
|
||||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/records/%s", record.ID))
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.URL(fmt.Sprintf("/records/%s", record.ID)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 213, Col: 53}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 268, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" hx-target=\"closest tr\" hx-swap=\"outerHTML\" hx-confirm=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" x-target=\"closest tr\" @ajax:before=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var19 string
|
var templ_7745c5c3_Var19 string
|
||||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("Are you sure you want to delete the record for \"%s\"?", record.Name))
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`confirm('Are you sure you want to delete the record for "%s"?') || $event.preventDefault(); deleteLoading = true`, record.Name))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 216, Col: 99}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 270, Col: 159}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\"><i class=\"bi bi-trash\"></i></button></td></tr>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" @ajax:after=\"deleteLoading = false\" @ajax:error=\"deleteLoading = false\" @ajax:success=\"$el.closest('tr').remove()\" style=\"display: inline;\"><button class=\"btn btn-sm btn-outline-danger\" type=\"submit\" :disabled=\"editLoading || deleteLoading\"><template x-if=\"!deleteLoading\"><i class=\"bi bi-trash\"></i></template><template x-if=\"deleteLoading\"><span class=\"spinner-border spinner-border-sm\"></span></template></button></form></td></tr>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,16 @@ func Render(w io.Writer, component templ.Component) error {
|
||||||
return component.Render(context.Background(), w)
|
return component.Render(context.Background(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenderMultiple renders multiple components to an io.Writer in sequence
|
||||||
|
func RenderMultiple(w io.Writer, components ...templ.Component) error {
|
||||||
|
for _, component := range components {
|
||||||
|
if err := component.Render(context.Background(), w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Layout is the base layout for all pages
|
// Layout is the base layout for all pages
|
||||||
templ Layout(title string) {
|
templ Layout(title string) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
@ -16,6 +26,8 @@ templ Layout(title string) {
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>{ title }</title>
|
<title>{ title }</title>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.12.2/dist/cdn.min.js"></script>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>
|
||||||
<link
|
<link
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
@ -24,8 +36,9 @@ templ Layout(title string) {
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
|
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
|
||||||
/>
|
/>
|
||||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
/* Base Styles */
|
||||||
body {
|
body {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
|
@ -47,62 +60,185 @@ templ Layout(title string) {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
.htmx-indicator {
|
.spin {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Styles */
|
||||||
|
dialog {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
max-width: none;
|
||||||
|
max-height: none;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog[open] {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog::backdrop {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
animation: fadeIn 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 1rem;
|
||||||
|
animation: modalShow 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: scale(1);
|
||||||
|
animation: modalSlideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 2rem;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced form styling within modals */
|
||||||
|
.modal-body .form-label {
|
||||||
|
color: #495057;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control,
|
||||||
|
.modal-body .form-select {
|
||||||
|
border: 2px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control:focus,
|
||||||
|
.modal-body .form-select:focus {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .input-group-text {
|
||||||
|
border: 2px solid #e9ecef;
|
||||||
|
border-left: none;
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-check {
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-text {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalShow {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideIn {
|
||||||
|
from {
|
||||||
|
transform: scale(0.95) translateY(-20px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 300ms ease-in;
|
|
||||||
}
|
}
|
||||||
.htmx-request .htmx-indicator {
|
to {
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.htmx-request.htmx-indicator {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modal-container {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
max-height: 95vh;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header,
|
||||||
|
.modal-body,
|
||||||
|
.modal-footer {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
max-height: 70vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body id="body">
|
||||||
{ children... }
|
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<ul x-sync id="notification_list" x-merge="prepend" role="status" class="list-unstyled"></ul>
|
||||||
<script>
|
|
||||||
// Simple toast function for HTMX responses
|
|
||||||
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
|
||||||
const xhr = evt.detail.xhr;
|
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
|
||||||
const successMessage = xhr.getResponseHeader('HX-Success-Message');
|
|
||||||
if (successMessage) {
|
|
||||||
showToast(successMessage, 'success');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const errorMessage = xhr.getResponseHeader('HX-Error-Message') || 'An error occurred';
|
|
||||||
showToast(errorMessage, 'danger');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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 = `
|
|
||||||
<div class="d-flex">
|
|
||||||
<div class="toast-body">${message}</div>
|
|
||||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
{ children... }
|
||||||
|
<dialog id="global-dialog" x-init @dialog:open.window="$el.showModal()">
|
||||||
toastContainer.appendChild(toast);
|
<div id="contact"></div>
|
||||||
const bsToast = new bootstrap.Toast(toast);
|
</dialog>
|
||||||
bsToast.show();
|
|
||||||
|
|
||||||
toast.addEventListener("hidden.bs.toast", () => {
|
|
||||||
toast.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,68 +3,87 @@ package templates
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
templ ConfigModal(config ConfigData, frequencies []UpdateFrequency) {
|
templ ConfigModal(config ConfigData, frequencies []UpdateFrequency) {
|
||||||
<div
|
<div id="contact" class="modal-container">
|
||||||
class="modal fade"
|
|
||||||
id="configModal"
|
|
||||||
tabindex="-1"
|
|
||||||
aria-labelledby="configModalLabel"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="configModalLabel">Configuration</h5>
|
<h5 class="modal-title">Configuration Settings</h5>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-close"
|
class="btn-close"
|
||||||
data-bs-dismiss="modal"
|
@click="$el.closest('dialog').close()"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
<form hx-post="/config" hx-target="body" hx-swap="outerHTML">
|
<form
|
||||||
|
x-target="config-status"
|
||||||
|
method="post"
|
||||||
|
action="/config"
|
||||||
|
@ajax:success="$el.closest('dialog').close()"
|
||||||
|
x-data="{ saving: false }"
|
||||||
|
@ajax:before="saving = true"
|
||||||
|
@ajax:after="saving = false"
|
||||||
|
@ajax:error="saving = false"
|
||||||
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="api-token" class="form-label">Cloudflare API Token</label>
|
<label for="api-token" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-key-fill text-primary me-2"></i>
|
||||||
|
Cloudflare API Token
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="api-token"
|
id="api-token"
|
||||||
name="api_token"
|
name="api_token"
|
||||||
value={ config.ApiToken }
|
value={ config.ApiToken }
|
||||||
|
placeholder="Enter your Cloudflare API token"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
|
<i class="bi bi-info-circle me-1"></i>
|
||||||
Create a token with <code>Zone.DNS:Edit</code> permissions in
|
Create a token with <code>Zone.DNS:Edit</code> permissions in
|
||||||
the Cloudflare dashboard.
|
the Cloudflare dashboard.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="zone-id-input" class="form-label">Zone ID</label>
|
<label for="zone-id-input" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-globe text-info me-2"></i>
|
||||||
|
Zone ID
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="zone-id-input"
|
id="zone-id-input"
|
||||||
name="zone_id"
|
name="zone_id"
|
||||||
value={ config.ZoneID }
|
value={ config.ZoneID }
|
||||||
|
placeholder="e.g., 1234567890abcdef1234567890abcdef"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
|
<i class="bi bi-info-circle me-1"></i>
|
||||||
Found in the Cloudflare dashboard under your domain's overview page.
|
Found in the Cloudflare dashboard under your domain's overview page.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="domain-input" class="form-label">Domain</label>
|
<label for="domain-input" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-link-45deg text-success me-2"></i>
|
||||||
|
Domain
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="domain-input"
|
id="domain-input"
|
||||||
name="domain"
|
name="domain"
|
||||||
value={ config.Domain }
|
value={ config.Domain }
|
||||||
|
placeholder="e.g., example.com"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="update-period" class="form-label">Update Frequency</label>
|
<label for="update-period" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-clock text-warning me-2"></i>
|
||||||
|
Update Frequency
|
||||||
|
</label>
|
||||||
<select class="form-select" id="update-period" name="update_period">
|
<select class="form-select" id="update-period" name="update_period">
|
||||||
for _, freq := range frequencies {
|
for _, freq := range frequencies {
|
||||||
<option value={ freq.Value } selected?={ freq.Value == config.UpdatePeriod }>
|
<option value={ freq.Value } selected?={ freq.Value == config.UpdatePeriod }>
|
||||||
|
@ -78,59 +97,77 @@ templ ConfigModal(config ConfigData, frequencies []UpdateFrequency) {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
data-bs-dismiss="modal"
|
@click="$el.closest('dialog').close()"
|
||||||
>
|
>
|
||||||
|
<i class="bi bi-x-lg me-1"></i>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary">
|
<button
|
||||||
Save
|
type="submit"
|
||||||
<span class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
class="btn btn-primary"
|
||||||
|
:disabled="saving"
|
||||||
|
>
|
||||||
|
<template x-if="!saving">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-check-lg me-2"></i>
|
||||||
|
Save Configuration
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="saving">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||||
|
Saving...
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ RecordModal(domain string) {
|
|
||||||
<div
|
|
||||||
class="modal fade"
|
|
||||||
id="recordModal"
|
|
||||||
tabindex="-1"
|
|
||||||
aria-labelledby="recordModalLabel"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div id="record-modal-content" class="modal-content">
|
|
||||||
@RecordForm("Add DNS Record", "", domain, DNSRecord{Type: "A", TTL: 1})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||||
|
<div id="contact" class="modal-container">
|
||||||
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">{ title }</h5>
|
<h5 class="modal-title">
|
||||||
|
<i class="bi bi-dns text-primary me-2"></i>
|
||||||
|
{ title }
|
||||||
|
</h5>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-close"
|
class="btn-close"
|
||||||
data-bs-dismiss="modal"
|
@click="$el.closest('dialog').close()"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="error-message" class="alert alert-danger d-none mx-3 mt-3" role="alert">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||||
|
<span class="error-text"></span>
|
||||||
|
</div>
|
||||||
<form
|
<form
|
||||||
if recordID != "" {
|
if recordID != "" {
|
||||||
hx-put={ fmt.Sprintf("/records/%s", recordID) }
|
method="put"
|
||||||
|
action={ templ.URL(fmt.Sprintf("/records/%s", recordID)) }
|
||||||
} else {
|
} else {
|
||||||
hx-post="/records"
|
method="post"
|
||||||
|
action="/records"
|
||||||
}
|
}
|
||||||
hx-target="#dns-records-table"
|
x-target="dns-records-table"
|
||||||
hx-on::after-request="if(event.detail.successful) bootstrap.Modal.getInstance(document.getElementById('recordModal')).hide()"
|
x-target.error="none"
|
||||||
|
@ajax:success="$el.closest('dialog').close()"
|
||||||
|
x-data="{ saving: false }"
|
||||||
|
@ajax:before="saving = true"
|
||||||
|
@ajax:after="saving = false"
|
||||||
|
@ajax:error="saving = false"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="mb-3">
|
<div class="row">
|
||||||
<label for="record-name" class="form-label">Name</label>
|
<div class="col-md-8 mb-3">
|
||||||
|
<label for="record-name" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-tag text-info me-2"></i>
|
||||||
|
Record Name
|
||||||
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -141,37 +178,48 @@ templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||||
value={ getRecordName(record.Name, domain) }
|
value={ getRecordName(record.Name, domain) }
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<span class="input-group-text">.{ domain }</span>
|
<span class="input-group-text bg-light text-muted">.{ domain }</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-text">Use @ for the root domain</div>
|
<div class="form-text">
|
||||||
|
<i class="bi bi-info-circle me-1"></i>
|
||||||
|
Use <code>{ "@" }</code> { "for" } the root domain
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
</div>
|
||||||
<label for="record-type" class="form-label">Type</label>
|
<div class="col-md-4 mb-3">
|
||||||
|
<label for="record-type" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-diagram-3 text-success me-2"></i>
|
||||||
|
Type
|
||||||
|
</label>
|
||||||
<select
|
<select
|
||||||
class="form-select"
|
class="form-select"
|
||||||
id="record-type"
|
id="record-type"
|
||||||
name="type"
|
name="type"
|
||||||
onchange="toggleMyIPOption()"
|
onchange="toggleMyIPOption()"
|
||||||
>
|
>
|
||||||
<option value="A" selected?={ record.Type == "A" }>A</option>
|
<option value="A" selected?={ record.Type == "A" }>A (IPv4)</option>
|
||||||
<option value="AAAA" selected?={ record.Type == "AAAA" }>AAAA</option>
|
<option value="AAAA" selected?={ record.Type == "AAAA" }>AAAA (IPv6)</option>
|
||||||
<option value="CNAME" selected?={ record.Type == "CNAME" }>CNAME</option>
|
<option value="CNAME" selected?={ record.Type == "CNAME" }>CNAME</option>
|
||||||
<option value="TXT" selected?={ record.Type == "TXT" }>TXT</option>
|
<option value="TXT" selected?={ record.Type == "TXT" }>TXT</option>
|
||||||
<option value="MX" selected?={ record.Type == "MX" }>MX</option>
|
<option value="MX" selected?={ record.Type == "MX" }>MX</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-3" id="content-group">
|
<div class="mb-3" id="content-group">
|
||||||
<label for="record-content" class="form-label">Content</label>
|
<label for="record-content" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-file-text text-warning me-2"></i>
|
||||||
|
Content
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="record-content"
|
id="record-content"
|
||||||
name="content"
|
name="content"
|
||||||
value={ record.Content }
|
value={ record.Content }
|
||||||
|
placeholder="Enter the record value"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 form-check" id="use-my-ip-group" style="display: none;">
|
<div class="mb-3 form-check bg-light p-3 rounded" id="use-my-ip-group" style="display: none;">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
|
@ -179,10 +227,20 @@ templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||||
name="use_my_ip"
|
name="use_my_ip"
|
||||||
onchange="toggleContentField()"
|
onchange="toggleContentField()"
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="use-my-ip">Use my current IP address</label>
|
<label class="form-check-label fw-semibold" for="use-my-ip">
|
||||||
|
<i class="bi bi-geo-alt text-primary me-2"></i>
|
||||||
|
Use my current IP address
|
||||||
|
</label>
|
||||||
|
<div class="form-text">
|
||||||
|
Automatically use your current public IP address
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
</div>
|
||||||
<label for="record-ttl" class="form-label">TTL</label>
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="record-ttl" class="form-label fw-semibold">
|
||||||
|
<i class="bi bi-clock-history text-secondary me-2"></i>
|
||||||
|
TTL (Time to Live)
|
||||||
|
</label>
|
||||||
<select class="form-select" id="record-ttl" name="ttl">
|
<select class="form-select" id="record-ttl" name="ttl">
|
||||||
<option value="1" selected?={ record.TTL == 1 }>Auto</option>
|
<option value="1" selected?={ record.TTL == 1 }>Auto</option>
|
||||||
<option value="120" selected?={ record.TTL == 120 }>2 minutes</option>
|
<option value="120" selected?={ record.TTL == 120 }>2 minutes</option>
|
||||||
|
@ -196,7 +254,8 @@ templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||||
<option value="86400" selected?={ record.TTL == 86400 }>1 day</option>
|
<option value="86400" selected?={ record.TTL == 86400 }>1 day</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 form-check">
|
<div class="col-md-6 mb-3 d-flex align-items-end">
|
||||||
|
<div class="form-check bg-light p-3 rounded w-100">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
|
@ -204,52 +263,54 @@ templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||||
name="proxied"
|
name="proxied"
|
||||||
checked?={ record.Proxied }
|
checked?={ record.Proxied }
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="record-proxied">Proxied through Cloudflare</label>
|
<label class="form-check-label fw-semibold" for="record-proxied">
|
||||||
|
<i class="bi bi-shield-check text-success me-2"></i>
|
||||||
|
Proxied through Cloudflare
|
||||||
|
</label>
|
||||||
|
<div class="form-text">
|
||||||
|
Enable Cloudflare's proxy and security features
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
data-bs-dismiss="modal"
|
@click="$el.closest('dialog').close()"
|
||||||
>
|
>
|
||||||
|
<i class="bi bi-x-lg me-1"></i>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary">
|
<button
|
||||||
Save
|
type="submit"
|
||||||
<span class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
class="btn btn-primary"
|
||||||
|
:disabled="saving"
|
||||||
|
>
|
||||||
|
<template x-if="!saving">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-check-lg me-2"></i>
|
||||||
|
if recordID != "" {
|
||||||
|
Update Record
|
||||||
|
} else {
|
||||||
|
Create Record
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="saving">
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||||
|
if recordID != "" {
|
||||||
|
Updating...
|
||||||
|
} else {
|
||||||
|
Creating...
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
</div>
|
||||||
function toggleMyIPOption() {
|
</div>
|
||||||
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';
|
|
||||||
} else {
|
|
||||||
useMyIPGroup.style.display = 'none';
|
|
||||||
contentGroup.style.display = 'block';
|
|
||||||
document.getElementById('use-my-ip').checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleContentField() {
|
|
||||||
const useMyIP = document.getElementById('use-my-ip').checked;
|
|
||||||
const contentGroup = document.getElementById('content-group');
|
|
||||||
contentGroup.style.display = useMyIP ? 'none' : 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetRecordForm() {
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById('record-type').value = 'A';
|
|
||||||
toggleMyIPOption();
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the form state
|
|
||||||
toggleMyIPOption();
|
|
||||||
</script>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,46 +31,46 @@ func ConfigModal(config ConfigData, frequencies []UpdateFrequency) templ.Compone
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"modal fade\" id=\"configModal\" tabindex=\"-1\" aria-labelledby=\"configModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"configModalLabel\">Configuration</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><form hx-post=\"/config\" hx-target=\"body\" hx-swap=\"outerHTML\"><div class=\"modal-body\"><div class=\"mb-3\"><label for=\"api-token\" class=\"form-label\">Cloudflare API Token</label> <input type=\"password\" class=\"form-control\" id=\"api-token\" name=\"api_token\" value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"contact\" class=\"modal-container\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\">Configuration Settings</h5><button type=\"button\" class=\"btn-close\" @click=\"$el.closest('dialog').close()\" aria-label=\"Close\"></button></div><form x-target=\"config-status\" method=\"post\" action=\"/config\" @ajax:success=\"$el.closest('dialog').close()\" x-data=\"{ saving: false }\" @ajax:before=\"saving = true\" @ajax:after=\"saving = false\" @ajax:error=\"saving = false\"><div class=\"modal-body\"><div class=\"mb-3\"><label for=\"api-token\" class=\"form-label fw-semibold\"><i class=\"bi bi-key-fill text-primary me-2\"></i> Cloudflare API Token</label> <input type=\"password\" class=\"form-control\" id=\"api-token\" name=\"api_token\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(config.ApiToken)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(config.ApiToken)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 33, Col: 31}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 38, Col: 30}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" required><div class=\"form-text\">Create a token with <code>Zone.DNS:Edit</code> permissions in the Cloudflare dashboard.</div></div><div class=\"mb-3\"><label for=\"zone-id-input\" class=\"form-label\">Zone ID</label> <input type=\"text\" class=\"form-control\" id=\"zone-id-input\" name=\"zone_id\" value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" placeholder=\"Enter your Cloudflare API token\" required><div class=\"form-text\"><i class=\"bi bi-info-circle me-1\"></i> Create a token with <code>Zone.DNS:Edit</code> permissions in the Cloudflare dashboard.</div></div><div class=\"mb-3\"><label for=\"zone-id-input\" class=\"form-label fw-semibold\"><i class=\"bi bi-globe text-info me-2\"></i> Zone ID</label> <input type=\"text\" class=\"form-control\" id=\"zone-id-input\" name=\"zone_id\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(config.ZoneID)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(config.ZoneID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 48, Col: 29}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 58, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" required><div class=\"form-text\">Found in the Cloudflare dashboard under your domain's overview page.</div></div><div class=\"mb-3\"><label for=\"domain-input\" class=\"form-label\">Domain</label> <input type=\"text\" class=\"form-control\" id=\"domain-input\" name=\"domain\" value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" placeholder=\"e.g., 1234567890abcdef1234567890abcdef\" required><div class=\"form-text\"><i class=\"bi bi-info-circle me-1\"></i> Found in the Cloudflare dashboard under your domain's overview page.</div></div><div class=\"mb-3\"><label for=\"domain-input\" class=\"form-label fw-semibold\"><i class=\"bi bi-link-45deg text-success me-2\"></i> Domain</label> <input type=\"text\" class=\"form-control\" id=\"domain-input\" name=\"domain\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(config.Domain)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(config.Domain)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 62, Col: 29}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 77, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" required></div><div class=\"mb-3\"><label for=\"update-period\" class=\"form-label\">Update Frequency</label> <select class=\"form-select\" id=\"update-period\" name=\"update_period\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" placeholder=\"e.g., example.com\" required></div><div class=\"mb-3\"><label for=\"update-period\" class=\"form-label fw-semibold\"><i class=\"bi bi-clock text-warning me-2\"></i> Update Frequency</label> <select class=\"form-select\" id=\"update-period\" name=\"update_period\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ func ConfigModal(config ConfigData, frequencies []UpdateFrequency) templ.Compone
|
||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(freq.Value)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(freq.Value)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 70, Col: 35}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 89, Col: 34}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -105,7 +105,7 @@ func ConfigModal(config ConfigData, frequencies []UpdateFrequency) templ.Compone
|
||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(freq.Label)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(freq.Label)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 71, Col: 22}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 90, Col: 21}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -116,44 +116,7 @@ func ConfigModal(config ConfigData, frequencies []UpdateFrequency) templ.Compone
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</select></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Save <span class=\"htmx-indicator spinner-border spinner-border-sm ms-1\"></span></button></div></form></div></div></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</select></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" @click=\"$el.closest('dialog').close()\"><i class=\"bi bi-x-lg me-1\"></i> Cancel</button> <button type=\"submit\" class=\"btn btn-primary\" :disabled=\"saving\"><template x-if=\"!saving\"><span class=\"d-flex align-items-center\"><i class=\"bi bi-check-lg me-2\"></i> Save Configuration</span></template><template x-if=\"saving\"><span class=\"d-flex align-items-center\"><span class=\"spinner-border spinner-border-sm me-2\"></span> Saving...</span></template></button></div></form></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func RecordModal(domain string) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
||||||
return templ_7745c5c3_CtxErr
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
||||||
if !templ_7745c5c3_IsBuffer {
|
|
||||||
defer func() {
|
|
||||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err == nil {
|
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
ctx = templ.InitializeContext(ctx)
|
|
||||||
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var7 == nil {
|
|
||||||
templ_7745c5c3_Var7 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"modal fade\" id=\"recordModal\" tabindex=\"-1\" aria-labelledby=\"recordModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog\"><div id=\"record-modal-content\" class=\"modal-content\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = RecordForm("Add DNS Record", "", domain, DNSRecord{Type: "A", TTL: 1}).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div></div></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -177,79 +140,105 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var8 == nil {
|
if templ_7745c5c3_Var7 == nil {
|
||||||
templ_7745c5c3_Var8 = templ.NopComponent
|
templ_7745c5c3_Var7 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div class=\"modal-header\"><h5 class=\"modal-title\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div id=\"contact\" class=\"modal-container\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\"><i class=\"bi bi-dns text-primary me-2\"></i> ")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 114, Col: 33}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 135, Col: 12}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</h5><button type=\"button\" class=\"btn-close\" @click=\"$el.closest('dialog').close()\" aria-label=\"Close\"></button></div><div id=\"error-message\" class=\"alert alert-danger d-none mx-3 mt-3\" role=\"alert\"><i class=\"bi bi-exclamation-triangle-fill me-2\"></i> <span class=\"error-text\"></span></div><form")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if recordID != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " method=\"put\" action=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.URL(fmt.Sprintf("/records/%s", recordID)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 151, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><form")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if recordID != "" {
|
} else {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " hx-put=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " method=\"post\" action=\"/records\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " x-target=\"dns-records-table\" x-target.error=\"none\" @ajax:success=\"$el.closest('dialog').close()\" x-data=\"{ saving: false }\" @ajax:before=\"saving = true\" @ajax:after=\"saving = false\" @ajax:error=\"saving = false\"><div class=\"modal-body\"><div class=\"row\"><div class=\"col-md-8 mb-3\"><label for=\"record-name\" class=\"form-label fw-semibold\"><i class=\"bi bi-tag text-info me-2\"></i> Record Name</label><div class=\"input-group\"><input type=\"text\" class=\"form-control\" id=\"record-name\" name=\"name\" placeholder=\"subdomain\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/records/%s", recordID))
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(getRecordName(record.Name, domain))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 124, Col: 48}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 178, Col: 51}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" required> <span class=\"input-group-text bg-light text-muted\">.")
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " hx-post=\"/records\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " hx-target=\"#dns-records-table\" hx-on::after-request=\"if(event.detail.successful) bootstrap.Modal.getInstance(document.getElementById('recordModal')).hide()\"><div class=\"modal-body\"><div class=\"mb-3\"><label for=\"record-name\" class=\"form-label\">Name</label><div class=\"input-group\"><input type=\"text\" class=\"form-control\" id=\"record-name\" name=\"name\" placeholder=\"subdomain\" value=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(getRecordName(record.Name, domain))
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(domain)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 141, Col: 48}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 181, Col: 68}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" required> <span class=\"input-group-text\">.")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</span></div><div class=\"form-text\"><i class=\"bi bi-info-circle me-1\"></i> Use <code>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(domain)
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs("@")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 144, Col: 45}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 185, Col: 23}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span></div><div class=\"form-text\">Use @ for the root domain</div></div><div class=\"mb-3\"><label for=\"record-type\" class=\"form-label\">Type</label> <select class=\"form-select\" id=\"record-type\" name=\"type\" onchange=\"toggleMyIPOption()\"><option value=\"A\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</code> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs("for")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 185, Col: 40}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " the root domain</div></div><div class=\"col-md-4 mb-3\"><label for=\"record-type\" class=\"form-label fw-semibold\"><i class=\"bi bi-diagram-3 text-success me-2\"></i> Type</label> <select class=\"form-select\" id=\"record-type\" name=\"type\" onchange=\"toggleMyIPOption()\"><option value=\"A\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -259,7 +248,7 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, ">A</option> <option value=\"AAAA\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, ">A (IPv4)</option> <option value=\"AAAA\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -269,7 +258,7 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, ">AAAA</option> <option value=\"CNAME\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, ">AAAA (IPv6)</option> <option value=\"CNAME\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -299,20 +288,20 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, ">MX</option></select></div><div class=\"mb-3\" id=\"content-group\"><label for=\"record-content\" class=\"form-label\">Content</label> <input type=\"text\" class=\"form-control\" id=\"record-content\" name=\"content\" value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, ">MX</option></select></div></div><div class=\"mb-3\" id=\"content-group\"><label for=\"record-content\" class=\"form-label fw-semibold\"><i class=\"bi bi-file-text text-warning me-2\"></i> Content</label> <input type=\"text\" class=\"form-control\" id=\"record-content\" name=\"content\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(record.Content)
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(record.Content)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 170, Col: 27}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/modal.templ`, Line: 217, Col: 29}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" required></div><div class=\"mb-3 form-check\" id=\"use-my-ip-group\" style=\"display: none;\"><input type=\"checkbox\" class=\"form-check-input\" id=\"use-my-ip\" name=\"use_my_ip\" onchange=\"toggleContentField()\"> <label class=\"form-check-label\" for=\"use-my-ip\">Use my current IP address</label></div><div class=\"mb-3\"><label for=\"record-ttl\" class=\"form-label\">TTL</label> <select class=\"form-select\" id=\"record-ttl\" name=\"ttl\"><option value=\"1\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" placeholder=\"Enter the record value\" required></div><div class=\"mb-3 form-check bg-light p-3 rounded\" id=\"use-my-ip-group\" style=\"display: none;\"><input type=\"checkbox\" class=\"form-check-input\" id=\"use-my-ip\" name=\"use_my_ip\" onchange=\"toggleContentField()\"> <label class=\"form-check-label fw-semibold\" for=\"use-my-ip\"><i class=\"bi bi-geo-alt text-primary me-2\"></i> Use my current IP address</label><div class=\"form-text\">Automatically use your current public IP address</div></div><div class=\"row\"><div class=\"col-md-6 mb-3\"><label for=\"record-ttl\" class=\"form-label fw-semibold\"><i class=\"bi bi-clock-history text-secondary me-2\"></i> TTL (Time to Live)</label> <select class=\"form-select\" id=\"record-ttl\" name=\"ttl\"><option value=\"1\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -412,7 +401,7 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, ">1 day</option></select></div><div class=\"mb-3 form-check\"><input type=\"checkbox\" class=\"form-check-input\" id=\"record-proxied\" name=\"proxied\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, ">1 day</option></select></div><div class=\"col-md-6 mb-3 d-flex align-items-end\"><div class=\"form-check bg-light p-3 rounded w-100\"><input type=\"checkbox\" class=\"form-check-input\" id=\"record-proxied\" name=\"proxied\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -422,7 +411,37 @@ func RecordForm(title, recordID, domain string, record DNSRecord) templ.Componen
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "> <label class=\"form-check-label\" for=\"record-proxied\">Proxied through Cloudflare</label></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Save <span class=\"htmx-indicator spinner-border spinner-border-sm ms-1\"></span></button></div></form><script>\n\t\tfunction toggleMyIPOption() {\n\t\t\tconst recordType = document.getElementById('record-type').value;\n\t\t\tconst useMyIPGroup = document.getElementById('use-my-ip-group');\n\t\t\tconst contentGroup = document.getElementById('content-group');\n\n\t\t\tif (recordType === 'A') {\n\t\t\t\tuseMyIPGroup.style.display = 'block';\n\t\t\t} else {\n\t\t\t\tuseMyIPGroup.style.display = 'none';\n\t\t\t\tcontentGroup.style.display = 'block';\n\t\t\t\tdocument.getElementById('use-my-ip').checked = false;\n\t\t\t}\n\t\t}\n\n\t\tfunction toggleContentField() {\n\t\t\tconst useMyIP = document.getElementById('use-my-ip').checked;\n\t\t\tconst contentGroup = document.getElementById('content-group');\n\t\t\tcontentGroup.style.display = useMyIP ? 'none' : 'block';\n\t\t}\n\n\t\tfunction resetRecordForm() {\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.getElementById('record-type').value = 'A';\n\t\t\t\ttoggleMyIPOption();\n\t\t\t}, 100);\n\t\t}\n\n\t\t// Initialize the form state\n\t\ttoggleMyIPOption();\n\t</script>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "> <label class=\"form-check-label fw-semibold\" for=\"record-proxied\"><i class=\"bi bi-shield-check text-success me-2\"></i> Proxied through Cloudflare</label><div class=\"form-text\">Enable Cloudflare's proxy and security features</div></div></div></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" @click=\"$el.closest('dialog').close()\"><i class=\"bi bi-x-lg me-1\"></i> Cancel</button> <button type=\"submit\" class=\"btn btn-primary\" :disabled=\"saving\"><template x-if=\"!saving\"><span class=\"d-flex align-items-center\"><i class=\"bi bi-check-lg me-2\"></i> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if recordID != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "Update Record")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "Create Record")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</span></template><template x-if=\"saving\"><span class=\"d-flex align-items-center\"><span class=\"spinner-border spinner-border-sm me-2\"></span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if recordID != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "Updating...")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "Creating...")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</span></template></button></div></form></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
73
templates/toast.templ
Normal file
73
templates/toast.templ
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// NotificationList renders the container for notifications
|
||||||
|
templ NotificationList() {
|
||||||
|
<ul x-sync id="notification_list" x-merge="prepend" role="status" class="list-unstyled">
|
||||||
|
{ children... }
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationToast renders a single notification toast
|
||||||
|
templ NotificationToast(message, notificationType string) {
|
||||||
|
<li>
|
||||||
|
<div
|
||||||
|
class={ fmt.Sprintf("toast align-items-center text-white bg-%s show", notificationType) }
|
||||||
|
role="alert"
|
||||||
|
aria-live="assertive"
|
||||||
|
aria-atomic="true"
|
||||||
|
x-data="{
|
||||||
|
show: false,
|
||||||
|
init() {
|
||||||
|
this.$nextTick(() => this.show = true);
|
||||||
|
setTimeout(() => this.dismiss(), 6000);
|
||||||
|
},
|
||||||
|
dismiss() {
|
||||||
|
this.show = false;
|
||||||
|
setTimeout(() => this.$root.remove(), 500);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
x-show="show"
|
||||||
|
x-transition.duration.500ms
|
||||||
|
>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body">{ message }</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-close btn-close-white me-2 m-auto"
|
||||||
|
@click="dismiss()"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for common notification types
|
||||||
|
templ SuccessNotification(message string) {
|
||||||
|
@NotificationList() {
|
||||||
|
@NotificationToast(message, "success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ ErrorNotification(message string) {
|
||||||
|
@NotificationList() {
|
||||||
|
@NotificationToast(message, "danger")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ WarningNotification(message string) {
|
||||||
|
@NotificationList() {
|
||||||
|
@NotificationToast(message, "warning")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ InfoNotification(message string) {
|
||||||
|
@NotificationList() {
|
||||||
|
@NotificationToast(message, "info")
|
||||||
|
}
|
||||||
|
}
|
306
templates/toast_templ.go
Normal file
306
templates/toast_templ.go
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.898
|
||||||
|
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// NotificationList renders the container for notifications
|
||||||
|
func NotificationList() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<ul x-sync id=\"notification_list\" x-merge=\"prepend\" role=\"status\" class=\"list-unstyled\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</ul>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationToast renders a single notification toast
|
||||||
|
func NotificationToast(message, notificationType string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var2 == nil {
|
||||||
|
templ_7745c5c3_Var2 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<li>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 = []any{fmt.Sprintf("toast align-items-center text-white bg-%s show", notificationType)}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/toast.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" role=\"alert\" aria-live=\"assertive\" aria-atomic=\"true\" x-data=\"{\n\t\t\t\tshow: false,\n\t\t\t\tinit() {\n\t\t\t\t\tthis.$nextTick(() => this.show = true);\n\t\t\t\t\tsetTimeout(() => this.dismiss(), 6000);\n\t\t\t\t},\n\t\t\t\tdismiss() {\n\t\t\t\t\tthis.show = false;\n\t\t\t\t\tsetTimeout(() => this.$root.remove(), 500);\n\t\t\t\t}\n\t\t\t}\" x-show=\"show\" x-transition.duration.500ms><div class=\"d-flex\"><div class=\"toast-body\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(message)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/toast.templ`, Line: 36, Col: 37}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><button type=\"button\" class=\"btn-close btn-close-white me-2 m-auto\" @click=\"dismiss()\" aria-label=\"Close\">×</button></div></div></li>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for common notification types
|
||||||
|
func SuccessNotification(message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var6 == nil {
|
||||||
|
templ_7745c5c3_Var6 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = NotificationToast(message, "success").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = NotificationList().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorNotification(message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var8 == nil {
|
||||||
|
templ_7745c5c3_Var8 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = NotificationToast(message, "danger").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = NotificationList().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarningNotification(message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var10 == nil {
|
||||||
|
templ_7745c5c3_Var10 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = NotificationToast(message, "warning").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = NotificationList().Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func InfoNotification(message string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var12 == nil {
|
||||||
|
templ_7745c5c3_Var12 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = NotificationToast(message, "info").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = NotificationList().Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
Loading…
Add table
Add a link
Reference in a new issue