Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions scripts/js/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,16 @@ function testCookies() {
}

function applyCheckboxRadioStyle() {
// Get all radio/checkboxes for theming, with the exception of the two radio buttons on the custom disable timer,
// as well as every element with an id that starts with "status_"
// Get all radio/checkboxes for theming, with the exception of:
// - the two radio buttons on the custom disable timer,
// - radio/checkboxes elements with class "no-icheck",
// - every element with an id that starts with "status_"
const sel = $("input[type='radio'],input[type='checkbox']")
.not("#selSec")
.not("#selMin")
.not("#expert-settings")
.not("#only-changed")
.not(".no-icheck")
.not("[id^=status_]");
sel.parent().removeClass();
sel.parent().addClass("icheck-primary");
Expand Down
311 changes: 311 additions & 0 deletions scripts/js/settings-dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,274 @@ function updateDNSserversTextfield(upstreams, customServers) {
);
}

function getRevServerLines() {
// Return the lines from the textarea (array of lines)
return $(".revServers")
.val()
.split(/\r?\n/v)
.filter(line => line.trim() !== "");
}

// Return an array of objects containing the current values from the textarea
function getRevServerArray() {
const items = [];

const lines = getRevServerLines();
for (const line of lines) {
const cols = line.split(",").map(s => s.trim());
items.push({
enabled: cols[0] ?? "",
network: cols[1] ?? "",
ip: cols[2] ?? "",
domain: cols[3] ?? "",
});
}

return items;
}

function createRevServerTable() {
// The Conditional Forwarding option will be disabled when this option was set via ENV VAR.
// Check if the textarea is disabled.
if ($(".revServers").prop("disabled")) {
// In this case, we don't show the table because the values can't be changed.
// Show the disabled textarea and return
$(".revServers").show();
return;
}

// Get the data
const tableRows = getRevServerArray();

$("#revServers-table").DataTable({
data: tableRows,
autoWidth: false,
columns: [
{ data: null, width: "54px" },
{ data: "network" },
{ data: "ip" },
{ data: "domain" },
{ data: null, width: "52px" },
],
ordering: false,
columnDefs: [
{
targets: 0,
class: "input-checkbox text-center",
render(data, type, row, meta) {
const name = "enabled_" + meta.row;
const ckbox =
`<input type="checkbox" name="${name}" id="${name}" class="no-icheck" ` +
(data.enabled === "true" ? "checked " : "") +
` data-initial-value="${data.enabled}" disabled>`;
return ckbox;
},
},
{
targets: "_all",
class: "input-text",
render(data, type, row, meta) {
let name;
switch (meta.col) {
case 1:
name = "network_" + meta.row;
break;
case 2:
name = "ip_" + meta.row;
break;
case 3:
name = "domain_" + meta.row;
break;
// No default
}

return `<input type="text" name="${name}" id="${name}" value="${data}" class="form-control" data-initial-value="${data}" disabled>`;
},
},
],
drawCallback() {
$('button[id^="deleteRevServers"]').on("click", deleteRecord);
$('button[id^="editRevServers"]').on("click", editRecord);
$('button[id^="saveRevServers"]').on("click", saveRecord).hide();
$('button[id^="cancelRevServers"]').on("click", restoreRecord).hide();
},
rowCallback(row, data, displayNum, displayIndex, dataIndex) {
$(row).attr("data-index", dataIndex);
const bt = '<button type="button" class="btn btn-xs"</button>';
const btEdit = $(bt)
.addClass("btn-primary")
.attr("id", `editRevServers_${dataIndex}`)
.attr("title", "Edit")
.append('<span class="far fa-edit"></span>');
const btDel = $(bt)
.addClass("btn-danger")
.attr("id", `deleteRevServers_${dataIndex}`)
.attr("data-tag", Object.values(data))
.attr("data-type", "revServers")
.attr("title", "Delete")
.append('<span class="fa fa-trash"></span>');
const btSave = $(bt)
.addClass("btn-success")
.attr("id", `saveRevServers_${dataIndex}`)
.attr("title", "Confirm changes")
.append('<span class="fa fa-check"></span>');
const btCancel = $(bt)
.addClass("btn-warning")
.attr("id", `cancelRevServers_${dataIndex}`)
.attr("data-tag", Object.values(data))
.attr("title", "Undo changes")
.append('<span class="fa fa-undo"></span>');

$("td:eq(4)", row).html(btEdit).append(" ", btDel, " ", btSave, " ", btCancel);
},
dom:
"<'row'<'col-sm-12 text-right'l>>" +
"<'row'<'col-sm-12'<'table-responsive'tr>>><'row'<'col-sm-12'i>>",
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"],
],
language: {
emptyTable: "No revese DNS servers defined.",
},
stateSave: true,
stateDuration: 0,
processing: true,
stateSaveCallback(settings, data) {
utils.stateSaveCallback("revServers-records-table", data);
},
stateLoadCallback() {
const data = utils.stateLoadCallback("revServers-records-table");
// Return if not available
if (data === null) return null;

// Apply loaded state to table
return data;
},
});
}

