fix(migration): seamless zero-defect migration from NetShift, Forkop, and Podkop
This commit is contained in:
@@ -399,11 +399,32 @@ EOF
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
[ -n "${IPKG_INSTROOT}" ] && exit 0
|
[ -n "${IPKG_INSTROOT}" ] && exit 0
|
||||||
|
|
||||||
|
# Stop and disable legacy forkop / podkop / netshift services to prevent boot conflicts
|
||||||
|
for legacy_svc in podkop forkop netshift podkop_plus forkop_plus; do
|
||||||
|
if [ -f "/etc/init.d/${legacy_svc}" ]; then
|
||||||
|
"/etc/init.d/${legacy_svc}" stop >/dev/null 2>&1 || true
|
||||||
|
"/etc/init.d/${legacy_svc}" disable >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* /etc/rc.d/*netshift* 2>/dev/null || true
|
||||||
|
nft delete table inet podkop >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet PodkopTable >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet forkop >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet ForkopTable >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet netshift >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet NetShiftTable >/dev/null 2>&1 || true
|
||||||
|
|
||||||
rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* \
|
rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* \
|
||||||
/usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json \
|
/usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json \
|
||||||
/etc/uci-defaults/50_luci-forkop 2>/dev/null || true
|
/etc/uci-defaults/50_luci-forkop \
|
||||||
|
/usr/lib/lua/luci/i18n/netshift.* /www/luci-static/resources/i18n/netshift.* \
|
||||||
|
/usr/share/luci/menu.d/luci-app-netshift.json /usr/share/rpcd/acl.d/luci-app-netshift.json \
|
||||||
|
/etc/uci-defaults/50_luci-netshift 2>/dev/null || true
|
||||||
|
|
||||||
if [ -f /etc/config/forkop ]; then
|
if [ -f /etc/config/netshift ]; then
|
||||||
|
mv /etc/config/netshift /etc/config/tachyon
|
||||||
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
||||||
|
elif [ -f /etc/config/forkop ]; then
|
||||||
mv /etc/config/forkop /etc/config/tachyon
|
mv /etc/config/forkop /etc/config/tachyon
|
||||||
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
||||||
elif [ -f /etc/config/forkop_plus ]; then
|
elif [ -f /etc/config/forkop_plus ]; then
|
||||||
@@ -677,14 +698,22 @@ function restore_cfg_from_backup() {
|
|||||||
fs.unlink(backup);
|
fs.unlink(backup);
|
||||||
}
|
}
|
||||||
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
||||||
system("rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* /usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json /etc/uci-defaults/50_luci-forkop 2>/dev/null || true");
|
system("for svc in podkop forkop netshift podkop_plus forkop_plus; do [ -f /etc/init.d/$svc ] && /etc/init.d/$svc stop >/dev/null 2>&1 && /etc/init.d/$svc disable >/dev/null 2>&1; done || true");
|
||||||
let is_legacy_migration = false;
|
system("rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* /etc/rc.d/*netshift* 2>/dev/null || true");
|
||||||
if (system("test -f /etc/config/forkop") == 0) {
|
system("for tbl in podkop PodkopTable forkop ForkopTable netshift NetShiftTable; do nft delete table inet $tbl >/dev/null 2>&1 || true; done");
|
||||||
|
system("rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* /usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json /etc/uci-defaults/50_luci-forkop /usr/lib/lua/luci/i18n/netshift.* /www/luci-static/resources/i18n/netshift.* /usr/share/luci/menu.d/luci-app-netshift.json /usr/share/rpcd/acl.d/luci-app-netshift.json /etc/uci-defaults/50_luci-netshift 2>/dev/null || true");
|
||||||
|
if (system("test -f /etc/config/netshift") == 0) {
|
||||||
|
system("mv /etc/config/netshift /etc/config/tachyon");
|
||||||
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
|
} else if (system("test -f /etc/config/forkop") == 0) {
|
||||||
system("mv /etc/config/forkop /etc/config/tachyon");
|
system("mv /etc/config/forkop /etc/config/tachyon");
|
||||||
is_legacy_migration = true;
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
} else if (system("test -f /etc/config/forkop_plus") == 0) {
|
} else if (system("test -f /etc/config/forkop_plus") == 0) {
|
||||||
system("mv /etc/config/forkop_plus /etc/config/tachyon");
|
system("mv /etc/config/forkop_plus /etc/config/tachyon");
|
||||||
is_legacy_migration = true;
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
} else if (system("test -f /etc/config/podkop") == 0) {
|
} else if (system("test -f /etc/config/podkop") == 0) {
|
||||||
system("mv /etc/config/podkop /etc/config/tachyon");
|
system("mv /etc/config/podkop /etc/config/tachyon");
|
||||||
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
@@ -694,8 +723,7 @@ if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
|||||||
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
exit(system("/usr/bin/tachyon package_postinst"));
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
}
|
}
|
||||||
if (!is_legacy_migration)
|
restore_cfg_from_backup();
|
||||||
restore_cfg_from_backup();
|
|
||||||
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate && /usr/bin/tachyon package_postinst"));
|
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate && /usr/bin/tachyon package_postinst"));
|
||||||
}
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -780,14 +808,22 @@ function restore_cfg_from_backup() {
|
|||||||
fs.unlink(backup);
|
fs.unlink(backup);
|
||||||
}
|
}
|
||||||
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
||||||
system("rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* /usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json /etc/uci-defaults/50_luci-forkop 2>/dev/null || true");
|
system("for svc in podkop forkop netshift podkop_plus forkop_plus; do [ -f /etc/init.d/$svc ] && /etc/init.d/$svc stop >/dev/null 2>&1 && /etc/init.d/$svc disable >/dev/null 2>&1; done || true");
|
||||||
let is_legacy_migration = false;
|
system("rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* /etc/rc.d/*netshift* 2>/dev/null || true");
|
||||||
if (system("test -f /etc/config/forkop") == 0) {
|
system("for tbl in podkop PodkopTable forkop ForkopTable netshift NetShiftTable; do nft delete table inet $tbl >/dev/null 2>&1 || true; done");
|
||||||
|
system("rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* /usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json /etc/uci-defaults/50_luci-forkop /usr/lib/lua/luci/i18n/netshift.* /www/luci-static/resources/i18n/netshift.* /usr/share/luci/menu.d/luci-app-netshift.json /usr/share/rpcd/acl.d/luci-app-netshift.json /etc/uci-defaults/50_luci-netshift 2>/dev/null || true");
|
||||||
|
if (system("test -f /etc/config/netshift") == 0) {
|
||||||
|
system("mv /etc/config/netshift /etc/config/tachyon");
|
||||||
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
|
} else if (system("test -f /etc/config/forkop") == 0) {
|
||||||
system("mv /etc/config/forkop /etc/config/tachyon");
|
system("mv /etc/config/forkop /etc/config/tachyon");
|
||||||
is_legacy_migration = true;
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
} else if (system("test -f /etc/config/forkop_plus") == 0) {
|
} else if (system("test -f /etc/config/forkop_plus") == 0) {
|
||||||
system("mv /etc/config/forkop_plus /etc/config/tachyon");
|
system("mv /etc/config/forkop_plus /etc/config/tachyon");
|
||||||
is_legacy_migration = true;
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
} else if (system("test -f /etc/config/podkop") == 0) {
|
} else if (system("test -f /etc/config/podkop") == 0) {
|
||||||
system("mv /etc/config/podkop /etc/config/tachyon");
|
system("mv /etc/config/podkop /etc/config/tachyon");
|
||||||
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
@@ -797,8 +833,7 @@ if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "") {
|
|||||||
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop");
|
||||||
exit(system("/usr/bin/tachyon package_postinst"));
|
exit(system("/usr/bin/tachyon package_postinst"));
|
||||||
}
|
}
|
||||||
if (!is_legacy_migration)
|
restore_cfg_from_backup();
|
||||||
restore_cfg_from_backup();
|
|
||||||
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate && /usr/bin/tachyon package_postinst"));
|
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate && /usr/bin/tachyon package_postinst"));
|
||||||
}
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|||||||
+7
-1
@@ -1375,6 +1375,11 @@ function installer_cleanup_legacy() {
|
|||||||
installer_release_init_lock();
|
installer_release_init_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let legacy_tbl in [ "podkop", "PodkopTable", "forkop", "ForkopTable", "netshift", "NetShiftTable" ]) {
|
||||||
|
run_args([ "nft", "delete", "table", "inet", legacy_tbl ]);
|
||||||
|
}
|
||||||
|
system("rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* /etc/rc.d/*netshift* 2>/dev/null || true");
|
||||||
|
|
||||||
let packages_removed = true;
|
let packages_removed = true;
|
||||||
for (let package_name in [ "luci-app-https-dns-proxy", "https-dns-proxy" ])
|
for (let package_name in [ "luci-app-https-dns-proxy", "https-dns-proxy" ])
|
||||||
if (!installer_remove_package(package_name))
|
if (!installer_remove_package(package_name))
|
||||||
@@ -1490,7 +1495,8 @@ function installer_finalize_legacy() {
|
|||||||
|
|
||||||
for (let legacy_ts_dir in [
|
for (let legacy_ts_dir in [
|
||||||
INSTALLER_LEGACY_PERSISTENT_DIR + "/tailscale",
|
INSTALLER_LEGACY_PERSISTENT_DIR + "/tailscale",
|
||||||
"/etc/forkop/tailscale"
|
"/etc/forkop/tailscale",
|
||||||
|
"/etc/netshift/tailscale"
|
||||||
]) {
|
]) {
|
||||||
if (path_exists(legacy_ts_dir)) {
|
if (path_exists(legacy_ts_dir)) {
|
||||||
let entries = fs.lsdir(legacy_ts_dir);
|
let entries = fs.lsdir(legacy_ts_dir);
|
||||||
|
|||||||
+17
-7
@@ -96,24 +96,34 @@ define Package/tachyon/postinst
|
|||||||
|
|
||||||
[ -n "$${IPKG_INSTROOT}" ] && exit 0
|
[ -n "$${IPKG_INSTROOT}" ] && exit 0
|
||||||
|
|
||||||
# Stop and disable legacy forkop / podkop services to prevent boot conflicts
|
# Stop and disable legacy forkop / podkop / netshift services to prevent boot conflicts
|
||||||
for legacy_svc in podkop forkop podkop_plus forkop_plus; do
|
for legacy_svc in podkop forkop netshift podkop_plus forkop_plus; do
|
||||||
if [ -f "/etc/init.d/$${legacy_svc}" ]; then
|
if [ -f "/etc/init.d/$${legacy_svc}" ]; then
|
||||||
"/etc/init.d/$${legacy_svc}" stop >/dev/null 2>&1 || true
|
"/etc/init.d/$${legacy_svc}" stop >/dev/null 2>&1 || true
|
||||||
"/etc/init.d/$${legacy_svc}" disable >/dev/null 2>&1 || true
|
"/etc/init.d/$${legacy_svc}" disable >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* 2>/dev/null || true
|
rm -f /etc/rc.d/*podkop* /etc/rc.d/*forkop* /etc/rc.d/*netshift* 2>/dev/null || true
|
||||||
nft delete table inet podkop >/dev/null 2>&1 || true
|
nft delete table inet podkop >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet PodkopTable >/dev/null 2>&1 || true
|
||||||
nft delete table inet forkop >/dev/null 2>&1 || true
|
nft delete table inet forkop >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet ForkopTable >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet netshift >/dev/null 2>&1 || true
|
||||||
|
nft delete table inet NetShiftTable >/dev/null 2>&1 || true
|
||||||
|
|
||||||
# Clean up legacy forkop package files
|
# Clean up legacy package files
|
||||||
rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* \
|
rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.* \
|
||||||
/usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json \
|
/usr/share/luci/menu.d/luci-app-forkop.json /usr/share/rpcd/acl.d/luci-app-forkop.json \
|
||||||
/etc/uci-defaults/50_luci-forkop 2>/dev/null || true
|
/etc/uci-defaults/50_luci-forkop \
|
||||||
|
/usr/lib/lua/luci/i18n/netshift.* /www/luci-static/resources/i18n/netshift.* \
|
||||||
|
/usr/share/luci/menu.d/luci-app-netshift.json /usr/share/rpcd/acl.d/luci-app-netshift.json \
|
||||||
|
/etc/uci-defaults/50_luci-netshift 2>/dev/null || true
|
||||||
|
|
||||||
# Migration from Forkop or Podkop
|
# Migration from NetShift, Forkop or Podkop
|
||||||
if [ -f /etc/config/forkop ]; then
|
if [ -f /etc/config/netshift ]; then
|
||||||
|
mv /etc/config/netshift /etc/config/tachyon
|
||||||
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
||||||
|
elif [ -f /etc/config/forkop ]; then
|
||||||
mv /etc/config/forkop /etc/config/tachyon
|
mv /etc/config/forkop /etc/config/tachyon
|
||||||
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
||||||
elif [ -f /etc/config/forkop_plus ]; then
|
elif [ -f /etc/config/forkop_plus ]; then
|
||||||
|
|||||||
@@ -412,7 +412,13 @@ const LEGACY_URLTEST_OPTIONS = [
|
|||||||
"urltest_include_regex",
|
"urltest_include_regex",
|
||||||
"urltest_exclude_countries",
|
"urltest_exclude_countries",
|
||||||
"urltest_exclude_outbounds",
|
"urltest_exclude_outbounds",
|
||||||
"urltest_exclude_regex"
|
"urltest_exclude_regex",
|
||||||
|
"urltest_idle_timeout",
|
||||||
|
"urltest_interrupt_exist_connections",
|
||||||
|
"urltest_interval",
|
||||||
|
"urltest_url",
|
||||||
|
"urltest_proxy_links_text",
|
||||||
|
"selector_proxy_links_text"
|
||||||
];
|
];
|
||||||
|
|
||||||
function delete_legacy_urltest_options(ctx, section) {
|
function delete_legacy_urltest_options(ctx, section) {
|
||||||
@@ -510,29 +516,51 @@ function delete_subscription_cache(ctx, section) {
|
|||||||
push(ctx.removed_caches, path);
|
push(ctx.removed_caches, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function migrate_text_proxy_links(ctx, section, option_name) {
|
||||||
|
let text = option(section, option_name, "");
|
||||||
|
if (text == "")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let migrated = false;
|
||||||
|
for (let link in trim_lines(text)) {
|
||||||
|
if (substr(link, 0, 2) == "//" || substr(link, 0, 1) == "#")
|
||||||
|
continue;
|
||||||
|
add_list_unique(ctx, section, "selector_proxy_links", link);
|
||||||
|
migrated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_option(ctx, section, option_name);
|
||||||
|
return migrated;
|
||||||
|
}
|
||||||
|
|
||||||
function migrate_subscription_url(ctx, section) {
|
function migrate_subscription_url(ctx, section) {
|
||||||
let subscription_url = option(section, "subscription_url", "");
|
let urls = option_list_values(section, "subscription_url");
|
||||||
if (subscription_url == "")
|
if (length(urls) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let subscription_user_agent = option(section, "subscription_user_agent", "");
|
let subscription_user_agent = option(section, "subscription_user_agent", "");
|
||||||
let entry = subscription_user_agent != ""
|
for (let u in urls) {
|
||||||
? subscription_url + " | " + subscription_user_agent
|
u = trim(as_string(u));
|
||||||
: subscription_url;
|
if (u == "")
|
||||||
add_list_unique(ctx, section, "subscription_urls", entry);
|
continue;
|
||||||
|
let entry = subscription_user_agent != ""
|
||||||
|
? u + " | " + subscription_user_agent
|
||||||
|
: u;
|
||||||
|
add_list_unique(ctx, section, "subscription_urls", entry);
|
||||||
|
}
|
||||||
delete_option(ctx, section, "subscription_url");
|
delete_option(ctx, section, "subscription_url");
|
||||||
delete_option(ctx, section, "subscription_user_agent");
|
delete_option(ctx, section, "subscription_user_agent");
|
||||||
delete_subscription_cache(ctx, section_name(section));
|
delete_subscription_cache(ctx, section_name(section));
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrate_interval_flags(ctx, section, proxy_config_type) {
|
function migrate_interval_flags(ctx, section, proxy_config_type) {
|
||||||
if (proxy_config_type == "urltest" || proxy_config_type == "subscription") {
|
if (proxy_config_type == "urltest" || proxy_config_type == "subscription" || proxy_config_type == "urltest_text") {
|
||||||
if (option(section, "urltest_check_interval_disabled", "") == "1")
|
if (option(section, "urltest_check_interval_disabled", "") == "1")
|
||||||
set_option(ctx, section, "urltest_enabled", "0");
|
set_option(ctx, section, "urltest_enabled", "0");
|
||||||
else
|
else
|
||||||
set_option_if_missing(ctx, section, "urltest_enabled", "1");
|
set_option_if_missing(ctx, section, "urltest_enabled", "1");
|
||||||
}
|
}
|
||||||
else if (proxy_config_type == "url" || proxy_config_type == "selector") {
|
else if (proxy_config_type == "url" || proxy_config_type == "selector" || proxy_config_type == "selector_text") {
|
||||||
set_option_if_missing(ctx, section, "urltest_enabled", "0");
|
set_option_if_missing(ctx, section, "urltest_enabled", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,6 +587,18 @@ function migrate_proxy_rule(ctx, section, proxy_config_type) {
|
|||||||
migrate_subscription_url(ctx, section);
|
migrate_subscription_url(ctx, section);
|
||||||
delete_subscription_cache(ctx, section_name(section));
|
delete_subscription_cache(ctx, section_name(section));
|
||||||
}
|
}
|
||||||
|
else if (proxy_config_type == "selector_text") {
|
||||||
|
migrate_text_proxy_links(ctx, section, "selector_proxy_links_text");
|
||||||
|
}
|
||||||
|
else if (proxy_config_type == "urltest_text") {
|
||||||
|
migrate_text_proxy_links(ctx, section, "urltest_proxy_links_text");
|
||||||
|
set_option_if_missing(ctx, section, "urltest_enabled", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always migrate text links if present on section regardless of proxy_config_type
|
||||||
|
migrate_text_proxy_links(ctx, section, "selector_proxy_links_text");
|
||||||
|
if (migrate_text_proxy_links(ctx, section, "urltest_proxy_links_text"))
|
||||||
|
set_option_if_missing(ctx, section, "urltest_enabled", "1");
|
||||||
|
|
||||||
migrate_interval_flags(ctx, section, proxy_config_type);
|
migrate_interval_flags(ctx, section, proxy_config_type);
|
||||||
delete_option(ctx, section, "proxy_config_type");
|
delete_option(ctx, section, "proxy_config_type");
|
||||||
@@ -718,18 +758,31 @@ function migrate_urltest_item_settings(ctx, section, constants) {
|
|||||||
if (legacy_enabled) {
|
if (legacy_enabled) {
|
||||||
let child = create_child_for_section(ctx, section, "urltest");
|
let child = create_child_for_section(ctx, section, "urltest");
|
||||||
set_option(ctx, child, "name", "Fastest");
|
set_option(ctx, child, "name", "Fastest");
|
||||||
set_option(ctx, child, "check_interval", option(section, "urltest_check_interval", "3m") || "3m");
|
let check_interval = option(section, "urltest_check_interval", "");
|
||||||
|
if (check_interval == "")
|
||||||
|
check_interval = option(section, "urltest_interval", "3m");
|
||||||
|
set_option(ctx, child, "check_interval", check_interval || "3m");
|
||||||
|
|
||||||
set_option(ctx, child, "tolerance", option(section, "urltest_tolerance", "50") || "50");
|
set_option(ctx, child, "tolerance", option(section, "urltest_tolerance", "50") || "50");
|
||||||
set_option(ctx, child, "testing_url", option(section, "urltest_testing_url", "https://www.gstatic.com/generate_204") || "https://www.gstatic.com/generate_204");
|
|
||||||
|
let testing_url = option(section, "urltest_testing_url", "");
|
||||||
|
if (testing_url == "")
|
||||||
|
testing_url = option(section, "urltest_url", "https://www.gstatic.com/generate_204");
|
||||||
|
set_option(ctx, child, "testing_url", testing_url || "https://www.gstatic.com/generate_204");
|
||||||
set_option(ctx, child, "filter_mode", option(section, "urltest_filter_mode", "disabled") || "disabled");
|
set_option(ctx, child, "filter_mode", option(section, "urltest_filter_mode", "disabled") || "disabled");
|
||||||
set_option(ctx, child, "detect_server_country", normalize_detect_server_country_method(option(section, "detect_server_country", SERVER_COUNTRY_METHOD_FLAG_EMOJI)));
|
set_option(ctx, child, "detect_server_country", normalize_detect_server_country_method(option(section, "detect_server_country", SERVER_COUNTRY_METHOD_FLAG_EMOJI)));
|
||||||
set_option(ctx, child, "interrupt_exist_connections", "1");
|
set_option(ctx, child, "interrupt_exist_connections", "1");
|
||||||
set_option(ctx, child, "pin_dashboard", "1");
|
set_option(ctx, child, "pin_dashboard", "1");
|
||||||
|
|
||||||
let idle_timeout = legacy_urltest_idle_timeout(section, constants);
|
let idle_timeout = option(section, "urltest_idle_timeout", "");
|
||||||
|
if (idle_timeout == "")
|
||||||
|
idle_timeout = legacy_urltest_idle_timeout(section, constants);
|
||||||
if (idle_timeout != "")
|
if (idle_timeout != "")
|
||||||
set_option(ctx, child, "idle_timeout", idle_timeout);
|
set_option(ctx, child, "idle_timeout", idle_timeout);
|
||||||
|
|
||||||
|
if (option(section, "urltest_interrupt_exist_connections", "") != "")
|
||||||
|
set_option(ctx, child, "interrupt_exist_connections", option(section, "urltest_interrupt_exist_connections", "1"));
|
||||||
|
|
||||||
set_list_option_if_not_empty(ctx, child, "include_countries", option_list_values(section, "urltest_include_countries"));
|
set_list_option_if_not_empty(ctx, child, "include_countries", option_list_values(section, "urltest_include_countries"));
|
||||||
set_list_option_if_not_empty(ctx, child, "include_outbounds", option_list_values(section, "urltest_include_outbounds"));
|
set_list_option_if_not_empty(ctx, child, "include_outbounds", option_list_values(section, "urltest_include_outbounds"));
|
||||||
set_list_option_if_not_empty(ctx, child, "include_regex", option_list_values(section, "urltest_include_regex"));
|
set_list_option_if_not_empty(ctx, child, "include_regex", option_list_values(section, "urltest_include_regex"));
|
||||||
@@ -1036,6 +1089,38 @@ function migrate_rule(ctx, section, converted_from_rule, constants) {
|
|||||||
delete_option(ctx, section, "subscription_group_by_countries");
|
delete_option(ctx, section, "subscription_group_by_countries");
|
||||||
delete_option(ctx, section, "group_by_countries");
|
delete_option(ctx, section, "group_by_countries");
|
||||||
delete_option(ctx, section, "subscription_detect_server_countries");
|
delete_option(ctx, section, "subscription_detect_server_countries");
|
||||||
|
delete_option(ctx, section, "subscription_insecure");
|
||||||
|
delete_option(ctx, section, "subscription_format_preference");
|
||||||
|
delete_option(ctx, section, "subscription_group_mode");
|
||||||
|
delete_option(ctx, section, "subscription_group_prefix_len");
|
||||||
|
delete_option(ctx, section, "subscription_filter_include_keywords");
|
||||||
|
delete_option(ctx, section, "subscription_filter_exclude_keywords");
|
||||||
|
delete_option(ctx, section, "global_proxy");
|
||||||
|
|
||||||
|
// Migrate legacy domain_list_urls and subnet_list_urls (early Podkop)
|
||||||
|
if (option_exists(section, "domain_list_urls")) {
|
||||||
|
for (let entry in option_list_values(section, "domain_list_urls")) {
|
||||||
|
entry = trim(as_string(entry));
|
||||||
|
if (entry == "") continue;
|
||||||
|
if (match(entry, /\.srs$/i))
|
||||||
|
add_list_unique(ctx, section, "rule_set", entry);
|
||||||
|
else
|
||||||
|
add_list_unique(ctx, section, "remote_domain_lists", entry);
|
||||||
|
}
|
||||||
|
delete_option(ctx, section, "domain_list_urls");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option_exists(section, "subnet_list_urls")) {
|
||||||
|
for (let entry in option_list_values(section, "subnet_list_urls")) {
|
||||||
|
entry = trim(as_string(entry));
|
||||||
|
if (entry == "") continue;
|
||||||
|
if (match(entry, /\.srs$/i) || match(entry, /\.json$/i))
|
||||||
|
add_list_unique(ctx, section, "rule_set_with_subnets", entry);
|
||||||
|
else
|
||||||
|
add_list_unique(ctx, section, "remote_subnet_lists", entry);
|
||||||
|
}
|
||||||
|
delete_option(ctx, section, "subnet_list_urls");
|
||||||
|
}
|
||||||
|
|
||||||
if (action == "connection") {
|
if (action == "connection") {
|
||||||
migrate_urltest_filter_mode(ctx, section);
|
migrate_urltest_filter_mode(ctx, section);
|
||||||
@@ -1102,11 +1187,31 @@ function migrate_list_update_enabled(ctx) {
|
|||||||
|
|
||||||
function normalize_existing_list_option(ctx, section, key) {
|
function normalize_existing_list_option(ctx, section, key) {
|
||||||
let current = object_or_empty(section)[key];
|
let current = object_or_empty(section)[key];
|
||||||
if (current == null || type(current) == "array")
|
if (current == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let values = [];
|
||||||
|
if (type(current) == "array") {
|
||||||
|
for (let item in current) {
|
||||||
|
item = trim(as_string(item));
|
||||||
|
if (item != "")
|
||||||
|
push(values, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let text = trim(as_string(current));
|
||||||
|
if (text != "") {
|
||||||
|
for (let item in split(text, /[ \t\r\n,]+/)) {
|
||||||
|
item = trim(item);
|
||||||
|
if (item != "")
|
||||||
|
push(values, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type(current) == "array" && length(values) == length(current))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let value = trim(as_string(current));
|
|
||||||
let values = value == "" ? [] : [ value ];
|
|
||||||
section[key] = values;
|
section[key] = values;
|
||||||
record_operation(ctx, { op: "set_list", section: section_name(section), option: key, values });
|
record_operation(ctx, { op: "set_list", section: section_name(section), option: key, values });
|
||||||
}
|
}
|
||||||
@@ -1522,6 +1627,19 @@ function migrate_podkop_model(model, constants) {
|
|||||||
constants = object_or_empty(constants);
|
constants = object_or_empty(constants);
|
||||||
|
|
||||||
delete_option(ctx, model.settings, "routing_excluded_ips");
|
delete_option(ctx, model.settings, "routing_excluded_ips");
|
||||||
|
|
||||||
|
// NetShift: dns_via_outbound and dns_outbound_section
|
||||||
|
if (bool_option(model.settings, "dns_via_outbound", false)) {
|
||||||
|
set_option_if_missing(ctx, model.settings, "dns_detour_enabled", "1");
|
||||||
|
let detour_sec = option(model.settings, "dns_outbound_section", "");
|
||||||
|
if (detour_sec != "")
|
||||||
|
set_option_if_missing(ctx, model.settings, "dns_detour_section", detour_sec);
|
||||||
|
}
|
||||||
|
delete_option(ctx, model.settings, "dns_via_outbound");
|
||||||
|
delete_option(ctx, model.settings, "dns_outbound_section");
|
||||||
|
delete_option(ctx, model.settings, "block_doh");
|
||||||
|
delete_option(ctx, model.settings, "enable_ipv6");
|
||||||
|
|
||||||
migrate_dns_server_lists(ctx);
|
migrate_dns_server_lists(ctx);
|
||||||
migrate_list_update_enabled(ctx);
|
migrate_list_update_enabled(ctx);
|
||||||
|
|
||||||
@@ -1781,9 +1899,18 @@ function detect_config_migration_source(path) {
|
|||||||
|
|
||||||
if (match(content, /config[ \t]+rule[ \t]+/) ||
|
if (match(content, /config[ \t]+rule[ \t]+/) ||
|
||||||
match(content, /option[ \t]+domain_list_urls/) ||
|
match(content, /option[ \t]+domain_list_urls/) ||
|
||||||
|
match(content, /option[ \t]+subnet_list_urls/) ||
|
||||||
match(content, /option[ \t]+routing_excluded_ips/) ||
|
match(content, /option[ \t]+routing_excluded_ips/) ||
|
||||||
match(content, /option[ \t]+connection_type[ \t]+['"]?proxy/) ||
|
match(content, /option[ \t]+connection_type/) ||
|
||||||
|
match(content, /option[ \t]+proxy_config_type/) ||
|
||||||
match(content, /option[ \t]+proxy_string/) ||
|
match(content, /option[ \t]+proxy_string/) ||
|
||||||
|
match(content, /option[ \t]+selector_proxy_links_text/) ||
|
||||||
|
match(content, /option[ \t]+urltest_proxy_links_text/) ||
|
||||||
|
match(content, /option[ \t]+subscription_url/) ||
|
||||||
|
match(content, /list[ \t]+subscription_url/) ||
|
||||||
|
match(content, /option[ \t]+dns_via_outbound/) ||
|
||||||
|
match(content, /option[ \t]+block_doh/) ||
|
||||||
|
match(content, /option[ \t]+global_proxy/) ||
|
||||||
match(content, /option[ \t]+ip_cidr/)) {
|
match(content, /option[ \t]+ip_cidr/)) {
|
||||||
return "podkop";
|
return "podkop";
|
||||||
}
|
}
|
||||||
@@ -1840,11 +1967,13 @@ function scan_legacy_config_candidates() {
|
|||||||
"/tmp/legacy-config*",
|
"/tmp/legacy-config*",
|
||||||
"/tmp/*forkop*",
|
"/tmp/*forkop*",
|
||||||
"/tmp/*podkop*",
|
"/tmp/*podkop*",
|
||||||
|
"/tmp/*netshift*",
|
||||||
"/tmp/*tachyon*",
|
"/tmp/*tachyon*",
|
||||||
"/tmp/tmp.*/legacy-config*",
|
"/tmp/tmp.*/legacy-config*",
|
||||||
"/etc/.tachyon/*",
|
"/etc/.tachyon/*",
|
||||||
"/root/*forkop*",
|
"/root/*forkop*",
|
||||||
"/root/*podkop*",
|
"/root/*podkop*",
|
||||||
|
"/root/*netshift*",
|
||||||
"/root/*tachyon*",
|
"/root/*tachyon*",
|
||||||
"/root/*.backup*",
|
"/root/*.backup*",
|
||||||
"/root/*.bak*"
|
"/root/*.bak*"
|
||||||
@@ -1940,7 +2069,7 @@ function import_settings_cli(source_path) {
|
|||||||
system("chmod 0600 " + shell_quote(target_config) + " 2>/dev/null");
|
system("chmod 0600 " + shell_quote(target_config) + " 2>/dev/null");
|
||||||
|
|
||||||
let source_type = detect_config_migration_source(target_config);
|
let source_type = detect_config_migration_source(target_config);
|
||||||
print(" ✓ Detected format: " + (source_type == "podkop" ? "Legacy Forkop / Podkop" : "Tachyon") + "\n");
|
print(" ✓ Detected format: " + (source_type == "podkop" ? "Legacy Forkop / Podkop / NetShift" : "Tachyon") + "\n");
|
||||||
|
|
||||||
let ok = migrate_runtime(source_type);
|
let ok = migrate_runtime(source_type);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -1967,8 +2096,10 @@ function import_settings_cli(source_path) {
|
|||||||
function main(argv) {
|
function main(argv) {
|
||||||
let mode = argv[0] || "";
|
let mode = argv[0] || "";
|
||||||
|
|
||||||
if (mode == "migrate")
|
if (mode == "migrate") {
|
||||||
return migrate_runtime("tachyon") ? 0 : 1;
|
let source = detect_config_migration_source(TARGET_CONFIG);
|
||||||
|
return migrate_runtime(source) ? 0 : 1;
|
||||||
|
}
|
||||||
if (mode == "migrate-podkop")
|
if (mode == "migrate-podkop")
|
||||||
return migrate_runtime("podkop") ? 0 : 1;
|
return migrate_runtime("podkop") ? 0 : 1;
|
||||||
if (mode == "import-settings" || mode == "import_settings")
|
if (mode == "import-settings" || mode == "import_settings")
|
||||||
|
|||||||
@@ -2412,6 +2412,11 @@ function nft_delete_table(table) {
|
|||||||
function nft_rebuild_runtime_from_uci(rt_table, table, localv4_set, common_set, port_set, ip_port_set, interface_set, fakeip_mark, outbound_mark, fakeip_range, tproxy_port, zapret_bin, zapret_route_mark_base, zapret_queue_base, zapret_desync_mark, zapret_desync_mark_postnat, zapret2_bin, zapret2_route_mark_base, zapret2_queue_base, zapret2_desync_mark, zapret2_desync_mark_postnat, localv6_set, common6_set, ip_port6_set, fakeip6_range, tproxy6_address) {
|
function nft_rebuild_runtime_from_uci(rt_table, table, localv4_set, common_set, port_set, ip_port_set, interface_set, fakeip_mark, outbound_mark, fakeip_range, tproxy_port, zapret_bin, zapret_route_mark_base, zapret_queue_base, zapret_desync_mark, zapret_desync_mark_postnat, zapret2_bin, zapret2_route_mark_base, zapret2_queue_base, zapret2_desync_mark, zapret2_desync_mark_postnat, localv6_set, common6_set, ip_port6_set, fakeip6_range, tproxy6_address) {
|
||||||
log_debug("Applying nftables runtime rules");
|
log_debug("Applying nftables runtime rules");
|
||||||
|
|
||||||
|
for (let legacy_table in [ "NetShiftTable", "PodkopTable", "ForkopTable", "podkop", "forkop", "netshift" ]) {
|
||||||
|
if (legacy_table != table && nft_table_present(legacy_table))
|
||||||
|
nft_delete_table(legacy_table);
|
||||||
|
}
|
||||||
|
|
||||||
if (nft_table_present(table) && !nft_delete_table(table))
|
if (nft_table_present(table) && !nft_delete_table(table))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -864,7 +864,7 @@ function start_main() {
|
|||||||
|
|
||||||
module_success(BYEDPI_UC, [ "start-runtime" ]);
|
module_success(BYEDPI_UC, [ "start-runtime" ]);
|
||||||
|
|
||||||
for (let comp_svc in [ "forkop", "podkop" ]) {
|
for (let comp_svc in [ "forkop", "podkop", "netshift" ]) {
|
||||||
if (fs.stat("/etc/init.d/" + comp_svc) != null) {
|
if (fs.stat("/etc/init.d/" + comp_svc) != null) {
|
||||||
command_status_from_args([ "/etc/init.d/" + comp_svc, "stop" ]);
|
command_status_from_args([ "/etc/init.d/" + comp_svc, "stop" ]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ function uninstall_tachyon(purge_config, keep_binaries) {
|
|||||||
if (fs.stat("/etc/init.d/uhttpd") != null)
|
if (fs.stat("/etc/init.d/uhttpd") != null)
|
||||||
run_cmd(["/etc/init.d/uhttpd", "restart"]);
|
run_cmd(["/etc/init.d/uhttpd", "restart"]);
|
||||||
|
|
||||||
// Restore parent Forkop / Podkop services if present
|
// Restore parent Forkop / Podkop / NetShift services if present
|
||||||
if (fs.stat("/etc/init.d/forkop") != null) {
|
if (fs.stat("/etc/init.d/forkop") != null) {
|
||||||
log_step("Forkop detected! Restoring Forkop service...");
|
log_step("Forkop detected! Restoring Forkop service...");
|
||||||
run_cmd(["/etc/init.d/forkop", "enable"]);
|
run_cmd(["/etc/init.d/forkop", "enable"]);
|
||||||
@@ -257,6 +257,10 @@ function uninstall_tachyon(purge_config, keep_binaries) {
|
|||||||
log_step("Podkop detected! Restoring Podkop service...");
|
log_step("Podkop detected! Restoring Podkop service...");
|
||||||
run_cmd(["/etc/init.d/podkop", "enable"]);
|
run_cmd(["/etc/init.d/podkop", "enable"]);
|
||||||
run_cmd(["/etc/init.d/podkop", "restart"]);
|
run_cmd(["/etc/init.d/podkop", "restart"]);
|
||||||
|
} else if (fs.stat("/etc/init.d/netshift") != null) {
|
||||||
|
log_step("NetShift detected! Restoring NetShift service...");
|
||||||
|
run_cmd(["/etc/init.d/netshift", "enable"]);
|
||||||
|
run_cmd(["/etc/init.d/netshift", "restart"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_step("Tachyon has been successfully uninstalled.");
|
log_step("Tachyon has been successfully uninstalled.");
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ function configure_service() {
|
|||||||
command_success_from_args([ "/etc/init.d/sing-box", "disable" ]);
|
command_success_from_args([ "/etc/init.d/sing-box", "disable" ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let comp_svc in [ "forkop", "podkop" ]) {
|
for (let comp_svc in [ "forkop", "podkop", "netshift" ]) {
|
||||||
if (file_exists("/etc/init.d/" + comp_svc)) {
|
if (file_exists("/etc/init.d/" + comp_svc)) {
|
||||||
command_status_from_args([ "/etc/init.d/" + comp_svc, "stop" ]);
|
command_status_from_args([ "/etc/init.d/" + comp_svc, "stop" ]);
|
||||||
command_status_from_args([ "/etc/init.d/" + comp_svc, "disable" ]);
|
command_status_from_args([ "/etc/init.d/" + comp_svc, "disable" ]);
|
||||||
|
|||||||
@@ -0,0 +1,245 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||||
|
MIGRATION="$TACHYON_LIB/config/migration.uc"
|
||||||
|
WORK_DIR="$(mktemp -d)"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf 'FAIL: %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -s "$MIGRATION" ] || fail "runtime configuration migration module is missing"
|
||||||
|
|
||||||
|
# ─── 1. Test NetShift and Early Podkop Fixture Migration ─────────────────────
|
||||||
|
node >"$WORK_DIR/predecessors_fixture.json" <<'NODE'
|
||||||
|
const fixture = {
|
||||||
|
settings: {
|
||||||
|
'.name': 'settings',
|
||||||
|
'.type': 'settings',
|
||||||
|
dns_via_outbound: '1',
|
||||||
|
dns_outbound_section: 'sec_proxy',
|
||||||
|
block_doh: '1',
|
||||||
|
dns_server: '8.8.8.8, 1.1.1.1',
|
||||||
|
bootstrap_dns_server: '77.88.8.8 9.9.9.9'
|
||||||
|
},
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
'.name': 'sec_proxy',
|
||||||
|
'.type': 'rule',
|
||||||
|
enabled: '1',
|
||||||
|
connection_type: 'proxy',
|
||||||
|
proxy_config_type: 'selector_text',
|
||||||
|
selector_proxy_links_text: 'vless://b1234567-89ab-cdef-0123-456789abcdef@proxy1.example.com:443?encryption=none&security=reality&sni=test.com&fp=chrome&pbk=publicKey&type=tcp#ProxyOne\n\n# comment\nss://YWVzLTEyOC1nY206cGFzc3dvcmQ@proxy2.example.com:8388#ProxyTwo'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'.name': 'sec_urltest',
|
||||||
|
'.type': 'rule',
|
||||||
|
enabled: '1',
|
||||||
|
connection_type: 'proxy',
|
||||||
|
proxy_config_type: 'urltest_text',
|
||||||
|
urltest_proxy_links_text: 'trojan://password@proxy3.example.com:443#TrojanOne\nvmess://eyJ2IjoiMiIsInBzIjoiVm1lc3NPbmUiLCJhZGQiOiJwcm94eTQuZXhhbXBsZS5jb20iLCJwb3J0IjoiNDQzIiwiaWQiOiJiMTIzNDU2Ny04OWFiLWNkZWYtMDEyMy00NTY3ODlhYmNkZWYiLCJhaWQiOiIwIiwic2N5IjoiYXV0byIsIm5ldCI6IndzIiwidHlwZSI6Im5vbmUiLCJob3N0IjoidGVzdC5jb20iLCJwYXRoIjoiL3dzIiwidGxzIjoidGxzIn0=',
|
||||||
|
urltest_idle_timeout: '25m',
|
||||||
|
urltest_interrupt_exist_connections: '1',
|
||||||
|
urltest_interval: '15m'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'.name': 'sec_sub',
|
||||||
|
'.type': 'rule',
|
||||||
|
enabled: '1',
|
||||||
|
connection_type: 'proxy',
|
||||||
|
proxy_config_type: 'subscription',
|
||||||
|
subscription_url: [
|
||||||
|
'https://feed1.example.com/sub.txt',
|
||||||
|
'https://feed2.example.com/sub.txt'
|
||||||
|
],
|
||||||
|
subscription_insecure: '1',
|
||||||
|
subscription_format_preference: 'auto',
|
||||||
|
subscription_group_mode: 'none',
|
||||||
|
subscription_group_prefix_len: '2',
|
||||||
|
subscription_filter_include_keywords: 'US|NL',
|
||||||
|
subscription_filter_exclude_keywords: 'RU|BY',
|
||||||
|
global_proxy: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'.name': 'sec_early_podkop',
|
||||||
|
'.type': 'rule',
|
||||||
|
enabled: '1',
|
||||||
|
connection_type: 'proxy',
|
||||||
|
proxy_config_type: 'url',
|
||||||
|
proxy_string: 'vless://b1234567-89ab-cdef-0123-456789abcdef@single.example.com:443?encryption=none&security=reality&sni=test.com&fp=chrome&pbk=publicKey&type=tcp#Single',
|
||||||
|
domain_list_urls: [
|
||||||
|
'https://antifilter.download/list/domains.lst'
|
||||||
|
],
|
||||||
|
subnet_list_urls: [
|
||||||
|
'https://antifilter.download/list/ip.lst'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
process.stdout.write(`${JSON.stringify(fixture, null, 2)}\n`);
|
||||||
|
NODE
|
||||||
|
|
||||||
|
TACHYON_LIB="$TACHYON_LIB" ucode -L "$TACHYON_LIB" "$MIGRATION" migrate-fixture "$WORK_DIR/predecessors_fixture.json" podkop >"$WORK_DIR/output.json"
|
||||||
|
|
||||||
|
node - "$WORK_DIR/output.json" <<'NODE'
|
||||||
|
const fs = require('fs');
|
||||||
|
const out = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
||||||
|
const config = out.config;
|
||||||
|
|
||||||
|
function assert(condition, message) {
|
||||||
|
if (!condition) {
|
||||||
|
console.error(`ASSERTION FAILED: ${message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Settings checks
|
||||||
|
assert(config.settings.dns_detour_enabled === '1', 'NetShift dns_via_outbound must map to dns_detour_enabled=1');
|
||||||
|
assert(config.settings.dns_detour_section === 'sec_proxy', 'NetShift dns_outbound_section must map to dns_detour_section');
|
||||||
|
assert(config.settings.block_doh === undefined, 'NetShift block_doh must be cleaned up');
|
||||||
|
assert(Array.isArray(config.settings.dns_server) && config.settings.dns_server.length === 2, 'Comma-separated dns_server must be normalized to array');
|
||||||
|
assert(config.settings.dns_server[0] === '8.8.8.8' && config.settings.dns_server[1] === '1.1.1.1', 'dns_server entries must match split elements');
|
||||||
|
assert(Array.isArray(config.settings.bootstrap_dns_server) && config.settings.bootstrap_dns_server.length === 2, 'Space-separated bootstrap_dns_server must be normalized to array');
|
||||||
|
assert(config.settings.bootstrap_dns_server[0] === '77.88.8.8' && config.settings.bootstrap_dns_server[1] === '9.9.9.9', 'bootstrap_dns_server entries must match split elements');
|
||||||
|
|
||||||
|
const sections = Object.fromEntries(config.section.map(s => [s['.name'], s]));
|
||||||
|
|
||||||
|
// 2. NetShift selector_text rule migrated to connection with selector_proxy_links
|
||||||
|
const secProxy = sections['sec_proxy'];
|
||||||
|
assert(secProxy !== undefined, 'sec_proxy section must exist');
|
||||||
|
assert(secProxy.action === 'connection', 'sec_proxy must be migrated to action=connection');
|
||||||
|
assert(secProxy.proxy_config_type === undefined, 'proxy_config_type must be deleted in Tachyon unified schema');
|
||||||
|
assert(Array.isArray(secProxy.selector_proxy_links) && secProxy.selector_proxy_links.length === 2, `Expected 2 selector_proxy_links for sec_proxy, got ${JSON.stringify(secProxy.selector_proxy_links)}`);
|
||||||
|
assert(secProxy.selector_proxy_links[0].startsWith('vless://'), 'First proxy link must be preserved');
|
||||||
|
assert(secProxy.selector_proxy_links[1].startsWith('ss://'), 'Second proxy link must be preserved');
|
||||||
|
|
||||||
|
// 3. NetShift urltest_text rule migrated to connection with child urltest
|
||||||
|
const secUrltest = sections['sec_urltest'];
|
||||||
|
assert(secUrltest !== undefined, 'sec_urltest section must exist');
|
||||||
|
assert(secUrltest.action === 'connection', 'sec_urltest must be migrated to action=connection');
|
||||||
|
assert(secUrltest.proxy_config_type === undefined, 'proxy_config_type must be deleted in Tachyon unified schema');
|
||||||
|
assert(Array.isArray(secUrltest.selector_proxy_links) && secUrltest.selector_proxy_links.length === 2, `Expected 2 selector_proxy_links for sec_urltest, got ${JSON.stringify(secUrltest.selector_proxy_links)}`);
|
||||||
|
const childUrltests = config.urltest || [];
|
||||||
|
const urltestItem = childUrltests.find(u => u.section === 'sec_urltest');
|
||||||
|
assert(urltestItem !== undefined, 'child urltest section must exist for sec_urltest');
|
||||||
|
assert(urltestItem.idle_timeout === '25m', 'urltest_idle_timeout must be inherited into child urltest');
|
||||||
|
assert(urltestItem.interrupt_exist_connections === '1', 'urltest_interrupt_exist_connections must be inherited into child urltest');
|
||||||
|
assert(urltestItem.check_interval === '15m', 'urltest check_interval must be preserved in child urltest');
|
||||||
|
|
||||||
|
// 4. NetShift multi-feed subscription_url
|
||||||
|
const secSub = sections['sec_sub'];
|
||||||
|
assert(secSub !== undefined, 'sec_sub section must exist');
|
||||||
|
assert(secSub.action === 'connection', 'sec_sub must be migrated to action=connection');
|
||||||
|
assert(secSub.proxy_config_type === undefined, 'proxy_config_type must be deleted in Tachyon unified schema');
|
||||||
|
const childSubs = config.subscription_url || [];
|
||||||
|
const subItems = childSubs.filter(s => s.section === 'sec_sub');
|
||||||
|
assert(subItems.length === 2, `Multi-feed subscription_url must generate 2 child subscription_url sections, got ${subItems.length}`);
|
||||||
|
assert(subItems[0].url === 'https://feed1.example.com/sub.txt', 'First subscription URL must be preserved');
|
||||||
|
assert(subItems[1].url === 'https://feed2.example.com/sub.txt', 'Second subscription URL must be preserved');
|
||||||
|
|
||||||
|
// Verify obsolete NetShift options were cleaned up
|
||||||
|
assert(secSub.subscription_insecure === undefined, 'subscription_insecure must be cleaned up');
|
||||||
|
assert(secSub.subscription_format_preference === undefined, 'subscription_format_preference must be cleaned up');
|
||||||
|
assert(secSub.subscription_group_mode === undefined, 'subscription_group_mode must be cleaned up');
|
||||||
|
assert(secSub.global_proxy === undefined, 'global_proxy must be cleaned up');
|
||||||
|
|
||||||
|
// 5. Early Podkop domain_list_urls and subnet_list_urls
|
||||||
|
const secEarly = sections['sec_early_podkop'];
|
||||||
|
assert(secEarly !== undefined, 'sec_early_podkop section must exist');
|
||||||
|
assert(Array.isArray(secEarly.remote_domain_lists) && secEarly.remote_domain_lists.includes('https://antifilter.download/list/domains.lst'), 'domain_list_urls must migrate to remote_domain_lists');
|
||||||
|
assert(Array.isArray(secEarly.remote_subnet_lists) && secEarly.remote_subnet_lists.includes('https://antifilter.download/list/ip.lst'), 'subnet_list_urls must migrate to remote_subnet_lists');
|
||||||
|
assert(secEarly.domain_list_urls === undefined, 'domain_list_urls key must be deleted');
|
||||||
|
assert(secEarly.subnet_list_urls === undefined, 'subnet_list_urls key must be deleted');
|
||||||
|
|
||||||
|
console.log('Predecessor fixtures assertion passed successfully.');
|
||||||
|
NODE
|
||||||
|
|
||||||
|
# ─── 2. Test Format Detection for All Predecessors ───────────────────────────
|
||||||
|
cat >"$WORK_DIR/mock_netshift.conf" <<'EOF'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '1'
|
||||||
|
option dns_via_outbound '1'
|
||||||
|
option dns_outbound_section 'main_proxy'
|
||||||
|
|
||||||
|
config rule 'main_proxy'
|
||||||
|
option enabled '1'
|
||||||
|
option proxy_config_type 'selector_text'
|
||||||
|
option selector_proxy_links_text 'vless://example'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$WORK_DIR/mock_forkop.conf" <<'EOF'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '1'
|
||||||
|
|
||||||
|
config section 'my_wg'
|
||||||
|
option enabled '1'
|
||||||
|
option connection_type 'vpn'
|
||||||
|
option proxy_config_type 'interface'
|
||||||
|
option interface 'wg0'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$WORK_DIR/mock_podkop.conf" <<'EOF'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '1'
|
||||||
|
|
||||||
|
config rule 'antifilter'
|
||||||
|
option enabled '1'
|
||||||
|
option connection_type 'proxy'
|
||||||
|
option proxy_config_type 'url'
|
||||||
|
list domain_list_urls 'https://antifilter.download/list/domains.lst'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$WORK_DIR/mock_tachyon.conf" <<'EOF'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '1'
|
||||||
|
option config_version '1.0.5'
|
||||||
|
list applied_migrations 'interface_sections'
|
||||||
|
list applied_migrations 'ensure_dns_server_defaults'
|
||||||
|
|
||||||
|
config section 'my_proxy'
|
||||||
|
option enabled '1'
|
||||||
|
option action 'connection'
|
||||||
|
list selector_proxy_links 'vless://example'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$WORK_DIR/detect_test.uc" <<'UCODE'
|
||||||
|
let migration = require("config.migration");
|
||||||
|
|
||||||
|
function assert(val, msg) {
|
||||||
|
if (!val) {
|
||||||
|
warn("ASSERTION FAILED: " + msg + "\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ns_src = migration.detect_config_migration_source(ARGV[0]);
|
||||||
|
assert(ns_src == "podkop", "NetShift config must be detected as legacy podkop format, got: " + ns_src);
|
||||||
|
|
||||||
|
let fk_src = migration.detect_config_migration_source(ARGV[1]);
|
||||||
|
assert(fk_src == "podkop", "Forkop config must be detected as legacy podkop format, got: " + fk_src);
|
||||||
|
|
||||||
|
let pk_src = migration.detect_config_migration_source(ARGV[2]);
|
||||||
|
assert(pk_src == "podkop", "Podkop config must be detected as legacy podkop format, got: " + pk_src);
|
||||||
|
|
||||||
|
let tc_src = migration.detect_config_migration_source(ARGV[3]);
|
||||||
|
assert(tc_src == "tachyon", "Tachyon config must be detected as native tachyon format, got: " + tc_src);
|
||||||
|
|
||||||
|
print("Format detection tests passed successfully.\n");
|
||||||
|
UCODE
|
||||||
|
|
||||||
|
ucode -L "$TACHYON_LIB" "$WORK_DIR/detect_test.uc" \
|
||||||
|
"$WORK_DIR/mock_netshift.conf" \
|
||||||
|
"$WORK_DIR/mock_forkop.conf" \
|
||||||
|
"$WORK_DIR/mock_podkop.conf" \
|
||||||
|
"$WORK_DIR/mock_tachyon.conf"
|
||||||
|
|
||||||
|
printf 'ALL predecessor migration tests passed successfully.\n'
|
||||||
+5
-1
@@ -418,7 +418,7 @@ if [ -f "/etc/init.d/uhttpd" ]; then
|
|||||||
/etc/init.d/uhttpd restart >/dev/null 2>&1 || true
|
/etc/init.d/uhttpd restart >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if parent Forkop / Podkop services exist and restore them
|
# Check if parent Forkop / Podkop / NetShift services exist and restore them
|
||||||
if [ -f "/etc/init.d/forkop" ]; then
|
if [ -f "/etc/init.d/forkop" ]; then
|
||||||
/etc/init.d/forkop enable >/dev/null 2>&1 || true
|
/etc/init.d/forkop enable >/dev/null 2>&1 || true
|
||||||
/etc/init.d/forkop restart >/dev/null 2>&1 || true
|
/etc/init.d/forkop restart >/dev/null 2>&1 || true
|
||||||
@@ -427,6 +427,10 @@ elif [ -f "/etc/init.d/podkop" ]; then
|
|||||||
/etc/init.d/podkop enable >/dev/null 2>&1 || true
|
/etc/init.d/podkop enable >/dev/null 2>&1 || true
|
||||||
/etc/init.d/podkop restart >/dev/null 2>&1 || true
|
/etc/init.d/podkop restart >/dev/null 2>&1 || true
|
||||||
tui_ok "Обнаружен родительский сервис Podkop — восстановлен и запущен"
|
tui_ok "Обнаружен родительский сервис Podkop — восстановлен и запущен"
|
||||||
|
elif [ -f "/etc/init.d/netshift" ]; then
|
||||||
|
/etc/init.d/netshift enable >/dev/null 2>&1 || true
|
||||||
|
/etc/init.d/netshift restart >/dev/null 2>&1 || true
|
||||||
|
tui_ok "Обнаружен родительский сервис NetShift — восстановлен и запущен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tui_ok "Кэш LuCI очищен, файлы и остаточные фрагменты полностью удалены"
|
tui_ok "Кэш LuCI очищен, файлы и остаточные фрагменты полностью удалены"
|
||||||
|
|||||||
Reference in New Issue
Block a user