feat(migration): 1.3.11 - CLI команда import_settings и исправление миграции Forkop
- Добавлена CLI команда tachyon import_settings [файл] (и алиас import-settings) для автоматического сканирования и бесшовного импорта настроек из Forkop, Forkop Plus, Podkop, Netshift или резервных копий - Поддержка in-place миграции прямо из активного /etc/config/tachyon при обнаружении унаследованных правил config rule - Расширенное сканирование резервных копий и файлов конфигураций в /etc/config/, /root/ и /tmp/ - Исправлен режим миграции Forkop в Makefile, build.sh и install.sh (использование migrate-podkop для корректного преобразования старых секций config rule в config section) - Добавлен интеграционный тест tests/import_settings.sh (119 тестов бэкенда пройдены)
This commit is contained in:
@@ -402,19 +402,19 @@ rm -rf /usr/lib/lua/luci/i18n/forkop.* /www/luci-static/resources/i18n/forkop.*
|
|||||||
|
|
||||||
if [ -f /etc/config/forkop ]; then
|
if [ -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
|
||||||
elif [ -f /etc/config/forkop_plus ]; then
|
elif [ -f /etc/config/forkop_plus ]; then
|
||||||
mv /etc/config/forkop_plus /etc/config/tachyon
|
mv /etc/config/forkop_plus /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/podkop ]; then
|
elif [ -f /etc/config/podkop ]; then
|
||||||
mv /etc/config/podkop /etc/config/tachyon
|
mv /etc/config/podkop /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
|
||||||
exit 0
|
|
||||||
elif [ -f /etc/config/podkop_plus ]; then
|
elif [ -f /etc/config/podkop_plus ]; then
|
||||||
mv /etc/config/podkop_plus /etc/config/tachyon
|
mv /etc/config/podkop_plus /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
|
||||||
exit 0
|
else
|
||||||
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat > "$control_dir/prerm" <<'EOF'
|
cat > "$control_dir/prerm" <<'EOF'
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "fe-app-tachyon",
|
"name": "fe-app-tachyon",
|
||||||
"version": "1.3.10",
|
"version": "1.3.11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fe-app-tachyon",
|
"name": "fe-app-tachyon",
|
||||||
"version": "1.3.10",
|
"version": "1.3.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/parser": "7.28.4",
|
"@babel/parser": "7.28.4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fe-app-tachyon",
|
"name": "fe-app-tachyon",
|
||||||
"version": "1.3.10",
|
"version": "1.3.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -2588,9 +2588,6 @@ migrate_legacy_configuration() {
|
|||||||
|
|
||||||
msg "$(installer_text migrating_config_detail)"
|
msg "$(installer_text migrating_config_detail)"
|
||||||
local migration_mode="migrate-podkop"
|
local migration_mode="migrate-podkop"
|
||||||
if [ "$TACHYON_FORKOP_MIGRATION" -eq 1 ] || [ "$TACHYON_NETSHIFT_MIGRATION" -eq 1 ]; then
|
|
||||||
migration_mode="migrate"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! TACHYON_CONFIG_NAME="tachyon" \
|
if ! TACHYON_CONFIG_NAME="tachyon" \
|
||||||
TACHYON_LIB="/usr/lib/tachyon" \
|
TACHYON_LIB="/usr/lib/tachyon" \
|
||||||
|
|||||||
+5
-5
@@ -101,23 +101,23 @@ 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 2>/dev/null || true
|
||||||
|
|
||||||
# Migration from Forkop
|
# Migration from Forkop or Podkop
|
||||||
if [ -f /etc/config/forkop ]; then
|
if [ -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
|
||||||
elif [ -f /etc/config/forkop_plus ]; then
|
elif [ -f /etc/config/forkop_plus ]; then
|
||||||
mv /etc/config/forkop_plus /etc/config/tachyon
|
mv /etc/config/forkop_plus /etc/config/tachyon
|
||||||
# Migration from Podkop
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate-podkop
|
||||||
elif [ -f /etc/config/podkop ]; then
|
elif [ -f /etc/config/podkop ]; then
|
||||||
mv /etc/config/podkop /etc/config/tachyon
|
mv /etc/config/podkop /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
|
||||||
exit 0
|
|
||||||
elif [ -f /etc/config/podkop_plus ]; then
|
elif [ -f /etc/config/podkop_plus ]; then
|
||||||
mv /etc/config/podkop_plus /etc/config/tachyon
|
mv /etc/config/podkop_plus /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
|
||||||
exit 0
|
else
|
||||||
|
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate
|
|
||||||
/usr/bin/tachyon package_postinst
|
/usr/bin/tachyon package_postinst
|
||||||
|
|
||||||
# Harden config: telegram bot_token must not be world-readable
|
# Harden config: telegram bot_token must not be world-readable
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ config settings 'settings'
|
|||||||
option component_auto_update_mode 'immediate'
|
option component_auto_update_mode 'immediate'
|
||||||
option component_auto_update_time '04:00'
|
option component_auto_update_time '04:00'
|
||||||
list component_auto_update_targets 'all'
|
list component_auto_update_targets 'all'
|
||||||
|
option component_backup_enabled '0'
|
||||||
option latency_test_url 'https://www.gstatic.com/generate_204'
|
option latency_test_url 'https://www.gstatic.com/generate_204'
|
||||||
option download_lists_via_proxy '0'
|
option download_lists_via_proxy '0'
|
||||||
option download_components_via_proxy '0'
|
option download_components_via_proxy '0'
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ function show_help() {
|
|||||||
print(" snapshot_save <name> Save a config snapshot\n");
|
print(" snapshot_save <name> Save a config snapshot\n");
|
||||||
print(" snapshot_restore <file> Restore a config snapshot\n");
|
print(" snapshot_restore <file> Restore a config snapshot\n");
|
||||||
print(" snapshot_delete <file> Delete a config snapshot\n");
|
print(" snapshot_delete <file> Delete a config snapshot\n");
|
||||||
|
print(" import_settings [file] Scan and import legacy settings from Forkop/Podkop or backup\n");
|
||||||
print(" validate_nfqws_strategy_json\n");
|
print(" validate_nfqws_strategy_json\n");
|
||||||
print(" Validate an NFQWS strategy and print JSON result\n");
|
print(" Validate an NFQWS strategy and print JSON result\n");
|
||||||
print(" validate_nfqws2_strategy_json\n");
|
print(" validate_nfqws2_strategy_json\n");
|
||||||
@@ -280,7 +281,9 @@ function command_spec(command) {
|
|||||||
snapshot_list: [ "service/snapshot.uc", "snapshot-list", 0 ],
|
snapshot_list: [ "service/snapshot.uc", "snapshot-list", 0 ],
|
||||||
snapshot_save: [ "service/snapshot.uc", "snapshot-save", 1 ],
|
snapshot_save: [ "service/snapshot.uc", "snapshot-save", 1 ],
|
||||||
snapshot_restore: [ "service/snapshot.uc", "snapshot-restore", 1 ],
|
snapshot_restore: [ "service/snapshot.uc", "snapshot-restore", 1 ],
|
||||||
snapshot_delete: [ "service/snapshot.uc", "snapshot-delete", 1 ]
|
snapshot_delete: [ "service/snapshot.uc", "snapshot-delete", 1 ],
|
||||||
|
import_settings: [ "config/migration.uc", "import-settings", 1 ],
|
||||||
|
"import-settings": [ "config/migration.uc", "import-settings", 1 ]
|
||||||
};
|
};
|
||||||
|
|
||||||
return commands[as_string(command)];
|
return commands[as_string(command)];
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ let bool_option = common.bool_option;
|
|||||||
let object_or_empty = common.object_or_empty;
|
let object_or_empty = common.object_or_empty;
|
||||||
|
|
||||||
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
||||||
|
const CONFIG_DIR = getenv("TACHYON_CONFIG_DIR") || "/etc/config";
|
||||||
|
const TARGET_CONFIG = getenv("TACHYON_CONFIG_FILE") || (CONFIG_DIR + "/" + CONFIG_NAME);
|
||||||
const TMP_SUBSCRIPTION_FOLDER = getenv("TMP_SUBSCRIPTION_FOLDER") || "/tmp/sing-box/subscriptions";
|
const TMP_SUBSCRIPTION_FOLDER = getenv("TMP_SUBSCRIPTION_FOLDER") || "/tmp/sing-box/subscriptions";
|
||||||
const TACHYON_RUNTIME_STATE_DIR = getenv("TACHYON_RUNTIME_STATE_DIR") || "/var/run/tachyon";
|
const TACHYON_RUNTIME_STATE_DIR = getenv("TACHYON_RUNTIME_STATE_DIR") || "/var/run/tachyon";
|
||||||
const TACHYON_SUBSCRIPTION_LINKS_DIR = getenv("TACHYON_SUBSCRIPTION_LINKS_DIR") || TACHYON_RUNTIME_STATE_DIR + "/subscription-links";
|
const TACHYON_SUBSCRIPTION_LINKS_DIR = getenv("TACHYON_SUBSCRIPTION_LINKS_DIR") || TACHYON_RUNTIME_STATE_DIR + "/subscription-links";
|
||||||
@@ -1701,6 +1703,196 @@ function migrate_fixture(path, source) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detect_config_migration_source(path) {
|
||||||
|
let content = fs.readfile(path);
|
||||||
|
if (content == null || content == "")
|
||||||
|
return "tachyon";
|
||||||
|
|
||||||
|
if (match(content, /config[ \t]+rule[ \t]+/) ||
|
||||||
|
match(content, /option[ \t]+domain_list_urls/) ||
|
||||||
|
match(content, /option[ \t]+routing_excluded_ips/) ||
|
||||||
|
match(content, /option[ \t]+connection_type[ \t]+['"]?proxy/) ||
|
||||||
|
match(content, /option[ \t]+proxy_string/) ||
|
||||||
|
match(content, /option[ \t]+ip_cidr/)) {
|
||||||
|
return "podkop";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "tachyon";
|
||||||
|
}
|
||||||
|
|
||||||
|
function scan_legacy_config_candidates() {
|
||||||
|
let base_cfg_dir = CONFIG_DIR;
|
||||||
|
let known = [
|
||||||
|
base_cfg_dir + "/forkop",
|
||||||
|
base_cfg_dir + "/forkop_plus",
|
||||||
|
base_cfg_dir + "/podkop",
|
||||||
|
base_cfg_dir + "/podkop_plus",
|
||||||
|
base_cfg_dir + "/netshift",
|
||||||
|
"/tmp/legacy-config.backup",
|
||||||
|
"/etc/.tachyon/.cfg-backup.pre-install"
|
||||||
|
];
|
||||||
|
let candidates = [];
|
||||||
|
let seen = {};
|
||||||
|
|
||||||
|
// 1. Check if current active config itself contains unconverted legacy rules
|
||||||
|
let current_tachyon = TARGET_CONFIG;
|
||||||
|
if (detect_config_migration_source(current_tachyon) == "podkop") {
|
||||||
|
let st = fs.stat(current_tachyon);
|
||||||
|
if (st != null && (st.size || 0) > 10) {
|
||||||
|
push(candidates, {
|
||||||
|
path: current_tachyon,
|
||||||
|
size: st.size || 0,
|
||||||
|
mtime: st.mtime || 0,
|
||||||
|
in_place_legacy: true
|
||||||
|
});
|
||||||
|
seen[current_tachyon] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let path in known) {
|
||||||
|
if (seen[path]) continue;
|
||||||
|
let st = fs.stat(path);
|
||||||
|
if (st != null && (st.type == "file" || st.type == null) && (st.size || 0) > 10) {
|
||||||
|
push(candidates, { path: path, size: st.size || 0, mtime: st.mtime || 0 });
|
||||||
|
seen[path] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let globs = [
|
||||||
|
base_cfg_dir + "/*forkop*",
|
||||||
|
base_cfg_dir + "/*podkop*",
|
||||||
|
base_cfg_dir + "/*netshift*",
|
||||||
|
base_cfg_dir + "/tachyon.backup*",
|
||||||
|
base_cfg_dir + "/tachyon.bak*",
|
||||||
|
base_cfg_dir + "/tachyon.pre-import-*",
|
||||||
|
base_cfg_dir + "/tachyon.pre-install*",
|
||||||
|
"/tmp/legacy-config*",
|
||||||
|
"/tmp/*forkop*",
|
||||||
|
"/tmp/*podkop*",
|
||||||
|
"/tmp/*tachyon*",
|
||||||
|
"/tmp/tmp.*/legacy-config*",
|
||||||
|
"/etc/.tachyon/*",
|
||||||
|
"/root/*forkop*",
|
||||||
|
"/root/*podkop*",
|
||||||
|
"/root/*tachyon*",
|
||||||
|
"/root/*.backup*",
|
||||||
|
"/root/*.bak*"
|
||||||
|
];
|
||||||
|
for (let g in globs) {
|
||||||
|
let paths = fs.glob(g);
|
||||||
|
if (type(paths) == "array") {
|
||||||
|
for (let path in paths) {
|
||||||
|
if (seen[path])
|
||||||
|
continue;
|
||||||
|
let st = fs.stat(path);
|
||||||
|
if (st != null && (st.type == "file" || st.type == null) && (st.size || 0) > 10) {
|
||||||
|
push(candidates, { path: path, size: st.size || 0, mtime: st.mtime || 0 });
|
||||||
|
seen[path] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
function import_settings_cli(source_path) {
|
||||||
|
source_path = trim(as_string(source_path));
|
||||||
|
let target_config = TARGET_CONFIG;
|
||||||
|
|
||||||
|
print("=== Tachyon Settings Importer ===\n\n");
|
||||||
|
|
||||||
|
let chosen_path = "";
|
||||||
|
let candidate_list = [];
|
||||||
|
|
||||||
|
if (source_path != "") {
|
||||||
|
let st = fs.stat(source_path);
|
||||||
|
if (st == null || (st.type != "file" && st.type != null)) {
|
||||||
|
warn("Error: Specified configuration file '" + source_path + "' does not exist or is not readable.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
chosen_path = source_path;
|
||||||
|
print("[1/3] Using specified configuration file:\n");
|
||||||
|
print(" ✓ " + chosen_path + " (" + sprintf("%.1f KB", (st.size || 0) / 1024.0) + ")\n\n");
|
||||||
|
} else {
|
||||||
|
print("[1/3] Scanning system for legacy or backup configurations...\n");
|
||||||
|
candidate_list = scan_legacy_config_candidates();
|
||||||
|
if (length(candidate_list) == 0) {
|
||||||
|
print(" Notice: No legacy or backup configurations found in standard directories.\n\n");
|
||||||
|
print("To locate any leftover configurations, you can search with:\n");
|
||||||
|
print(" grep -rnE 'vless://|vmess://|ss://|trojan://|config rule' /etc/ /root/ /tmp/ 2>/dev/null\n\n");
|
||||||
|
print("And specify the file path directly:\n");
|
||||||
|
print(" tachyon import_settings /path/to/backup-file\n\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(" Found candidates:\n");
|
||||||
|
for (let i = 0; i < length(candidate_list); i++) {
|
||||||
|
let c = candidate_list[i];
|
||||||
|
let note = c.in_place_legacy ? " (unconverted legacy rules in active config)" : "";
|
||||||
|
print(" [" + (i + 1) + "] " + c.path + " (" + sprintf("%.1f KB", c.size / 1024.0) + ")" + note + "\n");
|
||||||
|
}
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
chosen_path = candidate_list[0].path;
|
||||||
|
print(" ✓ Selected primary candidate: " + chosen_path + "\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("[2/3] Backing up current Tachyon configuration...\n");
|
||||||
|
let current_st = fs.stat(target_config);
|
||||||
|
if (current_st != null && (current_st.size || 0) > 0) {
|
||||||
|
let date_stamp = trim(command_output("date +%Y%m%d_%H%M%S 2>/dev/null || date +%s"));
|
||||||
|
if (date_stamp == "") date_stamp = "backup";
|
||||||
|
let backup_path = target_config + ".pre-import-" + date_stamp;
|
||||||
|
if (system("cp -a " + shell_quote(target_config) + " " + shell_quote(backup_path)) == 0) {
|
||||||
|
print(" ✓ Current configuration backed up to: " + backup_path + "\n\n");
|
||||||
|
} else {
|
||||||
|
warn(" Warning: Could not create safety backup of current configuration.\n\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(" ✓ No existing configuration to back up.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("[3/3] Migrating configuration structure...\n");
|
||||||
|
if (chosen_path != target_config) {
|
||||||
|
let target_dir = "";
|
||||||
|
let idx = rindex(target_config, "/");
|
||||||
|
if (idx > 0) target_dir = substr(target_config, 0, idx);
|
||||||
|
if (target_dir != "" && fs.stat(target_dir) == null) {
|
||||||
|
system("mkdir -p " + shell_quote(target_dir) + " 2>/dev/null");
|
||||||
|
}
|
||||||
|
if (system("cp -a " + shell_quote(chosen_path) + " " + shell_quote(target_config)) != 0) {
|
||||||
|
warn("Error: Failed to copy " + chosen_path + " to " + target_config + "\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system("chmod 0600 " + shell_quote(target_config) + " 2>/dev/null");
|
||||||
|
|
||||||
|
let source_type = detect_config_migration_source(target_config);
|
||||||
|
print(" ✓ Detected format: " + (source_type == "podkop" ? "Legacy Forkop / Podkop" : "Tachyon") + "\n");
|
||||||
|
|
||||||
|
let ok = migrate_runtime(source_type);
|
||||||
|
if (!ok) {
|
||||||
|
warn("Error: Configuration migration failed during UCI commit.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
system("chmod 0600 " + shell_quote(target_config) + " 2>/dev/null");
|
||||||
|
print(" ✓ Configuration successfully migrated and verified.\n\n");
|
||||||
|
|
||||||
|
if (fs.stat("/etc/init.d/tachyon") != null) {
|
||||||
|
print("Restarting Tachyon service...\n");
|
||||||
|
system("/usr/bin/tachyon restart >/dev/null 2>&1");
|
||||||
|
print(" ✓ Tachyon restarted with imported configuration.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("====================================================\n");
|
||||||
|
print("SUCCESS: Settings successfully imported from:\n");
|
||||||
|
print(" " + chosen_path + "\n");
|
||||||
|
print("====================================================\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function main(argv) {
|
function main(argv) {
|
||||||
let mode = argv[0] || "";
|
let mode = argv[0] || "";
|
||||||
|
|
||||||
@@ -1708,6 +1900,8 @@ function main(argv) {
|
|||||||
return migrate_runtime("tachyon") ? 0 : 1;
|
return migrate_runtime("tachyon") ? 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")
|
||||||
|
return import_settings_cli(argv[1]) ? 0 : 1;
|
||||||
if (mode == "commit")
|
if (mode == "commit")
|
||||||
return commit_runtime() ? 0 : 1;
|
return commit_runtime() ? 0 : 1;
|
||||||
if (mode == "migrate-fixture") {
|
if (mode == "migrate-fixture") {
|
||||||
@@ -1717,6 +1911,7 @@ function main(argv) {
|
|||||||
|
|
||||||
warn("Usage: config/migration.uc migrate\n");
|
warn("Usage: config/migration.uc migrate\n");
|
||||||
warn(" config/migration.uc migrate-podkop\n");
|
warn(" config/migration.uc migrate-podkop\n");
|
||||||
|
warn(" config/migration.uc import-settings [file]\n");
|
||||||
warn(" config/migration.uc commit\n");
|
warn(" config/migration.uc commit\n");
|
||||||
warn(" config/migration.uc migrate-fixture <fixture.json> [tachyon|podkop]\n");
|
warn(" config/migration.uc migrate-fixture <fixture.json> [tachyon|podkop]\n");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1728,6 +1923,9 @@ function module_exports() {
|
|||||||
migrate_model: migrate_model,
|
migrate_model: migrate_model,
|
||||||
migrate_tachyon_model: migrate_tachyon_model,
|
migrate_tachyon_model: migrate_tachyon_model,
|
||||||
migrate_podkop_model: migrate_podkop_model,
|
migrate_podkop_model: migrate_podkop_model,
|
||||||
|
scan_legacy_config_candidates: scan_legacy_config_candidates,
|
||||||
|
detect_config_migration_source: detect_config_migration_source,
|
||||||
|
import_settings_cli: import_settings_cli,
|
||||||
mark_internal_config_guard: mark_internal_config_guard
|
mark_internal_config_guard: mark_internal_config_guard
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||||
|
TACHYON_BIN="$ROOT_DIR/tachyon/files/usr/bin/tachyon"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Verify CLI mapping in /usr/bin/tachyon
|
||||||
|
grep -Fq 'import_settings: [ "config/migration.uc", "import-settings", 1 ]' "$TACHYON_BIN" ||
|
||||||
|
fail "tachyon CLI must map import_settings to migration.uc"
|
||||||
|
grep -Fq '"import-settings": [ "config/migration.uc", "import-settings", 1 ]' "$TACHYON_BIN" ||
|
||||||
|
fail "tachyon CLI must map import-settings alias"
|
||||||
|
|
||||||
|
# 2. Test import from explicit legacy Forkop file
|
||||||
|
MOCK_ETC="$WORK_DIR/etc"
|
||||||
|
mkdir -p "$MOCK_ETC/config"
|
||||||
|
|
||||||
|
cat > "$MOCK_ETC/config/forkop" <<'EOF_FORKOP'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '1'
|
||||||
|
option dns_server '8.8.8.8'
|
||||||
|
|
||||||
|
config rule 'youtube'
|
||||||
|
option enabled '1'
|
||||||
|
option connection_type 'proxy'
|
||||||
|
option proxy_config_type 'url'
|
||||||
|
option proxy_string 'vless://user@1.2.3.4:443?security=tls#node1'
|
||||||
|
list domain 'youtube.com'
|
||||||
|
list domain 'googlevideo.com'
|
||||||
|
EOF_FORKOP
|
||||||
|
|
||||||
|
# Initial dummy tachyon config
|
||||||
|
cat > "$MOCK_ETC/config/tachyon" <<'EOF_TACHYON'
|
||||||
|
config settings 'settings'
|
||||||
|
option enabled '0'
|
||||||
|
EOF_TACHYON
|
||||||
|
|
||||||
|
cat > "$WORK_DIR/test_import.state" <<'EOF_STATE'
|
||||||
|
tachyon.settings=settings
|
||||||
|
tachyon.settings.enabled=1
|
||||||
|
tachyon.settings.dns_server=8.8.8.8
|
||||||
|
tachyon.youtube=section
|
||||||
|
tachyon.youtube.enabled=1
|
||||||
|
tachyon.youtube.action=proxy
|
||||||
|
tachyon.youtube.proxy_config_type=url
|
||||||
|
tachyon.youtube.proxy_string=vless://user@1.2.3.4:443?security=tls#node1
|
||||||
|
EOF_STATE
|
||||||
|
|
||||||
|
OUTPUT="$(TACHYON_LIB="$TACHYON_LIB" \
|
||||||
|
TACHYON_CONFIG_FILE="$MOCK_ETC/config/tachyon" \
|
||||||
|
TACHYON_CONFIG_DIR="$MOCK_ETC/config" \
|
||||||
|
TACHYON_UCI_STATE_FILE="$WORK_DIR/test_import.state" \
|
||||||
|
TACHYON_UCI_LOG_FILE="$WORK_DIR/test_import.log" \
|
||||||
|
TACHYON_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||||
|
ucode -L "$TACHYON_LIB" "$MIGRATION" import-settings "$MOCK_ETC/config/forkop")"
|
||||||
|
|
||||||
|
echo "$OUTPUT" | grep -Fq 'SUCCESS: Settings successfully imported' ||
|
||||||
|
fail "import_settings must report success on valid legacy file"
|
||||||
|
|
||||||
|
echo "$OUTPUT" | grep -Fq 'Legacy Forkop / Podkop' ||
|
||||||
|
fail "import_settings must recognize legacy format"
|
||||||
|
|
||||||
|
# 3. Test auto-scan in mock directory
|
||||||
|
SCAN_OUTPUT="$(TACHYON_LIB="$TACHYON_LIB" \
|
||||||
|
TACHYON_CONFIG_FILE="$MOCK_ETC/config/tachyon" \
|
||||||
|
TACHYON_CONFIG_DIR="$MOCK_ETC/config" \
|
||||||
|
TACHYON_UCI_STATE_FILE="$WORK_DIR/test_import.state" \
|
||||||
|
TACHYON_UCI_LOG_FILE="$WORK_DIR/test_import.log" \
|
||||||
|
TACHYON_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||||
|
ucode -L "$TACHYON_LIB" "$MIGRATION" import-settings)"
|
||||||
|
|
||||||
|
echo "$SCAN_OUTPUT" | grep -Fq 'SUCCESS: Settings successfully imported' ||
|
||||||
|
fail "import_settings auto-scan must import legacy file"
|
||||||
|
|
||||||
|
# 4. Test non-existent file path error handling
|
||||||
|
ERR_OUTPUT="$(TACHYON_LIB="$TACHYON_LIB" \
|
||||||
|
TACHYON_CONFIG_FILE="$MOCK_ETC/config/tachyon" \
|
||||||
|
TACHYON_CONFIG_DIR="$MOCK_ETC/config" \
|
||||||
|
TACHYON_UCI_STATE_FILE="$WORK_DIR/test_import.state" \
|
||||||
|
TACHYON_UCI_LOG_FILE="$WORK_DIR/test_import.log" \
|
||||||
|
TACHYON_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||||
|
ucode -L "$TACHYON_LIB" "$MIGRATION" import-settings "$WORK_DIR/nonexistent.conf" 2>&1 || true)"
|
||||||
|
echo "$ERR_OUTPUT" | grep -Fq 'does not exist' ||
|
||||||
|
fail "import_settings must handle missing file with error"
|
||||||
|
|
||||||
|
printf 'import_settings tests passed\n'
|
||||||
Reference in New Issue
Block a user