function addRevServer() {
const values = [];
values[0] = $("#enabled-revServers").is(":checked") ? "true" : "false";
values[1] = $("#network-revServers").val();
values[2] = $("#server-revServers").val();
values[3] = $("#domain-revServers").val();

// Reject empty network range and server IP
if (values[1] === "" || values[2] === "") {
// Show error message
utils.showAlert("error", "fa fa-ban", "Network Range and Server IP are required", "");
return;
}

// Domain is optional: if empty, remove it from the array
if (values[3] === "") values.pop();

// Add the new values to the textarea
$(".revServers").val($(".revServers").val() + "\n" + values.join(","));

// Clear the table footer fields
$("#revServers-table tfoot input[type=text]").val("");
$("#revServers-table tfoot input[type=checkbox]").prop("checked", true);

// Save changes with message
saveRevServerData("New values added: " + values.join(", "));
}

// Button to add a new reverse server
$("#btnAdd-revServers").on("click", addRevServer);

function editRecord() {
// Enable fields on the selected row
$(this).closest("tr").find("td input").prop("disabled", false);

// Hide EDIT and DELETE buttons. Show SAVE and UNDO buttons
$(this).hide();
$(this).siblings('[id^="delete"]').hide();
$(this).siblings('[id^="save"]').show();
$(this).siblings('[id^="cancel"]').show();
}

function saveRecord() {
// Find the row index
const index = $(this).closest("tr").attr("data-index");

// Get the edited values from each field
const values = [];
values[0] = $("#enabled_" + index).prop("checked") ? "true" : "false";
values[1] = $("#network_" + index).val();
values[2] = $("#ip_" + index).val();
values[3] = $("#domain_" + index).val();

// Reject empty network range and server IP
if (values[1] === "" || values[2] === "") {
// Show error message
utils.showAlert("error", "fa fa-ban", "Network Range and Server IP are required", "");
return;
}

// Domain is optional: if empty, remove it from the array
if (values[3] === "") values.pop();

// Get the values from the textarea
const lines = getRevServerLines();

// Replace the edited line on the textarea
lines[index] = values.join(",");
$(".revServers").val(lines.join("\n"));

// Finish the edition disabling the fields
$(this).closest("tr").find("td input").prop("disabled", true);

// Show EDIT and DELETE buttons. Hide SAVE and UNDO buttons
$(this).siblings('[id^="edit"]').show();
$(this).siblings('[id^="delete"]').show();
$(this).hide();
$(this).siblings('[id^="cancel"]').hide();

// Save changes with message
saveRevServerData("Values successfully edited" + values.join(", "));
}

function restoreRecord() {
// Find the row index
const index = $(this).closest("tr").attr("data-index");

// Reset values
$("#enabled_" + index).prop("checked", $("#enabled_" + index).attr("data-initial-value"));
$("#network_" + index).val($("#network_" + index).attr("data-initial-value"));
$("#ip_" + index).val($("#ip_" + index).attr("data-initial-value"));
$("#domain_" + index).val($("#domain_" + index).attr("data-initial-value"));

// Show cancellation message
utils.showAlert("info", "fas fa-undo", "Canceled", "Original values restored");

// Finish the edition disabling the fields
$(this).closest("tr").find("td input").prop("disabled", true);

// Show EDIT and DELETE buttons. Hide SAVE and UNDO buttons
$(this).siblings('[id^="edit"]').show();
$(this).siblings('[id^="delete"]').show();
$(this).siblings('[id^="save"]').hide();
$(this).hide();
}

function deleteRecord() {
// Find the row index (this is also the index of the deleted row)
const index = $(this).closest("tr").attr("data-index");

// Get the current lines from the textarea
const lines = getRevServerLines();

// Remove the deleted line and update the textearea
lines.splice(index, 1);
$(".revServers").val(lines.join("\n"));

// Save changes with message
saveRevServerData("Line successfully deleted");
}

function processDNSConfig() {
$.ajax({
url: document.body.dataset.apiurl + "/config/dns?detailed=true", // We need the detailed output to get the DNS server list
Expand All @@ -146,6 +414,10 @@ function processDNSConfig() {
setInterfaceName(data.config.dns.interface.value);
setConfigValues("dns", "dns", data.config.dns);
})
.done(() => {
// This will be executed only after the done block above is executed
createRevServerTable();
})
.fail(data => {
apiFailure(data);
});
Expand All @@ -154,3 +426,42 @@ function processDNSConfig() {
$(() => {
processDNSConfig();
});

// Save the Reverse Servers data via API and recreate the table with updated values
function saveRevServerData(msg) {
// Get the data from the textarea
const data = getRevServerLines();

// Call the API to save only the dns.revServers option
$.ajax({
url: document.body.dataset.apiurl + "/config",
method: "PATCH",
dataType: "json",
processData: false,
data: JSON.stringify({ config: { dns: { revServers: data } } }),
contentType: "application/json; charset=utf-8",
})
.done(() => {
utils.enableAll();
// Success
utils.showAlert(
"success",
"fa-solid fa-fw fa-floppy-disk",
"Conditional Forwarding settings successfully saved",
msg
);
// Show loading overlay
utils.loadingOverlay(false);

// Reset the table to show the updated data
// Remove all rows from the table, then create rows with the updated data and finally redraw the table
const table = $("#revServers-table").DataTable();
table.clear().rows.add(getRevServerArray()).draw();
})
.fail((data, exception) => {
utils.enableAll();
utils.showAlert("error", "", "Error while applying settings", data.responseText);
console.log(exception); // eslint-disable-line no-console
apiFailure(data);
});
}
2 changes: 1 addition & 1 deletion scripts/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ function loadingOverlayTimeoutCallback(reloadAfterTimeout) {
if (reloadAfterTimeout) {
location.reload();
} else {
waitMe.hideAll();
waitMe.hide();
}
})
.fail(() => {
Expand Down
Loading
Loading