-
-
Notifications
You must be signed in to change notification settings - Fork 646
Expand file tree
/
Copy pathsettings.js
More file actions
203 lines (173 loc) · 6.03 KB
/
Copy pathsettings.js
File metadata and controls
203 lines (173 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
/* global utils:false, apiFailure:false*/
"use strict";
$(() => {
// Handle hiding of alerts
$("[data-hide]").on("click", function () {
$(this)
.closest("." + $(this).attr("data-hide"))
.hide();
});
// Handle saving of settings
$(".save-button").on("click", () => {
saveSettings();
});
});
// Globally available function to set config values
// eslint-disable-next-line no-unused-vars
function setConfigValues(topic, key, value) {
// If the value is an object, we need to recurse
if (!("description" in value)) {
for (const [subkey, subvalue] of Object.entries(value)) {
// If the key is empty, we are at the top level
const newKey = key === "" ? subkey : key + "." + subkey;
setConfigValues(topic, newKey, subvalue);
}
return;
}
// else: we have a setting we can set
const escapedKey = key.replaceAll(".", "\\.");
const envTitle = $(`[data-configkeys~='${key}']`);
if (
envTitle.parents().parents().hasClass("settings-level-expert") &&
envTitle.find(".expert-warning").length === 0
) {
envTitle.append(
'<span class="expert-warning"> <i class="fas fa-wrench" title="Expert level setting"></i></span>'
);
}
if (value.flags.restart_dnsmasq && envTitle.find(".restart-warning").length === 0) {
envTitle.append(
'<span class="restart-warning"> <i class="fas fa-redo text-orange" title="Setting requires FTL restart on change"></i></span>'
);
}
if (value.flags.env_var) {
// If this setting has been set by environment variable, display a padlock in the section title
if (envTitle.find(".env-warning").length === 0) {
envTitle.append(
'<span class="env-warning"> <i class="fas fa-lock text-orange env-warning" title="Settings overwritten by an environmental variable are read-only"></i></span>'
);
}
$(`#${escapedKey}`).prop("disabled", "disabled");
}
switch (value.type) {
case "enum (unsigned integer)": // fallthrough
case "enum (string)": {
// Remove all options from select
$("#" + escapedKey + " option").remove();
// Add allowed select items (if available)
for (const allowedValue of value.allowed) {
$("#" + escapedKey + "-" + allowedValue.item).prop("disabled", value.flags.env_var);
const newopt = $("<option></option>")
.attr("value", allowedValue.item)
.text(allowedValue.description);
$("#" + escapedKey).append(newopt);
}
// Select the current value
$("#" + escapedKey)
.val(value.value)
.trigger("click");
// Also select matching radio button (if any)
$("#" + escapedKey + "-" + value.value).prop("checked", true);
break;
}
case "boolean": {
// Select checkboxes (if available)
$("#" + escapedKey).prop("checked", value.value);
break;
}
case "string array": {
// Set input field values from array (if available)
$("#" + escapedKey).val(value.value.join("\n"));
break;
}
default: {
// Set input field values (if available)
// Set text if this is a <span> or <code> element
if ($("#" + escapedKey).is("span") || $("#" + escapedKey).is("code")) {
$("#" + escapedKey).text(value.value);
} else {
// Set value if this is an <input> element
$("#" + escapedKey).val(value.value);
}
}
}
}
function saveSettings() {
const settings = {};
utils.disableAll();
$("[data-key]").each(function () {
const key = $(this).data("key");
let value = $(this).val();
// If this is a checkbox, use the checked state
if ($(this).is(":checkbox")) {
value = $(this).is(":checked");
}
// If this is a radio button, skip all but the checked one
if ($(this).is(":radio") && !$(this).is(":checked")) {
return;
}
// If this is a string array, split the value into an array
if ($(this).is("textarea")) {
value = $(this).val();
value = value === "" ? [] : value.split("\n");
// On DNS settings page, upstream table selections are merged at save
// time so the textarea can remain manual-only in the UI.
if (key === "dns.upstreams" && typeof globalThis.getMergedDNSupstreams === "function") {
value = globalThis.getMergedDNSupstreams(value);
}
}
// If this is an integer number, parse it accordingly
if ($(this).data("type") === "integer") {
value = value === "" ? NaN : Math.trunc(value);
}
// If this is a floating point value, parse it accordingly
if ($(this).data("type") === "float") {
value = value === "" ? NaN : Number(value);
}
// Build deep object
// Transform "foo.bar.baz" into {foo: {bar: {baz: value}}}
const parts = key.split(".");
const obj = {};
let tmp = obj;
for (let i = 0; i < parts.length - 1; i++) {
tmp[parts[i]] = {};
tmp = tmp[parts[i]];
}
tmp[parts.at(-1)] = value;
// Merge deep object into settings
$.extend(true, settings, obj);
});
// Apply changes
$.ajax({
url: document.body.dataset.apiurl + "/config",
method: "PATCH",
dataType: "json",
processData: false,
data: JSON.stringify({ config: settings }),
contentType: "application/json; charset=utf-8",
})
.done(() => {
utils.enableAll();
// Success
utils.showAlert(
"success",
"fa-solid fa-fw fa-floppy-disk",
"Successfully saved and applied settings",
""
);
// Show loading overlay
utils.loadingOverlay(true);
})
.fail((data, exception) => {
utils.enableAll();
utils.showAlert("error", "", "Error while applying settings", data.responseText);
console.log(exception); // eslint-disable-line no-console
apiFailure(data);
});
}