fix(components): robust version installation and updates across Docker, APK v3 and OPKG
- fix(updater): support single release JSON object for specific tag queries and unblock prereleases on explicit selection - fix(action): replace wget --header with -U for uclient-fetch compatibility on OpenWrt 23.05 - fix(action): make package list updates best-effort to prevent failures on snapshot kmod feeds - fix(action): add offline/corrupt extract fallbacks (apk extract and opkg data.tar.gz) for kernel dependency mismatch - fix(action): ensure /var/lock and /tmp/run directories exist before opkg/procd operations - fix(action): fix fallback asset URL pattern for remittor zapret releases - fix(packages): add binary presence and --version detection fallbacks for zapret, zapret2, byedpi, sing-box - fix(byedpi): support direct apk list parsing for installed package version - fix(state): include schedule, profile, and guest_mode in reload state signatures - fix(imports): fix missing command_status_from_args, write_json, and remove_file imports - fix(updates): extract meaningful ucode errors and strip trailing caret lines
This commit is contained in:
@@ -17,7 +17,7 @@ export const DOMAIN_LIST_OPTIONS = {
|
||||
russia_outside: 'Russia outside',
|
||||
ukraine_inside: 'Ukraine',
|
||||
geoblock: 'Geo Block',
|
||||
block: 'Block (RKN)',
|
||||
block: 'Block',
|
||||
porn: 'Porn',
|
||||
news: 'News',
|
||||
anime: 'Anime',
|
||||
|
||||
@@ -465,10 +465,27 @@ export function showUpdateProgressModal(
|
||||
cleanupTimers();
|
||||
finishLogTracking();
|
||||
|
||||
let cleanError = (errorMessage || '').trim();
|
||||
if (
|
||||
cleanError.includes('Near here') ||
|
||||
/^[-=~_^]{3,}$/m.test(cleanError)
|
||||
) {
|
||||
cleanError = cleanError
|
||||
.split('\n')
|
||||
.filter(
|
||||
(line) =>
|
||||
!line.includes('Near here') &&
|
||||
!/^[\s\-_^]+$/.test(line) &&
|
||||
line.trim() !== '',
|
||||
)
|
||||
.join(' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
const bannerEl = E(
|
||||
'div',
|
||||
{ class: 'tachyon-update-modal__error-banner' },
|
||||
[renderXIcon24(), E('span', {}, errorMessage || _('Operation failed'))],
|
||||
[renderXIcon24(), E('span', {}, cleanError || _('Operation failed'))],
|
||||
);
|
||||
|
||||
const closeBtn = renderButton({
|
||||
|
||||
@@ -1232,7 +1232,7 @@ var DOMAIN_LIST_OPTIONS = {
|
||||
russia_outside: "Russia outside",
|
||||
ukraine_inside: "Ukraine",
|
||||
geoblock: "Geo Block",
|
||||
block: "Block (RKN)",
|
||||
block: "Block",
|
||||
porn: "Porn",
|
||||
news: "News",
|
||||
anime: "Anime",
|
||||
@@ -20316,10 +20316,16 @@ function showUpdateProgressModal(options) {
|
||||
completeError: (errorMessage) => {
|
||||
cleanupTimers();
|
||||
finishLogTracking();
|
||||
let cleanError = (errorMessage || "").trim();
|
||||
if (cleanError.includes("Near here") || /^[-=~_^]{3,}$/m.test(cleanError)) {
|
||||
cleanError = cleanError.split("\n").filter(
|
||||
(line) => !line.includes("Near here") && !/^[\s\-_^]+$/.test(line) && line.trim() !== ""
|
||||
).join(" ").trim();
|
||||
}
|
||||
const bannerEl = E(
|
||||
"div",
|
||||
{ class: "tachyon-update-modal__error-banner" },
|
||||
[renderXIcon24(), E("span", {}, errorMessage || _("Operation failed"))]
|
||||
[renderXIcon24(), E("span", {}, cleanError || _("Operation failed"))]
|
||||
);
|
||||
const closeBtn = renderButton({
|
||||
classNames: ["cbi-button-remove"],
|
||||
|
||||
@@ -7439,12 +7439,7 @@ function loadRulesetValues(option) {
|
||||
delete option.vallist;
|
||||
|
||||
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
|
||||
let displayLabel = _(label);
|
||||
if (key === "block") {
|
||||
displayLabel = "Block (РКН / внутри РФ)";
|
||||
} else if (key === "geoblock") {
|
||||
displayLabel = "Geo Block (снаружи РФ)";
|
||||
}
|
||||
const displayLabel = _(label);
|
||||
const hint = DOMAIN_LIST_HINTS[key];
|
||||
option.value(key, hint ? `${displayLabel} — ${hint}` : displayLabel);
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ let as_string = common.as_string;
|
||||
let shell_quote = common.shell_quote;
|
||||
let command_from_args = common.command_from_args;
|
||||
let command_status = common.command_status;
|
||||
let command_status_from_args = common.command_status_from_args;
|
||||
let command_success = common.command_success;
|
||||
let command_success_from_args = common.command_success_from_args;
|
||||
let command_output = common.command_output;
|
||||
@@ -195,6 +196,8 @@ function cleanup_stale_tmp_files() {
|
||||
}
|
||||
|
||||
function init_tmp_dir() {
|
||||
ensure_dir("/var/lock");
|
||||
ensure_dir("/tmp/run");
|
||||
if (tmp_dir != "")
|
||||
return true;
|
||||
|
||||
@@ -452,12 +455,25 @@ function pkg_install_name_downgrade(package_name, package_version) {
|
||||
// published under the same tag would silently not be applied. apk always writes
|
||||
// the file it is handed, so its argument list stays untouched.
|
||||
function pkg_install_files_command(files, force_reinstall) {
|
||||
let args = is_apk() ? [ "apk", "add", "--allow-untrusted" ] : [ "opkg", "install", "--force-overwrite", "--force-downgrade", "--force-depends" ];
|
||||
if (!is_apk() && force_reinstall)
|
||||
if (is_apk()) {
|
||||
let add_args = [ "apk", "add", "--allow-untrusted" ];
|
||||
for (let file in files)
|
||||
push(add_args, file);
|
||||
let extract_args = [ "apk", "extract", "--allow-untrusted", "--destination", "/" ];
|
||||
for (let file in files)
|
||||
push(extract_args, file);
|
||||
return "(" + command_from_args(add_args) + " </dev/null || " + command_from_args(extract_args) + " </dev/null)";
|
||||
}
|
||||
let args = [ "opkg", "install", "--force-overwrite", "--force-downgrade", "--force-depends" ];
|
||||
if (force_reinstall)
|
||||
push(args, "--force-reinstall");
|
||||
for (let file in files)
|
||||
push(args, file);
|
||||
return command_from_args(args) + " </dev/null";
|
||||
let opkg_cmd = command_from_args(args) + " </dev/null";
|
||||
let fallback_cmds = [];
|
||||
for (let file in files)
|
||||
push(fallback_cmds, "(tar -zxOf " + shell_quote(file) + " ./data.tar.gz 2>/dev/null || tar -zxOf " + shell_quote(file) + " data.tar.gz 2>/dev/null) | tar -zx -C /");
|
||||
return "(" + opkg_cmd + " || (" + join(" && ", fallback_cmds) + "))";
|
||||
}
|
||||
|
||||
function pkg_install_files(files, force_reinstall) {
|
||||
@@ -607,7 +623,7 @@ function http_get_once(url, output_path, proxy_address, timeout) {
|
||||
}
|
||||
|
||||
if (command_exists("wget")) {
|
||||
let command = command_from_args([ "wget", "-T", timeout, "-q", "-O", output_path, "--header=User-Agent: Tachyon-OpenWrt", url ]);
|
||||
let command = command_from_args([ "wget", "-T", timeout, "-q", "-O", output_path, "-U", "Tachyon-OpenWrt", url ]);
|
||||
if (proxy_address != "")
|
||||
command = command_env({ http_proxy: "http://" + proxy_address, https_proxy: "http://" + proxy_address }) + " " + command;
|
||||
return command_success(command);
|
||||
@@ -769,30 +785,40 @@ function fetch_github_release_tag_fallback(owner, repo) {
|
||||
let url = "https://github.com/" + as_string(owner) + "/" + as_string(repo) + "/releases/latest";
|
||||
let url_mod = core_url_module_or_null();
|
||||
let candidates = url_mod && type(url_mod.download_candidates) == "function" ? url_mod.download_candidates(url) : [ url ];
|
||||
let proxy_addr = service_proxy_address();
|
||||
|
||||
for (let target_url in candidates) {
|
||||
let args = [ "curl", "-sI", "--connect-timeout", "6", "-m", "12" ];
|
||||
let proxy_addr = service_proxy_address();
|
||||
if (proxy_addr != "") {
|
||||
push(args, "-x");
|
||||
push(args, "http://" + proxy_addr);
|
||||
}
|
||||
push(args, target_url);
|
||||
let output = command_output_from_args(args);
|
||||
if (output != "") {
|
||||
let loc_idx = index(lc(output), "location:");
|
||||
if (loc_idx >= 0) {
|
||||
let line = substr(output, loc_idx);
|
||||
let end_line = index(line, "\r");
|
||||
if (end_line < 0) end_line = index(line, "\n");
|
||||
if (end_line >= 0) line = substr(line, 0, end_line);
|
||||
let tag_idx = rindex(line, "/");
|
||||
if (tag_idx >= 0) {
|
||||
let tag = trim(substr(line, tag_idx + 1));
|
||||
if (tag != "")
|
||||
return tag;
|
||||
if (command_exists("curl")) {
|
||||
let args = [ "curl", "-sI", "--connect-timeout", "6", "-m", "12" ];
|
||||
if (proxy_addr != "") {
|
||||
push(args, "-x");
|
||||
push(args, "http://" + proxy_addr);
|
||||
}
|
||||
push(args, target_url);
|
||||
let output = command_output_from_args(args);
|
||||
if (output != "") {
|
||||
let loc_idx = index(lc(output), "location:");
|
||||
if (loc_idx >= 0) {
|
||||
let line = substr(output, loc_idx);
|
||||
let end_line = index(line, "\r");
|
||||
if (end_line < 0) end_line = index(line, "\n");
|
||||
if (end_line >= 0) line = substr(line, 0, end_line);
|
||||
let tag_idx = rindex(line, "/");
|
||||
if (tag_idx >= 0) {
|
||||
let tag = trim(substr(line, tag_idx + 1));
|
||||
if (tag != "")
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (command_exists("wget")) {
|
||||
let cmd = command_from_args([ "wget", "-s", "-T", "6", target_url ]);
|
||||
if (proxy_addr != "")
|
||||
cmd = command_env({ http_proxy: "http://" + proxy_addr, https_proxy: "http://" + proxy_addr }) + " " + cmd;
|
||||
let output = command_output("(" + cmd + ") 2>&1");
|
||||
let m = match(output, /Redirected to [^ \t\r\n]*\/releases\/tag\/([^ \t\r\n]+)/);
|
||||
if (m && m[1])
|
||||
return trim(m[1]);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -801,21 +827,29 @@ function fetch_github_release_tag_fallback(owner, repo) {
|
||||
function url_exists(url) {
|
||||
let url_mod = core_url_module_or_null();
|
||||
let candidates = url_mod && type(url_mod.download_candidates) == "function" ? url_mod.download_candidates(url) : [ url ];
|
||||
let proxy_addr = service_proxy_address();
|
||||
|
||||
for (let target_url in candidates) {
|
||||
let args = [ "curl", "-sI", "--connect-timeout", "6", "-m", "12" ];
|
||||
let proxy_addr = service_proxy_address();
|
||||
if (proxy_addr != "") {
|
||||
push(args, "-x");
|
||||
push(args, "http://" + proxy_addr);
|
||||
}
|
||||
push(args, as_string(target_url));
|
||||
let output = command_output_from_args(args);
|
||||
if (output != "") {
|
||||
let first_line = split(output, "\n")[0] || "";
|
||||
if (index(first_line, " 200 ") > 0 || index(first_line, " 301 ") > 0 || index(first_line, " 302 ") > 0) {
|
||||
return true;
|
||||
if (command_exists("curl")) {
|
||||
let args = [ "curl", "-sI", "--connect-timeout", "6", "-m", "12" ];
|
||||
if (proxy_addr != "") {
|
||||
push(args, "-x");
|
||||
push(args, "http://" + proxy_addr);
|
||||
}
|
||||
push(args, as_string(target_url));
|
||||
let output = command_output_from_args(args);
|
||||
if (output != "") {
|
||||
let first_line = split(output, "\n")[0] || "";
|
||||
if (index(first_line, " 200 ") > 0 || index(first_line, " 301 ") > 0 || index(first_line, " 302 ") > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (command_exists("wget")) {
|
||||
let cmd = command_from_args([ "wget", "-s", "-T", "6", "-q", as_string(target_url) ]);
|
||||
if (proxy_addr != "")
|
||||
cmd = command_env({ http_proxy: "http://" + proxy_addr, https_proxy: "http://" + proxy_addr }) + " " + cmd;
|
||||
if (command_success(cmd))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -916,8 +950,7 @@ function retry_resolve(description, fn) {
|
||||
function ensure_package_tool(tool_name, package_name, component, action) {
|
||||
if (command_exists(tool_name))
|
||||
return true;
|
||||
if (!run_logged("Updating package lists before installing " + as_string(package_name), pkg_list_update_command()))
|
||||
return false;
|
||||
run_logged("Updating package lists before installing " + as_string(package_name), pkg_list_update_command());
|
||||
return run_logged("Installing bootstrap package " + as_string(package_name), pkg_install_name_command(package_name));
|
||||
}
|
||||
|
||||
@@ -1199,27 +1232,40 @@ function resolve_zapret_release(arch, tag) {
|
||||
}
|
||||
if (tag != null && tag != "") {
|
||||
let tag_clean = replace(tag, /^v/, "");
|
||||
let bundle_name = "zapret_" + tag_clean + "_openwrt_" + arch.candidates + ".zip";
|
||||
let tag_with_v = "v" + tag_clean;
|
||||
let bundle_name = "zapret_" + tag_with_v + "_" + arch.candidates + ".zip";
|
||||
return {
|
||||
arch: arch.candidates,
|
||||
bundle_name: bundle_name,
|
||||
bundle_url: "https://github.com/remittor/zapret-openwrt/releases/download/" + tag + "/" + bundle_name,
|
||||
release_url: "https://github.com/remittor/zapret-openwrt/releases/tag/" + tag,
|
||||
bundle_url: "https://github.com/remittor/zapret-openwrt/releases/download/" + tag_with_v + "/" + bundle_name,
|
||||
release_url: "https://github.com/remittor/zapret-openwrt/releases/tag/" + tag_with_v,
|
||||
version: tag
|
||||
};
|
||||
}
|
||||
return { fetch_failed: true };
|
||||
}
|
||||
|
||||
function resolve_zapret2_release(arch) {
|
||||
let tag = fetch_github_release_tag_fallback("Dushnilin", "zapret2-openwrt");
|
||||
if (tag == "")
|
||||
function resolve_zapret2_release(arch, tag) {
|
||||
let resolved_tag = (tag != null && tag != "") ? tag : "";
|
||||
if (resolved_tag == "") {
|
||||
let releases_json = fetch_github_releases_json("Dushnilin", "zapret2-openwrt", "5");
|
||||
if (releases_json != "") {
|
||||
try {
|
||||
let parsed = json(releases_json);
|
||||
if (type(parsed) == "array" && length(parsed) > 0)
|
||||
resolved_tag = trim(as_string(parsed[0].tag_name || ""));
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
if (resolved_tag == "")
|
||||
resolved_tag = fetch_github_release_tag_fallback("Dushnilin", "zapret2-openwrt");
|
||||
if (resolved_tag == "")
|
||||
return { fetch_failed: true };
|
||||
|
||||
let asset_ext = is_apk() ? "apk" : "ipk";
|
||||
let base_dl = "https://github.com/Dushnilin/zapret2-openwrt/releases/download/" + tag + "/";
|
||||
let release_url = "https://github.com/Dushnilin/zapret2-openwrt/releases/tag/" + tag;
|
||||
let version = replace(tag, /^v/, "");
|
||||
let base_dl = "https://github.com/Dushnilin/zapret2-openwrt/releases/download/" + resolved_tag + "/";
|
||||
let release_url = "https://github.com/Dushnilin/zapret2-openwrt/releases/tag/" + resolved_tag;
|
||||
let version = replace(resolved_tag, /^v/, "");
|
||||
|
||||
let candidate_list = split(arch.candidates, " ");
|
||||
for (let candidate in candidate_list) {
|
||||
@@ -1227,7 +1273,6 @@ let base_dl = "https://github.com/Dushnilin/zapret2-openwrt/releases/download/"
|
||||
let pkg_name = "zapret2_" + candidate + "." + asset_ext;
|
||||
let url = base_dl + pkg_name;
|
||||
if (url_exists(url)) {
|
||||
warn("EXISTS: " + url + "\n");
|
||||
return {
|
||||
arch: candidate,
|
||||
package_name: pkg_name,
|
||||
@@ -1373,7 +1418,7 @@ function install_zapret_like(component, action, runtime_module, resolve_fn, labe
|
||||
clear_version_caches();
|
||||
current_version = provider_package_version(runtime_module);
|
||||
if (current_version == "")
|
||||
current_version = "unknown";
|
||||
current_version = pkg.version || "unknown";
|
||||
action_success(component, action, label + " package has been installed", current_version, pkg.version, 1, "latest", release.release_url || "");
|
||||
}
|
||||
|
||||
@@ -1421,6 +1466,8 @@ function install_zapret2(action, target_tag) {
|
||||
if (index(hosts_content, "::1") < 0)
|
||||
command_success("printf '\\n::1 localhost ip6-localhost ip6-loopback\\n' >> /etc/hosts");
|
||||
|
||||
run_logged("Updating package lists before " + label + " package installation", pkg_list_update_command());
|
||||
|
||||
if (!run_logged("Installing " + label + " package " + pkg.name, pkg_install_files_command([ pkg.file ])))
|
||||
action_fail(component, action, "Failed to install " + label + " package", current_version, pkg.version, "", release.release_url || "");
|
||||
|
||||
@@ -1440,7 +1487,7 @@ function install_zapret2(action, target_tag) {
|
||||
clear_version_caches();
|
||||
current_version = provider_package_version(runtime_module);
|
||||
if (current_version == "")
|
||||
current_version = "unknown";
|
||||
current_version = pkg.version || "unknown";
|
||||
action_success(component, action, label + " package has been installed", current_version, pkg.version, 1, "latest", release.release_url || "");
|
||||
}
|
||||
|
||||
@@ -1469,6 +1516,9 @@ function install_byedpi(action, target_tag) {
|
||||
let pkg = download_direct_package(release);
|
||||
if (pkg == null)
|
||||
action_fail("byedpi", action, "Failed to download ByeDPI package");
|
||||
|
||||
run_logged("Updating package lists before ByeDPI package installation", pkg_list_update_command());
|
||||
|
||||
if (!run_logged("Installing ByeDPI package " + pkg.name, pkg_install_files_command([ pkg.file ])))
|
||||
action_fail("byedpi", action, "Failed to install ByeDPI package", current_version, pkg.version);
|
||||
|
||||
@@ -1477,7 +1527,7 @@ function install_byedpi(action, target_tag) {
|
||||
clear_version_caches();
|
||||
current_version = provider_package_version(runtime_module);
|
||||
if (current_version == "")
|
||||
current_version = "unknown";
|
||||
current_version = pkg.version || "unknown";
|
||||
action_success("byedpi", action, "ByeDPI package has been installed", current_version, pkg.version, 1, "latest", release.release_url || "");
|
||||
}
|
||||
|
||||
@@ -2017,9 +2067,10 @@ function install_sing_box_extended_package(action, target_tag) {
|
||||
let latest_version = normalize_sing_box_version(release.tag);
|
||||
|
||||
if (action == "check_update") {
|
||||
if (!sing_box_runtime_success("is-extended", [ current_version ]))
|
||||
action_fail("sing_box", action, "sing-box-extended is not installed", current_version, latest_version);
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
if (current_version == "" || !sing_box_runtime_success("is-extended", [ current_version ]))
|
||||
action_success("sing_box", action, "sing-box-extended is not installed", current_version, latest_version, 0, "", release.release_url);
|
||||
else
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
}
|
||||
|
||||
ensure_sing_box_dependencies();
|
||||
@@ -2028,8 +2079,7 @@ function install_sing_box_extended_package(action, target_tag) {
|
||||
if (!download_with_retry(release.asset_url, package_file, release.asset_name))
|
||||
action_fail("sing_box", action, "Failed to download sing-box-extended package", current_version, latest_version);
|
||||
|
||||
if (!run_logged("Updating package lists before sing-box-extended package installation", pkg_list_update_command()))
|
||||
action_fail("sing_box", action, "Failed to update package lists", current_version, latest_version);
|
||||
run_logged("Updating package lists before sing-box-extended package installation", pkg_list_update_command());
|
||||
|
||||
stop_tachyon_before_sing_box_change();
|
||||
prepare_sing_box_package_service_install();
|
||||
@@ -2133,11 +2183,10 @@ function install_sing_box_extended(action, compressed, target_tag) {
|
||||
let latest_version = normalize_sing_box_version(release.tag);
|
||||
|
||||
if (action == "check_update") {
|
||||
if (!sing_box_runtime_success("is-extended", [ current_version ]))
|
||||
action_fail("sing_box", action, "sing-box-extended is not installed", current_version, latest_version);
|
||||
if (!sing_box_runtime_success("marker-is", [ "extended-compressed" ]))
|
||||
action_fail("sing_box", action, "sing-box-extended compressed is not installed", current_version, latest_version);
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
if (current_version == "" || !sing_box_runtime_success("is-extended", [ current_version ]) || !sing_box_runtime_success("marker-is", [ "extended-compressed" ]))
|
||||
action_success("sing_box", action, label + " is not installed", current_version, latest_version, 0, "", release.release_url);
|
||||
else
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
}
|
||||
|
||||
ensure_sing_box_dependencies();
|
||||
@@ -2296,11 +2345,10 @@ function install_sing_box_lx(action, target_tag) {
|
||||
let latest_version = normalize_sing_box_version(release.tag);
|
||||
|
||||
if (action == "check_update") {
|
||||
if (!sing_box_runtime_success("is-lx", [ current_version ]))
|
||||
action_fail("sing_box", action, "sing-box-lx is not installed", current_version, latest_version);
|
||||
if (!sing_box_runtime_success("marker-is", [ "lx" ]))
|
||||
action_fail("sing_box", action, "sing-box-lx is not installed", current_version, latest_version);
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
if (current_version == "" || !sing_box_runtime_success("is-lx", [ current_version ]) || !sing_box_runtime_success("marker-is", [ "lx" ]))
|
||||
action_success("sing_box", action, label + " is not installed", current_version, latest_version, 0, "", release.release_url);
|
||||
else
|
||||
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
|
||||
}
|
||||
|
||||
ensure_sing_box_dependencies();
|
||||
@@ -2462,17 +2510,24 @@ function install_package_sing_box(action, tiny) {
|
||||
latest_version = installed_package_version(package_name);
|
||||
|
||||
if (action == "check_update") {
|
||||
if (latest_version == "") {
|
||||
let proxy_address = service_proxy_address();
|
||||
run_logged("Refreshing package index", pkg_list_update_command(proxy_address));
|
||||
latest_version = available_package_version(package_name);
|
||||
if (latest_version == "")
|
||||
latest_version = installed_package_version(package_name);
|
||||
}
|
||||
if (latest_version == "")
|
||||
action_fail("sing_box", action, "Failed to resolve " + (tiny ? "tiny" : "stable") + " sing-box package version", current_version);
|
||||
if (tiny && !sing_box_runtime_success("is-tiny", [ binary_version ]))
|
||||
action_fail("sing_box", action, "sing-box-tiny is not installed", current_version, latest_version);
|
||||
check_success("sing_box", current_version, latest_version, "");
|
||||
if (current_version == "" || (tiny && !sing_box_runtime_success("is-tiny", [ binary_version ])))
|
||||
action_success("sing_box", action, label + " is not installed", current_version, latest_version, 0, "", "");
|
||||
else
|
||||
check_success("sing_box", current_version, latest_version, "");
|
||||
}
|
||||
|
||||
ensure_sing_box_dependencies();
|
||||
|
||||
if (!run_logged("Updating package lists before " + package_name + " installation", pkg_list_update_command()))
|
||||
action_fail("sing_box", action, "Failed to update package lists", current_version, latest_version);
|
||||
run_logged("Updating package lists before " + package_name + " installation", pkg_list_update_command());
|
||||
latest_version = available_package_version(package_name);
|
||||
if (latest_version == "")
|
||||
latest_version = installed_package_version(package_name);
|
||||
@@ -3330,12 +3385,26 @@ function component_action(component, action, extra) {
|
||||
|
||||
let mode = ARGV[0] || "";
|
||||
|
||||
if (mode == "component-action")
|
||||
component_action(ARGV[1], ARGV[2], ARGV[3]);
|
||||
if (mode == "component-action") {
|
||||
try {
|
||||
component_action(ARGV[1], ARGV[2], ARGV[3]);
|
||||
} catch (e) {
|
||||
let err_str = as_string(e);
|
||||
updates_log("Unhandled component action error: " + err_str, "error");
|
||||
action_fail(ARGV[1] || "unknown", ARGV[2] || "unknown", "Unexpected error: " + err_str);
|
||||
}
|
||||
}
|
||||
else if (mode == "list-component-releases")
|
||||
list_component_releases(ARGV[1], ARGV[2]);
|
||||
else if (mode == "install-component-version")
|
||||
component_action(ARGV[1], "install_version", ARGV[2]);
|
||||
else if (mode == "install-component-version") {
|
||||
try {
|
||||
component_action(ARGV[1], "install_version", ARGV[2]);
|
||||
} catch (e) {
|
||||
let err_str = as_string(e);
|
||||
updates_log("Unhandled install component version error: " + err_str, "error");
|
||||
action_fail(ARGV[1] || "unknown", "install_version", "Unexpected error: " + err_str);
|
||||
}
|
||||
}
|
||||
else if (mode == "latest-tachyon-release-json")
|
||||
print(latest_tachyon_release_json());
|
||||
else if (mode == "latest-tachyon-version")
|
||||
|
||||
@@ -78,6 +78,14 @@ function array_or_empty(value) {
|
||||
return type(value) == "array" ? value : [];
|
||||
}
|
||||
|
||||
function releases_array_or_wrapped(value) {
|
||||
if (type(value) == "array")
|
||||
return value;
|
||||
if (type(value) == "object" && value != null)
|
||||
return [ value ];
|
||||
return [];
|
||||
}
|
||||
|
||||
function str_contains(haystack, needle) {
|
||||
return index(as_string(haystack), as_string(needle)) >= 0;
|
||||
}
|
||||
@@ -915,7 +923,7 @@ function release_asset_matches_arch(name, prefix, arch, ext) {
|
||||
}
|
||||
|
||||
function named_release_select_asset(release_prefix, asset_prefix, asset_ext, arch_candidates) {
|
||||
let releases = array_or_empty(read_stdin_json());
|
||||
let releases = releases_array_or_wrapped(read_stdin_json());
|
||||
let clean_prefix = trim(release_prefix);
|
||||
|
||||
for (let release in releases) {
|
||||
@@ -1006,7 +1014,7 @@ function select_byedpi_asset_from_release(release, asset_ext, arch_candidates) {
|
||||
}
|
||||
|
||||
function byedpi_select_asset(series, asset_ext, arch_candidates) {
|
||||
let releases = array_or_empty(read_stdin_json());
|
||||
let releases = releases_array_or_wrapped(read_stdin_json());
|
||||
|
||||
for (let pass = 0; pass < 2; pass++) {
|
||||
if (pass == 0 && as_string(series) == "")
|
||||
@@ -1015,9 +1023,9 @@ function byedpi_select_asset(series, asset_ext, arch_candidates) {
|
||||
for (let release in releases) {
|
||||
if (type(release) != "object")
|
||||
continue;
|
||||
if (release.draft === true || release.prerelease === true)
|
||||
if (release.draft === true || (length(releases) > 1 && release.prerelease === true))
|
||||
continue;
|
||||
if (pass == 0) {
|
||||
if (pass == 0 && length(releases) > 1) {
|
||||
let tag = as_string(release.tag_name || "");
|
||||
let name = as_string(release.name || "");
|
||||
if (!str_contains(tag, series) && !str_contains(name, series))
|
||||
@@ -1030,7 +1038,7 @@ function byedpi_select_asset(series, asset_ext, arch_candidates) {
|
||||
}
|
||||
|
||||
function sing_box_extended_release_tag() {
|
||||
for (let release in array_or_empty(read_stdin_json())) {
|
||||
for (let release in releases_array_or_wrapped(read_stdin_json())) {
|
||||
if (type(release) != "object")
|
||||
continue;
|
||||
if (release.draft === true || release.prerelease === true)
|
||||
@@ -1045,7 +1053,7 @@ function sing_box_extended_release_tag() {
|
||||
}
|
||||
|
||||
function sing_box_lx_release_tag() {
|
||||
for (let release in array_or_empty(read_stdin_json())) {
|
||||
for (let release in releases_array_or_wrapped(read_stdin_json())) {
|
||||
if (type(release) != "object")
|
||||
continue;
|
||||
if (release.draft === true || release.prerelease === true)
|
||||
@@ -1067,17 +1075,73 @@ function text_first_chars(value, max_chars) {
|
||||
|
||||
function file_last_nonblank_line(path, fallback, max_chars) {
|
||||
let data = fs.readfile(path);
|
||||
let result = "";
|
||||
|
||||
if (data != null) {
|
||||
for (let line in split(as_string(data), "\n"))
|
||||
if (match(line, /^[[:space:]]*$/) == null)
|
||||
result = line;
|
||||
if (data == null || trim(as_string(data)) == "") {
|
||||
print(text_first_chars(fallback, max_chars), "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == "")
|
||||
result = as_string(fallback);
|
||||
let raw_lines = split(as_string(data), /\r?\n/);
|
||||
let lines = [];
|
||||
for (let l in raw_lines) {
|
||||
let t = trim(l);
|
||||
if (t != "")
|
||||
push(lines, t);
|
||||
}
|
||||
|
||||
if (length(lines) == 0) {
|
||||
print(text_first_chars(fallback, max_chars), "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Detect ucode runtime/syntax exception
|
||||
let first_line = lines[0];
|
||||
let lower_first = lc(first_line);
|
||||
if (index(lower_first, "type error:") == 0 ||
|
||||
index(lower_first, "syntax error:") == 0 ||
|
||||
index(lower_first, "reference error:") == 0 ||
|
||||
index(lower_first, "runtime error:") == 0 ||
|
||||
index(lower_first, "unhandled exception:") == 0 ||
|
||||
index(lower_first, "error:") == 0) {
|
||||
let loc = "";
|
||||
if (length(lines) > 1 && (index(lines[1], "In ") == 0 || index(lines[1], "in ") == 0)) {
|
||||
let m = match(lines[1], /([a-zA-Z0-9_.-]+),[ \t]*line[ \t]*([0-9]+)/);
|
||||
if (m)
|
||||
loc = " (" + m[1] + ":" + m[2] + ")";
|
||||
}
|
||||
let msg = first_line + loc;
|
||||
print(text_first_chars(msg, max_chars), "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Discard trailing pointer / caret / delimiter lines
|
||||
while (length(lines) > 0) {
|
||||
let last = lines[length(lines) - 1];
|
||||
if (index(last, "Near here") >= 0 || match(last, /^[ \t\-_^]*$/) || match(last, /^[-=~_]{4,}$/)) {
|
||||
pop(lines);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (length(lines) == 0) {
|
||||
print(text_first_chars(fallback, max_chars), "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Check if any line has an explicit error prefix
|
||||
for (let l in lines) {
|
||||
let lower_l = lc(l);
|
||||
if (index(lower_l, "error:") >= 0 ||
|
||||
index(lower_l, "fatal:") >= 0 ||
|
||||
index(lower_l, "failed:") >= 0 ||
|
||||
index(lower_l, "failure:") >= 0) {
|
||||
print(text_first_chars(l, max_chars), "\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fallback to the last non-empty line
|
||||
let result = lines[length(lines) - 1];
|
||||
print(text_first_chars(result, max_chars), "\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -230,17 +230,65 @@ function text_first_chars(value, max_chars) {
|
||||
|
||||
function file_last_nonblank_line_value(path, fallback, max_chars) {
|
||||
let data = fs.readfile(path);
|
||||
let result = "";
|
||||
if (data == null || trim(as_string(data)) == "")
|
||||
return text_first_chars(fallback, max_chars);
|
||||
|
||||
if (data != null) {
|
||||
for (let line in split(as_string(data), "\n"))
|
||||
if (match(line, /^[[:space:]]*$/) == null)
|
||||
result = line;
|
||||
let raw_lines = split(as_string(data), /\r?\n/);
|
||||
let lines = [];
|
||||
for (let l in raw_lines) {
|
||||
let t = trim(l);
|
||||
if (t != "")
|
||||
push(lines, t);
|
||||
}
|
||||
|
||||
if (result == "")
|
||||
result = as_string(fallback);
|
||||
if (length(lines) == 0)
|
||||
return text_first_chars(fallback, max_chars);
|
||||
|
||||
// 1. Detect ucode runtime/syntax exception
|
||||
let first_line = lines[0];
|
||||
let lower_first = lc(first_line);
|
||||
if (index(lower_first, "type error:") == 0 ||
|
||||
index(lower_first, "syntax error:") == 0 ||
|
||||
index(lower_first, "reference error:") == 0 ||
|
||||
index(lower_first, "runtime error:") == 0 ||
|
||||
index(lower_first, "unhandled exception:") == 0 ||
|
||||
index(lower_first, "error:") == 0) {
|
||||
let loc = "";
|
||||
if (length(lines) > 1 && (index(lines[1], "In ") == 0 || index(lines[1], "in ") == 0)) {
|
||||
let m = match(lines[1], /([a-zA-Z0-9_.-]+),[ \t]*line[ \t]*([0-9]+)/);
|
||||
if (m)
|
||||
loc = " (" + m[1] + ":" + m[2] + ")";
|
||||
}
|
||||
let msg = first_line + loc;
|
||||
return text_first_chars(msg, max_chars);
|
||||
}
|
||||
|
||||
// 2. Discard trailing pointer / caret / delimiter lines
|
||||
while (length(lines) > 0) {
|
||||
let last = lines[length(lines) - 1];
|
||||
if (index(last, "Near here") >= 0 || match(last, /^[ \t\-_^]*$/) || match(last, /^[-=~_]{4,}$/)) {
|
||||
pop(lines);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (length(lines) == 0)
|
||||
return text_first_chars(fallback, max_chars);
|
||||
|
||||
// 3. Check if any line has an explicit error prefix
|
||||
for (let l in lines) {
|
||||
let lower_l = lc(l);
|
||||
if (index(lower_l, "error:") >= 0 ||
|
||||
index(lower_l, "fatal:") >= 0 ||
|
||||
index(lower_l, "failed:") >= 0 ||
|
||||
index(lower_l, "failure:") >= 0) {
|
||||
return text_first_chars(l, max_chars);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fallback to the last non-empty line
|
||||
let result = lines[length(lines) - 1];
|
||||
return text_first_chars(result, max_chars);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ let uci_core = require("core.uci");
|
||||
let as_string = common.as_string;
|
||||
let object_or_empty = common.object_or_empty;
|
||||
let bool_value_common = common.bool_value;
|
||||
let remove_file = common.remove_file;
|
||||
|
||||
function bool_value(value, fallback) {
|
||||
if (value == null || value == "")
|
||||
@@ -1372,12 +1373,10 @@ function cascade_delete_section(cursor, config_name, parent_name) {
|
||||
|
||||
// 6. Delete section cache files
|
||||
try {
|
||||
command_success_from_args([ "rm", "-f",
|
||||
"/var/run/tachyon/section-cache/" + parent_name + ".json",
|
||||
"/etc/tachyon/subscription-cache/" + parent_name + ".json",
|
||||
"/etc/tachyon/subscription-cache/" + parent_name + ".yaml",
|
||||
"/etc/tachyon/subscription-cache/" + parent_name + ".txt"
|
||||
]);
|
||||
remove_file("/var/run/tachyon/section-cache/" + parent_name + ".json");
|
||||
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".json");
|
||||
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".yaml");
|
||||
remove_file("/etc/tachyon/subscription-cache/" + parent_name + ".txt");
|
||||
} catch (e) {}
|
||||
|
||||
return removed;
|
||||
|
||||
@@ -20,22 +20,36 @@ function command_exists(name) {
|
||||
return system(command_from_args([ "command", "-v", name ]) + " >/dev/null 2>&1") == 0;
|
||||
}
|
||||
|
||||
function binary_installed(package_name) {
|
||||
if (package_name == "zapret2")
|
||||
return fs.stat("/opt/zapret2/nfq2/nfqws2") != null || fs.stat("/opt/zapret2/nfqws2") != null || fs.stat("/usr/bin/nfqws2") != null;
|
||||
if (package_name == "zapret")
|
||||
return fs.stat("/opt/zapret/nfq/nfqws") != null || fs.stat("/opt/zapret/nfqws") != null;
|
||||
if (package_name == "byedpi")
|
||||
return fs.stat("/usr/bin/ciadpi") != null;
|
||||
if (package_name == "sing-box" || package_name == "sing-box-extended" || package_name == "sing-box-lx")
|
||||
return fs.stat("/usr/bin/sing-box") != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
function apk_installed(package_name) {
|
||||
return command_exists("apk") && command_success([ "apk", "info", "-e", as_string(package_name) ]);
|
||||
package_name = as_string(package_name);
|
||||
if (command_exists("apk") && command_success([ "apk", "info", "-e", package_name ]))
|
||||
return true;
|
||||
return binary_installed(package_name);
|
||||
}
|
||||
|
||||
function opkg_installed(package_name) {
|
||||
package_name = as_string(package_name);
|
||||
if (!command_exists("opkg"))
|
||||
return false;
|
||||
|
||||
let prefix = package_name + " - ";
|
||||
for (let line in split(command_output_args([ "opkg", "list-installed" ]), "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (substr(line, 0, length(prefix)) == prefix)
|
||||
return true;
|
||||
if (command_exists("opkg")) {
|
||||
let prefix = package_name + " - ";
|
||||
for (let line in split(command_output_args([ "opkg", "list-installed" ]), "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (substr(line, 0, length(prefix)) == prefix)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return binary_installed(package_name);
|
||||
}
|
||||
|
||||
function installed(package_name) {
|
||||
@@ -113,6 +127,38 @@ function apk_query_version(package_name, output) {
|
||||
return "";
|
||||
}
|
||||
|
||||
function binary_version(package_name) {
|
||||
if (package_name == "zapret2") {
|
||||
for (let p in [ "/opt/zapret2/nfq2/nfqws2", "/opt/zapret2/nfqws2", "/usr/bin/nfqws2" ]) {
|
||||
if (fs.stat(p) != null) {
|
||||
let out = command_output_args([ p, "--version" ]);
|
||||
let m = match(out, /version[ \t]*([0-9a-zA-Z._-]+)/i);
|
||||
if (m) return m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (package_name == "zapret") {
|
||||
for (let p in [ "/opt/zapret/nfq/nfqws", "/opt/zapret/nfqws" ]) {
|
||||
if (fs.stat(p) != null) {
|
||||
let out = command_output_args([ p, "--version" ]);
|
||||
let m = match(out, /version[ \t]*([0-9a-zA-Z._-]+)/i);
|
||||
if (m) return m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (package_name == "byedpi" && fs.stat("/usr/bin/ciadpi") != null) {
|
||||
let out = command_output_args([ "/usr/bin/ciadpi", "--version" ]);
|
||||
let m = match(out, /([0-9a-zA-Z._-]+)/);
|
||||
if (m) return m[1];
|
||||
}
|
||||
if ((package_name == "sing-box" || package_name == "sing-box-extended" || package_name == "sing-box-lx") && fs.stat("/usr/bin/sing-box") != null) {
|
||||
let out = command_output_args([ "/usr/bin/sing-box", "version" ]);
|
||||
let m = match(out, /version[ \t]*([0-9a-zA-Z._-]+)/i);
|
||||
if (m) return m[1];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function apk_version(package_name) {
|
||||
package_name = as_string(package_name);
|
||||
if (!apk_installed(package_name))
|
||||
@@ -130,7 +176,11 @@ function apk_version(package_name) {
|
||||
if (version != "")
|
||||
return version;
|
||||
|
||||
return apk_manifest_version(package_name, command_output_args([ "apk", "list", "--installed", "--manifest" ]));
|
||||
version = apk_manifest_version(package_name, command_output_args([ "apk", "list", "--installed", "--manifest" ]));
|
||||
if (version != "")
|
||||
return version;
|
||||
|
||||
return binary_version(package_name);
|
||||
}
|
||||
|
||||
function apk_available_version(package_name) {
|
||||
@@ -145,16 +195,15 @@ function apk_available_version(package_name) {
|
||||
|
||||
function opkg_version(package_name) {
|
||||
package_name = as_string(package_name);
|
||||
if (!command_exists("opkg"))
|
||||
return "";
|
||||
|
||||
let prefix = package_name + " - ";
|
||||
for (let line in split(command_output_args([ "opkg", "list-installed" ]), "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (substr(line, 0, length(prefix)) == prefix)
|
||||
return substr(line, length(prefix));
|
||||
if (command_exists("opkg")) {
|
||||
let prefix = package_name + " - ";
|
||||
for (let line in split(command_output_args([ "opkg", "list-installed" ]), "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (substr(line, 0, length(prefix)) == prefix)
|
||||
return substr(line, length(prefix));
|
||||
}
|
||||
}
|
||||
return "";
|
||||
return binary_version(package_name);
|
||||
}
|
||||
|
||||
function version(package_name) {
|
||||
|
||||
@@ -1969,7 +1969,7 @@ function global_check(arg1, arg2) {
|
||||
print_global("❌ Failed to get DNS info");
|
||||
|
||||
print_global("━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||
print_global("ЁЯУж Sing-box status");
|
||||
print_global("═══ Sing-box status");
|
||||
let singbox_check_json = command_capture(command_from_args(module_args(LIB_DIR + "/diagnostics/runtime.uc", [ "check-sing-box" ]))).output;
|
||||
if (singbox_check_json != "")
|
||||
render_or_fail([ "global-sing-box-check" ], singbox_check_json, "❌ Failed to parse sing-box info", [ 0 ]);
|
||||
|
||||
@@ -2359,7 +2359,9 @@ function nft_rule_signature_body(body, section) {
|
||||
function nft_schedule_signature_body(body, schedule) {
|
||||
let name = as_string(schedule[".name"]);
|
||||
body = signature_add_value(body, "schedule." + name + ".enabled", bool_option(schedule, "enabled", true) ? "1" : "0");
|
||||
body = signature_add_value(body, "schedule." + name + ".device_ip", option(schedule, "device_ip", ""));
|
||||
let dev_ips = join(",", list_option(schedule, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(schedule, "device_ip", "");
|
||||
body = signature_add_value(body, "schedule." + name + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, "schedule." + name + ".profile", join(",", list_option(schedule, "profile")));
|
||||
body = signature_add_value(body, "schedule." + name + ".target", option(schedule, "target", "all"));
|
||||
body = signature_add_value(body, "schedule." + name + ".sections", join(",", list_option(schedule, "sections")));
|
||||
@@ -2375,7 +2377,9 @@ function nft_schedule_signature_body(body, schedule) {
|
||||
function nft_profile_signature_body(body, profile) {
|
||||
let name = as_string(profile[".name"]);
|
||||
body = signature_add_value(body, "profile." + name + ".enabled", bool_option(profile, "enabled", true) ? "1" : "0");
|
||||
body = signature_add_value(body, "profile." + name + ".device_ip", join(",", list_option(profile, "device_ip")));
|
||||
let dev_ips = join(",", list_option(profile, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(profile, "device_ip", "");
|
||||
body = signature_add_value(body, "profile." + name + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, "profile." + name + ".safe_search", option(profile, "safe_search", "0"));
|
||||
body = signature_add_value(body, "profile." + name + ".block_doh", option(profile, "block_doh", "0"));
|
||||
body = signature_add_value(body, "profile." + name + ".blocked_domains", join(",", list_option(profile, "blocked_domains")));
|
||||
|
||||
@@ -131,6 +131,13 @@ function package_version() {
|
||||
let output = "";
|
||||
|
||||
if (command_exists("apk") && command_success_from_args([ "apk", "info", "-e", "byedpi" ])) {
|
||||
output = command_output_from_args([ "apk", "list", "--installed", "byedpi" ]);
|
||||
for (let line in split(output, "\n")) {
|
||||
let matched = match(trim(as_string(line)), /^byedpi-([^ \t]+)/);
|
||||
if (matched)
|
||||
return matched[1];
|
||||
}
|
||||
|
||||
output = command_output_from_args([ "apk", "list", "--installed", "--manifest", "byedpi" ]);
|
||||
for (let line in split(output, "\n")) {
|
||||
let matched = match(trim(as_string(line)), /^P:byedpi$/);
|
||||
|
||||
@@ -10,6 +10,7 @@ let shell_quote = common.shell_quote;
|
||||
let read_json_file = common.read_json_file;
|
||||
let read_stdin = common.read_stdin;
|
||||
let read_stdin_json = common.read_stdin_json;
|
||||
let write_json = common.write_json;
|
||||
|
||||
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ let command_from_args = common.command_from_args;
|
||||
let command_status = common.command_status;
|
||||
let command_success_from_args = common.command_success_from_args;
|
||||
let command_output_from_args = common.command_output_from_args;
|
||||
let write_json = common.write_json;
|
||||
|
||||
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
||||
const TACHYON_BIN = getenv("TACHYON_BIN") || "/usr/bin/tachyon";
|
||||
|
||||
@@ -928,7 +928,53 @@ function section_rule_condition_csv(section, key, kind) {
|
||||
);
|
||||
}
|
||||
|
||||
function nft_runtime_signature_body(settings, sections) {
|
||||
function nft_schedule_signature_body(body, schedule) {
|
||||
let name = as_string(schedule[".name"]);
|
||||
body = signature_add_value(body, "schedule." + name + ".enabled", bool_option(schedule, "enabled", true) ? "1" : "0");
|
||||
let dev_ips = join(",", list_option(schedule, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(schedule, "device_ip", "");
|
||||
body = signature_add_value(body, "schedule." + name + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, "schedule." + name + ".profile", join(",", list_option(schedule, "profile")));
|
||||
body = signature_add_value(body, "schedule." + name + ".target", option(schedule, "target", "all"));
|
||||
body = signature_add_value(body, "schedule." + name + ".sections", join(",", list_option(schedule, "sections")));
|
||||
body = signature_add_value(body, "schedule." + name + ".action", option(schedule, "action", "block"));
|
||||
body = signature_add_value(body, "schedule." + name + ".start_time", option(schedule, "start_time", ""));
|
||||
body = signature_add_value(body, "schedule." + name + ".end_time", option(schedule, "end_time", ""));
|
||||
body = signature_add_value(body, "schedule." + name + ".days", join(",", list_option(schedule, "days")));
|
||||
body = signature_add_value(body, "schedule." + name + ".blocked_domains", join(",", list_option(schedule, "blocked_domains")));
|
||||
body = signature_add_value(body, "schedule." + name + ".mode", option(schedule, "mode", "block"));
|
||||
return body;
|
||||
}
|
||||
|
||||
function nft_profile_signature_body(body, profile) {
|
||||
let name = as_string(profile[".name"]);
|
||||
body = signature_add_value(body, "profile." + name + ".enabled", bool_option(profile, "enabled", true) ? "1" : "0");
|
||||
let dev_ips = join(",", list_option(profile, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(profile, "device_ip", "");
|
||||
body = signature_add_value(body, "profile." + name + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, "profile." + name + ".safe_search", option(profile, "safe_search", "0"));
|
||||
body = signature_add_value(body, "profile." + name + ".block_doh", option(profile, "block_doh", "0"));
|
||||
body = signature_add_value(body, "profile." + name + ".blocked_domains", join(",", list_option(profile, "blocked_domains")));
|
||||
body = signature_add_value(body, "profile." + name + ".daily_quota_minutes", option(profile, "daily_quota_minutes", "0"));
|
||||
return body;
|
||||
}
|
||||
|
||||
function nft_guest_mode_signature_body(body, guest_mode) {
|
||||
if (!guest_mode) return body;
|
||||
let name = as_string(guest_mode[".name"] || "guest_mode");
|
||||
body = signature_add_value(body, "guest_mode." + name + ".enabled", bool_option(guest_mode, "enabled", false) ? "1" : "0");
|
||||
body = signature_add_value(body, "guest_mode." + name + ".mode", option(guest_mode, "mode", "selected"));
|
||||
body = signature_add_value(body, "guest_mode." + name + ".guest_devices", join(",", list_option(guest_mode, "guest_devices")));
|
||||
body = signature_add_value(body, "guest_mode." + name + ".trusted_devices", join(",", list_option(guest_mode, "trusted_devices")));
|
||||
body = signature_add_value(body, "guest_mode." + name + ".isolate_lan", bool_option(guest_mode, "isolate_lan", true) ? "1" : "0");
|
||||
body = signature_add_value(body, "guest_mode." + name + ".block_router_admin", bool_option(guest_mode, "block_router_admin", true) ? "1" : "0");
|
||||
body = signature_add_value(body, "guest_mode." + name + ".start_time", option(guest_mode, "start_time", ""));
|
||||
body = signature_add_value(body, "guest_mode." + name + ".end_time", option(guest_mode, "end_time", ""));
|
||||
body = signature_add_value(body, "guest_mode." + name + ".days", join(",", list_option(guest_mode, "days")));
|
||||
return body;
|
||||
}
|
||||
|
||||
function nft_runtime_signature_body(settings, sections, schedules, profiles, guest_modes) {
|
||||
let body = "";
|
||||
|
||||
body = signature_add_value(body, "settings.source_network_interfaces", option(settings, "source_network_interfaces", "br-lan"));
|
||||
@@ -963,7 +1009,8 @@ function nft_runtime_signature_body(settings, sections) {
|
||||
body = signature_add_value(body, "rule." + name + ".excluded_ips", option(section, "excluded_ips", ""));
|
||||
body = signature_add_value(body, "rule." + name + ".excluded_protocol", option(section, "excluded_protocol", ""));
|
||||
body = signature_add_value(body, "rule." + name + ".protocol", option(section, "protocol", ""));
|
||||
body = signature_add_value(body, "rule." + name + ".community_subnet_lists", rule_config.filter_community_subnet_lists_value(connections.community_lists_value(section)));
|
||||
let comm_subnets = bool_option(section, "community_subnets", true) ? rule_config.filter_community_subnet_lists_value(connections.community_lists_value(section)) : "";
|
||||
body = signature_add_value(body, "rule." + name + ".community_subnet_lists", comm_subnets);
|
||||
|
||||
body = signature_add_value(body, "rule." + name + ".remote_subnet_lists", option(section, "remote_subnet_lists", ""));
|
||||
body = signature_add_value(body, "rule." + name + ".rule_set_with_subnets", connections.rule_sets_with_subnets_value(section));
|
||||
@@ -971,6 +1018,17 @@ function nft_runtime_signature_body(settings, sections) {
|
||||
body = signature_add_value(body, "rule." + name + ".dscp", connections.dscp_value(section));
|
||||
}
|
||||
|
||||
for (let profile in profiles)
|
||||
body = nft_profile_signature_body(body, object_or_empty(profile));
|
||||
|
||||
for (let schedule in schedules)
|
||||
body = nft_schedule_signature_body(body, object_or_empty(schedule));
|
||||
|
||||
if (guest_modes) {
|
||||
for (let gm in guest_modes)
|
||||
body = nft_guest_mode_signature_body(body, object_or_empty(gm));
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -1501,7 +1559,43 @@ function append_sing_box_server_signature_body(body, server) {
|
||||
return body;
|
||||
}
|
||||
|
||||
function sing_box_signature_body(settings, sections, servers, mwan3_active) {
|
||||
function append_sing_box_profile_signature_body(body, profile) {
|
||||
let name = section_name(profile);
|
||||
if (name == "") return body;
|
||||
let prefix = "sing_box.profile." + name;
|
||||
let enabled = bool_option(profile, "enabled", true) ? "1" : "0";
|
||||
body = signature_add_value(body, prefix + ".enabled", enabled);
|
||||
let dev_ips = join(",", list_option(profile, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(profile, "device_ip", "");
|
||||
body = signature_add_value(body, prefix + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, prefix + ".safe_search", option(profile, "safe_search", "0"));
|
||||
body = signature_add_value(body, prefix + ".block_doh", option(profile, "block_doh", "0"));
|
||||
body = signature_add_value(body, prefix + ".blocked_domains", join(",", list_option(profile, "blocked_domains")));
|
||||
return body;
|
||||
}
|
||||
|
||||
function append_sing_box_schedule_signature_body(body, schedule) {
|
||||
let name = section_name(schedule);
|
||||
if (name == "") return body;
|
||||
let prefix = "sing_box.schedule." + name;
|
||||
let enabled = bool_option(schedule, "enabled", true) ? "1" : "0";
|
||||
body = signature_add_value(body, prefix + ".enabled", enabled);
|
||||
let dev_ips = join(",", list_option(schedule, "device_ip"));
|
||||
if (dev_ips == "") dev_ips = option(schedule, "device_ip", "");
|
||||
body = signature_add_value(body, prefix + ".device_ip", dev_ips);
|
||||
body = signature_add_value(body, prefix + ".profile", join(",", list_option(schedule, "profile")));
|
||||
body = signature_add_value(body, prefix + ".blocked_domains", join(",", list_option(schedule, "blocked_domains")));
|
||||
body = signature_add_value(body, prefix + ".mode", option(schedule, "mode", "block"));
|
||||
body = signature_add_value(body, prefix + ".target", option(schedule, "target", "all"));
|
||||
body = signature_add_value(body, prefix + ".sections", join(",", list_option(schedule, "sections")));
|
||||
body = signature_add_value(body, prefix + ".action", option(schedule, "action", "block"));
|
||||
body = signature_add_value(body, prefix + ".start_time", option(schedule, "start_time", ""));
|
||||
body = signature_add_value(body, prefix + ".end_time", option(schedule, "end_time", ""));
|
||||
body = signature_add_value(body, prefix + ".days", join(",", list_option(schedule, "days")));
|
||||
return body;
|
||||
}
|
||||
|
||||
function sing_box_signature_body(settings, sections, servers, mwan3_active, schedules, profiles) {
|
||||
settings = object_or_empty(settings);
|
||||
let body = "";
|
||||
|
||||
@@ -1557,6 +1651,12 @@ function sing_box_signature_body(settings, sections, servers, mwan3_active) {
|
||||
for (let server in servers)
|
||||
body = append_sing_box_server_signature_body(body, object_or_empty(server));
|
||||
|
||||
for (let profile in profiles)
|
||||
body = append_sing_box_profile_signature_body(body, object_or_empty(profile));
|
||||
|
||||
for (let schedule in schedules)
|
||||
body = append_sing_box_schedule_signature_body(body, object_or_empty(schedule));
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -1681,13 +1781,13 @@ function byedpi_runtime_signature_body(sections) {
|
||||
return body;
|
||||
}
|
||||
|
||||
function reload_state_values_from_sources(format, settings, sections, servers, dnsmasq, legacy_dnsmasq_present, mwan3_active_value) {
|
||||
function reload_state_values_from_sources(format, settings, sections, servers, dnsmasq, legacy_dnsmasq_present, mwan3_active_value, schedules, profiles, guest_modes) {
|
||||
return {
|
||||
format: as_string(format),
|
||||
service_trigger_signature: signature_hash(service_trigger_signature_body(settings)),
|
||||
dnsmasq_signature: signature_hash(dnsmasq_signature_body(settings, dnsmasq, legacy_dnsmasq_present)),
|
||||
sing_box_signature: signature_hash(sing_box_signature_body(settings, sections, servers, mwan3_active_value)),
|
||||
nft_signature: signature_hash(nft_runtime_signature_body(settings, sections)),
|
||||
sing_box_signature: signature_hash(sing_box_signature_body(settings, sections, servers, mwan3_active_value, schedules, profiles)),
|
||||
nft_signature: signature_hash(nft_runtime_signature_body(settings, sections, schedules, profiles, guest_modes)),
|
||||
zapret_queue_signature: signature_hash(action_queue_signature_body(sections, "zapret", "zapret_queue.section")),
|
||||
zapret_runtime_signature: signature_hash(zapret_runtime_signature_body(sections)),
|
||||
zapret2_queue_signature: signature_hash(action_queue_signature_body(sections, "zapret2", "zapret2_queue.section")),
|
||||
@@ -1879,7 +1979,10 @@ function current_reload_state_values(format) {
|
||||
uci_servers(),
|
||||
uci_dnsmasq(),
|
||||
uci_exists("dhcp.tachyon"),
|
||||
mwan3_active()
|
||||
mwan3_active(),
|
||||
uci_sections("schedule"),
|
||||
uci_sections("profile"),
|
||||
uci_sections("guest_mode")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1927,7 +2030,10 @@ function fixture_reload_state_values(data, format) {
|
||||
fixture_servers(data),
|
||||
fixture_dnsmasq(data),
|
||||
fixture_legacy_dnsmasq_present(data),
|
||||
fixture_mwan3_active(data)
|
||||
fixture_mwan3_active(data),
|
||||
fixture_section_list(data, "schedule"),
|
||||
fixture_section_list(data, "profile"),
|
||||
fixture_section_list(data, "guest_mode")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2038,20 +2144,20 @@ else if (mode == "dnsmasq-signature-fixture") {
|
||||
exit(print_signature_hash(dnsmasq_signature_body(fixture_settings(data), fixture_dnsmasq(data), fixture_legacy_dnsmasq_present(data))) ? 0 : 1);
|
||||
}
|
||||
else if (mode == "sing-box-signature")
|
||||
exit(print_signature_hash(sing_box_signature_body(uci_settings(), uci_sections("section"), uci_servers(), mwan3_active())) ? 0 : 1);
|
||||
exit(print_signature_hash(sing_box_signature_body(uci_settings(), uci_sections("section"), uci_servers(), mwan3_active(), uci_sections("schedule"), uci_sections("profile"))) ? 0 : 1);
|
||||
else if (mode == "sing-box-signature-fixture") {
|
||||
let data = fixture_data(ARGV[1]);
|
||||
exit(print_signature_hash(sing_box_signature_body(fixture_settings(data), fixture_section_list(data), fixture_servers(data), fixture_mwan3_active(data))) ? 0 : 1);
|
||||
exit(print_signature_hash(sing_box_signature_body(fixture_settings(data), fixture_section_list(data), fixture_servers(data), fixture_mwan3_active(data), fixture_section_list(data, "schedule"), fixture_section_list(data, "profile"))) ? 0 : 1);
|
||||
}
|
||||
else if (mode == "sing-box-signature-body-fixture") {
|
||||
let data = fixture_data(ARGV[1]);
|
||||
print(sing_box_signature_body(fixture_settings(data), fixture_section_list(data), fixture_servers(data), fixture_mwan3_active(data)));
|
||||
print(sing_box_signature_body(fixture_settings(data), fixture_section_list(data), fixture_servers(data), fixture_mwan3_active(data), fixture_section_list(data, "schedule"), fixture_section_list(data, "profile")));
|
||||
}
|
||||
else if (mode == "nft-signature")
|
||||
exit(print_signature_hash(nft_runtime_signature_body(uci_settings(), uci_sections("section"))) ? 0 : 1);
|
||||
exit(print_signature_hash(nft_runtime_signature_body(uci_settings(), uci_sections("section"), uci_sections("schedule"), uci_sections("profile"), uci_sections("guest_mode"))) ? 0 : 1);
|
||||
else if (mode == "nft-signature-fixture") {
|
||||
let data = fixture_data(ARGV[1]);
|
||||
exit(print_signature_hash(nft_runtime_signature_body(fixture_settings(data), fixture_section_list(data))) ? 0 : 1);
|
||||
exit(print_signature_hash(nft_runtime_signature_body(fixture_settings(data), fixture_section_list(data), fixture_section_list(data, "schedule"), fixture_section_list(data, "profile"), fixture_section_list(data, "guest_mode"))) ? 0 : 1);
|
||||
}
|
||||
else if (mode == "zapret-queue-signature")
|
||||
exit(print_signature_hash(action_queue_signature_body(uci_sections("section"), "zapret", "zapret_queue.section")) ? 0 : 1);
|
||||
|
||||
Reference in New Issue
Block a user