hosts: add per-URL enable/disable toggle for remote hosts lists

- Backend: get_hosts_urls() now reads hosts_list_disabled list and
  filters out disabled URLs before downloading.
- Backend: hosts_list_status() now returns total and disabled counts.
- Frontend: Added hidden hosts_list_disabled DynamicList option.
- Frontend: Overrode renderWidget on hosts_list_urls to prepend a
  toggle switch (ON/OFF) next to each URL. Toggle syncs with
  hosts_list_disabled via uci.set().
This commit is contained in:
Dushnilin
2026-08-05 12:24:39 +03:00
parent a32ad76285
commit 969937428f
2 changed files with 90 additions and 3 deletions
@@ -1232,6 +1232,65 @@ function createSettingsContent(section, capabilities) {
);
hostsListUrlsOpt.placeholder = "https://raw.githubusercontent.com/.../hosts";
const hostsListDisabledOpt = section.option(
form.DynamicList,
"hosts_list_disabled",
);
hostsListDisabledOpt.anonymous = true;
hostsListDisabledOpt.hidden = true;
const _origHostsListRenderWidget = hostsListUrlsOpt.renderWidget;
hostsListUrlsOpt.renderWidget = function (section_id, option_index, cfgvalue) {
const node = _origHostsListRenderWidget.call(this, section_id, option_index, cfgvalue);
const disabledValues = uci.get(UCI_PACKAGE, "settings", "hosts_list_disabled") || [];
function syncToggles() {
const container = node.querySelector(".cbi-dynlist") || node;
const items = container.querySelectorAll(".cbi-dynlist-item, .item");
items.forEach((item) => {
if (item.querySelector(".hosts-toggle")) return;
const valueEl = item.querySelector(".cbi-dynlist-item-value, input");
if (!valueEl) return;
const url = (valueEl.textContent || valueEl.value || "").trim();
if (!url) return;
const isDisabled = disabledValues.includes(url);
const toggle = E("label", {
class: "hosts-toggle",
style: "display:inline-flex;align-items:center;cursor:pointer;margin-right:6px;flex-shrink:0;",
}, [
E("input", {
type: "checkbox",
checked: !isDisabled,
style: "margin-right:4px;",
change: function () {
const enabled = this.checked;
let currentDisabled = uci.get(UCI_PACKAGE, "settings", "hosts_list_disabled") || [];
if (!Array.isArray(currentDisabled)) currentDisabled = [currentDisabled];
if (enabled) {
currentDisabled = currentDisabled.filter((d) => d !== url);
} else {
if (!currentDisabled.includes(url)) currentDisabled.push(url);
}
uci.set(UCI_PACKAGE, "settings", "hosts_list_disabled", currentDisabled.length ? currentDisabled : null);
uci.save();
},
}),
E("span", {
style: "font-size:0.8rem;color:" + (isDisabled ? "var(--text-color-danger,#e53935)" : "var(--text-color-success,#43a047)"),
}, isDisabled ? "OFF" : "ON"),
]);
item.prepend(toggle);
});
}
const observer = new MutationObserver(syncToggles);
observer.observe(node, { childList: true, subtree: true });
setTimeout(syncToggles, 100);
return node;
};
o = section.option(
form.Flag,
"hosts_list_auto_update",
+31 -3
View File
@@ -145,15 +145,32 @@ function get_hosts_urls() {
if (uci_urls == "")
return urls;
let disabled = [];
let uci_disabled = uci.get(CONFIG_NAME + ".settings.hosts_list_disabled");
if (type(uci_disabled) == "array") {
for (let d in uci_disabled) {
let s = trim(d);
if (s != "") push(disabled, s);
}
} else if (uci_disabled != null && uci_disabled != "") {
push(disabled, trim(uci_disabled));
}
let is_disabled = function(url) {
for (let d in disabled)
if (d == url) return true;
return false;
};
if (type(uci_urls) == "array") {
for (let url in uci_urls) {
let s = trim(url);
if (s != "")
if (s != "" && !is_disabled(s))
push(urls, s);
}
} else {
let s = trim(uci_urls);
if (s != "")
if (s != "" && !is_disabled(s))
push(urls, s);
}
return urls;
@@ -208,6 +225,17 @@ function hosts_list_update(target_url) {
function hosts_list_status() {
let urls = get_hosts_urls();
let all_urls = [];
let uci_urls = uci.get(CONFIG_NAME + ".settings.hosts_list_urls");
if (type(uci_urls) == "array") {
for (let url in uci_urls) {
let s = trim(url);
if (s != "") push(all_urls, s);
}
} else if (uci_urls != null && uci_urls != "") {
push(all_urls, trim(uci_urls));
}
let disabled_count = length(all_urls) - length(urls);
let st = fs.stat(HOSTS_CACHE_FILE);
let cache_exists = st != null;
let entry_count = 0;
@@ -216,7 +244,7 @@ function hosts_list_status() {
entry_count = int(st.size);
}
print('{"urls":' + length(urls) + ',"cache_exists":' + (cache_exists ? 'true' : 'false') + ',"cache_size":' + entry_count + '}');
print('{"urls":' + length(urls) + ',"total":' + length(all_urls) + ',"disabled":' + disabled_count + ',"cache_exists":' + (cache_exists ? 'true' : 'false') + ',"cache_size":' + entry_count + '}');
}
let command = ARGV[0] || "";