fix: resolve parental & profile blocklists saving and enforcement, add dashboard support for missing protocols

This commit is contained in:
Dushnilin
2026-09-09 20:10:30 +03:00
parent c85811cbe0
commit f2865b4bb2
18 changed files with 587 additions and 306 deletions
@@ -2447,9 +2447,17 @@ function renderDefaultState({
isCollapsed,
onToggleCollapse
}) {
const isConnectionNode = ["vpn", "awg", "warp"].includes(
section.action || ""
);
const isConnectionNode = [
"vpn",
"awg",
"warp",
"anytls",
"snell",
"mieru",
"sudoku",
"masque",
"openvpn"
].includes(section.action || "");
const isServiceNode = ["zapret", "zapret2", "byedpi"].includes(section.action || "") || Boolean(section.serviceStatus);
if (isServiceNode) {
const ss = section.serviceStatus;
@@ -2845,9 +2853,17 @@ function renderDefaultState({
(o) => o.selected
);
if (!selectedOutbound) return "";
const isConnectionNode2 = ["vpn", "awg", "warp"].includes(
section.action || ""
);
const isConnectionNode2 = [
"vpn",
"awg",
"warp",
"anytls",
"snell",
"mieru",
"sudoku",
"masque",
"openvpn"
].includes(section.action || "");
function getLatencyColor() {
if (isConnectionNode2) {
if (latencyFetching)
@@ -4950,11 +4966,30 @@ function getJsonOutbounds(section) {
const values = getListValues(section.outbound_jsons);
return values.length ? values : getListValues(section.outbound_json);
}
var SINGLE_ENDPOINT_ACTIONS = /* @__PURE__ */ new Set([
"vpn",
"awg",
"warp",
"anytls",
"snell",
"mieru",
"sudoku",
"masque",
"openvpn"
]);
var ACTION_DISPLAY_NAMES = {
awg: "AmneziaWG",
warp: "WARP",
anytls: "AnyTLS",
snell: "Snell",
mieru: "Mieru",
sudoku: "Sudoku",
masque: "MASQUE",
openvpn: "OpenVPN"
};
function isConnectionAction(action) {
return Boolean(
action && ["connection", "proxy", "outbound", "vpn", "awg", "warp"].includes(
action
)
action && (SINGLE_ENDPOINT_ACTIONS.has(action) || ["connection", "proxy", "outbound"].includes(action))
);
}
function isServiceAction(action) {
@@ -5791,9 +5826,11 @@ async function getDashboardSections(options = {}) {
outbounds
};
}
if (sectionAction === "vpn" || sectionAction === "awg" || sectionAction === "warp") {
if (SINGLE_ENDPOINT_ACTIONS.has(sectionAction || "")) {
const outboundTag = getOutboundTagBySection(sectionName);
const outbound = proxies.find((proxy) => proxy.code === outboundTag);
const defaultLabel = ACTION_DISPLAY_NAMES[sectionAction || ""] || (sectionAction || "").toUpperCase();
const customName = outbound?.value?.name && outbound.value.name !== outboundTag ? outbound.value.name : "";
return {
withTagSelect: false,
code: outbound?.code || sectionName,
@@ -5803,9 +5840,9 @@ async function getDashboardSections(options = {}) {
outbounds: [
{
code: outbound?.code || sectionName,
displayName: section.interface || outbound?.value?.name || (sectionAction === "awg" ? "AmneziaWG" : sectionAction.toUpperCase()),
displayName: section.label || section.interface || customName || defaultLabel,
latency: outbound?.value?.history?.length ? outbound.value.history[0].delay > 0 ? outbound.value.history[0].delay : -1 : 0,
type: sectionAction === "awg" ? "AmneziaWG" : sectionAction === "warp" ? "WARP" : outbound?.value?.type || "",
type: ACTION_DISPLAY_NAMES[sectionAction || ""] || outbound?.value?.type || (sectionAction || "").toUpperCase(),
selected: true,
canCopyLink: false,
runtimeAvailable: Boolean(outbound)
@@ -8769,9 +8806,17 @@ function updateLatencyProgressInline(sectionsWidget) {
if (!label) {
return false;
}
const isConnectionNode = ["vpn", "awg", "warp"].includes(
section.action || ""
);
const isConnectionNode = [
"vpn",
"awg",
"warp",
"anytls",
"snell",
"mieru",
"sudoku",
"masque",
"openvpn"
].includes(section.action || "");
const text = isConnectionNode ? _("Checking Connection...") : getLatencyTestLabel(
sectionsWidget.latencyProgressSections[section.sectionName]
);
@@ -586,7 +586,7 @@ function createProfileContent(section) {
.map((l) => l.trim())
.filter(Boolean);
for (const line of lines) {
const clean = line.replace(/^(full:|keyword:|regex:)/, "");
const clean = line.replace(/^(full:|keyword:|regex:)/, "").replace(/^\*?\./, "");
if (/^(full:|keyword:|regex:)/.test(line) && !clean) {
return _("Invalid domain: empty prefix value");
}
@@ -599,30 +599,30 @@ function createProfileContent(section) {
}
return true;
};
o.write = function (sectionId, value) {
const lines = `${value || ""}`
.split(/\r?\n/)
.map((l) => l.trim())
.filter(Boolean);
const existingList = normalizeListValues(
uci.get(UCI_PACKAGE, sectionId, "blocked_domains"),
);
for (const d of lines) {
if (!existingList.includes(d)) {
uci.add_list(UCI_PACKAGE, sectionId, "blocked_domains", d);
}
}
for (const d of existingList) {
if (!lines.includes(d)) {
uci.remove_list(UCI_PACKAGE, sectionId, "blocked_domains", d);
}
}
};
o.load = function (sectionId) {
o.cfgvalue = function (sectionId) {
return normalizeListValues(
uci.get(UCI_PACKAGE, sectionId, "blocked_domains"),
).join("\n");
};
o.load = o.cfgvalue;
o.formvalue = function (sectionId) {
const node = document.getElementById(this.cbid(sectionId));
return node ? node.value : null;
};
o.write = function (sectionId, value) {
const lines = [];
for (const raw of `${value || ""}`.split(/\r?\n/)) {
const l = raw.trim();
if (l && !lines.includes(l)) {
lines.push(l);
}
}
if (lines.length === 0) {
uci.unset(UCI_PACKAGE, sectionId, "blocked_domains");
} else {
uci.set(UCI_PACKAGE, sectionId, "blocked_domains", lines);
}
};
o.remove = function (sectionId) {
uci.unset(UCI_PACKAGE, sectionId, "blocked_domains");
};
@@ -654,18 +654,23 @@ function createProfileContent(section) {
if (!merged.includes(d)) merged.push(d);
}
textarea.value = merged.join("\n");
textarea.dispatchEvent(new Event("input", { bubbles: true }));
textarea.dispatchEvent(new Event("change", { bubbles: true }));
});
presetRow.appendChild(btn);
});
container.appendChild(presetRow);
const cbid = this.cbid(sectionId);
const textarea = E("textarea", {
id: `cbid.tachyon.${sectionId}.blocked_domains`,
id: cbid,
name: cbid,
class: "cbi-input-textarea",
style: "width:100%;min-height:100px;box-sizing:border-box;",
placeholder: "tiktok.com\nroblox.com",
});
if (cfgvalue) textarea.value = cfgvalue;
const strVal = Array.isArray(cfgvalue) ? cfgvalue.join("\n") : (cfgvalue || "");
if (strVal) textarea.value = strVal;
container.appendChild(textarea);
return container;
};
@@ -1170,7 +1175,7 @@ function createParentalContent(section) {
.map((l) => l.trim())
.filter(Boolean);
for (const line of lines) {
const clean = line.replace(/^(full:|keyword:|regex:)/, "");
const clean = line.replace(/^(full:|keyword:|regex:)/, "").replace(/^\*?\./, "");
if (/^(full:|keyword:|regex:)/.test(line) && !clean) {
return _("Invalid domain: empty prefix value");
}
@@ -1183,30 +1188,30 @@ function createParentalContent(section) {
}
return true;
};
o.write = function (sectionId, value) {
const lines = `${value || ""}`
.split(/\r?\n/)
.map((l) => l.trim())
.filter(Boolean);
const existingList = normalizeListValues(
uci.get(UCI_PACKAGE, sectionId, "blocked_domains"),
);
for (const d of lines) {
if (!existingList.includes(d)) {
uci.add_list(UCI_PACKAGE, sectionId, "blocked_domains", d);
}
}
for (const d of existingList) {
if (!lines.includes(d)) {
uci.remove_list(UCI_PACKAGE, sectionId, "blocked_domains", d);
}
}
};
o.load = function (sectionId) {
o.cfgvalue = function (sectionId) {
return normalizeListValues(
uci.get(UCI_PACKAGE, sectionId, "blocked_domains"),
).join("\n");
};
o.load = o.cfgvalue;
o.formvalue = function (sectionId) {
const node = document.getElementById(this.cbid(sectionId));
return node ? node.value : null;
};
o.write = function (sectionId, value) {
const lines = [];
for (const raw of `${value || ""}`.split(/\r?\n/)) {
const l = raw.trim();
if (l && !lines.includes(l)) {
lines.push(l);
}
}
if (lines.length === 0) {
uci.unset(UCI_PACKAGE, sectionId, "blocked_domains");
} else {
uci.set(UCI_PACKAGE, sectionId, "blocked_domains", lines);
}
};
o.remove = function (sectionId) {
uci.unset(UCI_PACKAGE, sectionId, "blocked_domains");
};
@@ -1240,18 +1245,23 @@ function createParentalContent(section) {
if (!merged.includes(d)) merged.push(d);
}
textarea.value = merged.join("\n");
textarea.dispatchEvent(new Event("input", { bubbles: true }));
textarea.dispatchEvent(new Event("change", { bubbles: true }));
});
presetRow.appendChild(btn);
});
container.appendChild(presetRow);
const cbid = this.cbid(sectionId);
const textarea = E("textarea", {
id: `cbid.tachyon.${sectionId}.blocked_domains`,
id: cbid,
name: cbid,
class: "cbi-input-textarea",
style: "width:100%;min-height:110px;box-sizing:border-box;",
placeholder: "youtube.com\ngooglevideo.com",
});
if (cfgvalue) textarea.value = cfgvalue;
const strVal = Array.isArray(cfgvalue) ? cfgvalue.join("\n") : (cfgvalue || "");
if (strVal) textarea.value = strVal;
container.appendChild(textarea);
return container;
};