release: 1.3.26 — Watchdog proxy restart protection, WAN monitor debounce, and subscription bootstrap resilience

- Fix #52: Watchdog proxy restart on single URL failure:
  * Validate failure against secondary independent probe URL before declaring proxy down
  * Skip restart when direct WAN is also degraded or down (!direct_ok)
  * Implement 120s cooldown between automated proxy restarts
  * Fix Telegram notification to accurately state 'Перезапуск sing-box' instead of 'Перезапуск роутера'
- Fix #51: Telegram bot spamming WAN up/down notifications:
  * Filter hotplug events strictly by badwan_monitored_interfaces (defaults to wan)
  * Add atomic directory lock with debounce delay and real ubus state query
  * Prevent repeated notifications when interface state has not transitioned
- Feat: 2-VPN deadlock elimination & self-download subscription proxy:
  * Isolate unbootstrapped dependent subscription sections on initial boot (deferred_sections)
  * Allow self-download proxy target for subscriptions with explicit UI hint '(текущее правило при активности)'
  * Enable automatic local proxy fallback for subscription refresh when direct WAN download fails while sing-box is active
  * Add regression test in tests/subscription_bootstrap_deadlock.sh
- Fix: Parental control quota enforcement:
  * Correct route rule filtering when daily quota minutes are exhausted without a fixed time interval
- i18n & Version bump:
  * Bump fe-app-tachyon to 1.3.26 and update Russian PO/POT translations
This commit is contained in:
Dushnilin
2026-09-12 16:50:04 +03:00
parent afd7e270ce
commit 2019ddd12a
25 changed files with 391 additions and 74 deletions
@@ -559,11 +559,26 @@ function controller(bus, opts) {
let started = time();
let ok = command_success_from_args([
"curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
"curl", "-s", "-o", "/dev/null",
"--connect-timeout", "3", "--max-time", "5",
"--proxy", "http://" + host + ":" + port,
check_url
]);
// Fallback confirmation: never declare proxy down based on a single URL.
// Cloudflare or specific CDN endpoints can encounter rate limits or transient DPI packet loss.
let fallback_url = null;
if (!ok) {
fallback_url = index(check_url, "gstatic.com") >= 0
? "https://detectportal.firefox.com/success.txt"
: "https://connectivitycheck.gstatic.com/generate_204";
ok = command_success_from_args([
"curl", "-s", "-o", "/dev/null",
"--connect-timeout", "3", "--max-time", "5",
"--proxy", "http://" + host + ":" + port,
fallback_url
]);
}
let elapsed = (time() - started) * 1000;
state.proxy_latency_history = push_history(state.proxy_latency_history,
@@ -579,13 +594,18 @@ function controller(bus, opts) {
// Distinguish "proxy is broken" from "the whole uplink is down": only
// the former is worth restarting sing-box over.
// Use the same URL as the proxy check so the two measurements are
// comparable: if the user changed ai_proxy_health_url, direct_ok
// must probe the same target, not always fall back to Cloudflare.
// Check primary URL, and if that fails, check fallback URL directly too
// in case the primary URL is blocked directly on the ISP WAN.
let direct_ok = command_success_from_args([
"curl", "-s", "-I", "--connect-timeout", "3", "--max-time", "5",
check_url
]);
if (!direct_ok && fallback_url != null) {
direct_ok = command_success_from_args([
"curl", "-s", "-I", "--connect-timeout", "3", "--max-time", "5",
fallback_url
]);
}
bus.emit(EV.PROXY_DOWN, {
host: host,