fix: zapret2 strategy args, snapshot restore output leak, and opkg update lock handling

- providers/zapret2/common.uc: import core.common and declare as_string helper to resolve type error during zapret2 strategy argument preparation.
- service/snapshot.uc: redirect command output to /dev/null in command_success_from_args to prevent stdout leakage breaking LuCI JSON parsing on snapshot restore.
- settings.js: robustly parse JSON from snapshot command output using regex delimiter.
- components/action.uc: add --force-depends to opkg install arguments, detect opkg lock contention and retry with backoff, and log non-zero exit stdout as warning.
- providers/nfqueue/runtime.uc: ensure standalone services are cleanly stopped and disabled before starting Tachyon-managed runtime.
- section.js, generator_routes.uc, apply.uc: add community_subnets option to conditionally include community CIDRs in routing and nftables.
This commit is contained in:
Dushnilin
2026-09-09 11:55:12 +03:00
parent 01dad3bab0
commit 142fff6b4a
10 changed files with 100 additions and 43 deletions
@@ -10503,6 +10503,20 @@ function createSectionContent(section) {
}
};
const communitySubnetsOption = section.taboption(
"conditions",
form.Flag,
"community_subnets",
_("Route community list subnets (IP/CIDR)"),
_(
"Route IP subnets associated with built-in community lists (e.g. Discord voice/media). Disable this if community subnets overlap with CDN/Anycast networks (such as Cloudflare/Google Cloud) and interfere with unrelated services (e.g. game launchers).",
),
);
communitySubnetsOption.modalonly = true;
communitySubnetsOption.default = "1";
communitySubnetsOption.rmempty = false;
dependsOnRoutingAction(communitySubnetsOption);
const ruleSetOption = section.taboption(
"conditions",
SettingsDynamicList,
@@ -11126,23 +11140,27 @@ async function performTrace(query) {
}
// Check community list rulesets for matching IPs
const communityLists = normalizeOptionValues(
uci.get(UCI_PACKAGE, secName, "community_lists"),
);
for (const community of communityLists) {
if (
await matchCommunityList(secName, community, queryForMatching, type)
) {
return {
matched: true,
sectionName: secName,
label: label,
action: action,
ruleType: "Community List (" + community + ")",
pattern: queryForMatching,
priority: i + 1,
totalSections: totalSections,
};
const communitySubnetsEnabled =
uci.get(UCI_PACKAGE, secName, "community_subnets") !== "0";
if (communitySubnetsEnabled) {
const communityLists = normalizeOptionValues(
uci.get(UCI_PACKAGE, secName, "community_lists"),
);
for (const community of communityLists) {
if (
await matchCommunityList(secName, community, queryForMatching, type)
) {
return {
matched: true,
sectionName: secName,
label: label,
action: action,
ruleType: "Community List (" + community + ")",
pattern: queryForMatching,
priority: i + 1,
totalSections: totalSections,
};
}
}
}
}
@@ -534,7 +534,9 @@ function createSnapshotsWidget() {
return fs.exec("/usr/bin/tachyon", args).then(function (res) {
let ok = false;
try {
ok = JSON.parse((res && res.stdout) || "").success !== false;
let stdout = ((res && res.stdout) || "").trim();
let match = stdout.match(/\{[\s\S]*\}/);
ok = JSON.parse(match ? match[0] : stdout).success !== false;
} catch (e) {
ok = false;
}