Files
NeoTachyon/forkop/files/usr/lib/service/package.uc
T
Dushnilin e5c022c2c6 refactor: оптимизация хелперов бэкенда и очистка фронтенд-зависимостей
Проведен рефакторинг кодовой базы для устранения дублирования кода и оптимизации скриптов сборки:

- Бэкенд (ucode):
  - Перенесена функция экранирования шелл-аргументов `shell_quote` в общий модуль `core.common`.
  - Удалены дублирующиеся инлайновые объявления функций `as_string`, `shell_quote` и `read_json_file` из всех 23 файлов бэкенда. Вместо них настроен импорт из общего модуля `core.common`.
  - Валидация IP-адресов и CIDR в файле `core/helpers.uc` переведена на использование методов из модуля `core.ip` (`core_ip.valid_ipv4` и `core_ip.valid_ipv4_cidr`).
  - Проверка исполняемости файла (`test -x`) в скрипте `install.sh` заменена на нативную побитовую проверку прав через `fs.stat`.

- Локализация и сборка перевода:
  - Создан общий модуль `fe-app-forkop/locales-utils.js` для получения информации о пользователе Git (`getGitUser`) в скриптах перевода.
  - Из скриптов `generate-po.js` и `generate-pot.js` удалены локальные копии `getGitUser()`, они импортируют общую функцию.

- Зависимости Node.js фронтенда:
  - В скрипте `fe-app-forkop/watch-upload.js` стандартная библиотека `glob` заменена на `fast-glob`.
  - Устаревший пакет `glob` удален из `devDependencies` в `fe-app-forkop/package.json`.
2026-07-13 10:57:21 +03:00

160 lines
4.6 KiB
Ucode

#!/usr/bin/env ucode
let fs = require("fs");
let uci_core = require("core.uci");
let common = require("core.common");
let as_string = common.as_string;
let shell_quote = common.shell_quote;
function env(name, fallback) {
let value = getenv(name);
return value == null ? as_string(fallback) : as_string(value);
}
const CONFIG_NAME = env("FORKOP_CONFIG_NAME", "forkop");
const RT_TABLES_PATH = env("FORKOP_RT_TABLES", "/etc/iproute2/rt_tables");
const BIN_PATH = env("FORKOP_BIN", "/usr/bin/forkop");
const INIT_PATH = env("FORKOP_INIT", "/etc/init.d/forkop");
const DNS_APPLY_UC = env("FORKOP_DNS_APPLY_UC", "/usr/lib/forkop/dns/apply.uc");
const SING_BOX_INIT = env("FORKOP_SING_BOX_INIT", "/etc/init.d/sing-box");
const SING_BOX_BIN = env("FORKOP_SING_BOX_BIN", "/usr/bin/sing-box");
const SING_BOX_CRONET = env("FORKOP_SING_BOX_CRONET", "/usr/lib/libcronet.so");
const SING_BOX_MANAGED_MARKER = env("SB_MANAGED_SERVICE_MARKER", "Forkop managed sing-box service for binary variants");
const PACKAGE_TEST_MODE = env("FORKOP_PACKAGE_TEST_MODE", "") != "";
function command_from_args(args) {
let parts = [];
for (let arg in args)
push(parts, shell_quote(arg));
return join(" ", parts);
}
function normalize_status(status) {
status = int(status);
return status > 255 ? int(status / 256) : status;
}
function command_success_from_args(args) {
return normalize_status(system(command_from_args(args) + " >/dev/null 2>&1")) == 0;
}
function path_exists(path) {
return fs.stat(as_string(path)) != null;
}
function unlink_if_exists(path) {
if (path_exists(path))
fs.unlink(as_string(path));
}
function remove_rt_tables_entry() {
let data = fs.readfile(RT_TABLES_PATH);
if (data == null)
return true;
let changed = false;
let lines = [];
for (let line in split(data, "\n")) {
if (index(line, "105 forkop") >= 0) {
changed = true;
continue;
}
push(lines, line);
}
return !changed || fs.writefile(RT_TABLES_PATH, join("\n", lines)) != null;
}
function ascii_lower(value) {
let upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let lower = "abcdefghijklmnopqrstuvwxyz";
return replace(as_string(value), /[A-Z]/g, function(ch) {
return substr(lower, index(upper, ch), 1);
});
}
function truthy(value) {
value = ascii_lower(trim(as_string(value)));
return value == "1" || value == "true" || value == "yes" || value == "on";
}
function dont_touch_dhcp_enabled() {
return truthy(uci_core.get(CONFIG_NAME + ".settings.dont_touch_dhcp"));
}
function restore_dnsmasq_if_needed() {
if (dont_touch_dhcp_enabled())
return;
command_success_from_args([ BIN_PATH, "restore_dnsmasq" ]);
if (path_exists(DNS_APPLY_UC))
command_success_from_args([ "ucode", DNS_APPLY_UC, "failsafe-restore" ]);
}
function remove_managed_sing_box() {
let data = fs.readfile(SING_BOX_INIT);
if (data == null || index(data, SING_BOX_MANAGED_MARKER) < 0)
return;
command_success_from_args([ SING_BOX_INIT, "stop" ]);
command_success_from_args([ SING_BOX_INIT, "disable" ]);
unlink_if_exists(SING_BOX_INIT);
unlink_if_exists(SING_BOX_BIN);
unlink_if_exists(SING_BOX_CRONET);
}
function prerm_cleanup() {
if (env("IPKG_INSTROOT", "") != "")
return true;
if (!PACKAGE_TEST_MODE) {
command_success_from_args([ INIT_PATH, "stop" ]);
restore_dnsmasq_if_needed();
remove_managed_sing_box();
}
return remove_rt_tables_entry();
}
function luci_cache_globs() {
let configured = env("FORKOP_LUCI_CACHE_GLOBS", "");
if (configured != "")
return split(configured, /[ \t\r\n]+/);
return [ "/var/luci-indexcache*", "/tmp/luci-indexcache*" ];
}
function remove_luci_index_cache() {
for (let pattern in luci_cache_globs()) {
pattern = as_string(pattern);
if (pattern == "")
continue;
for (let path in fs.glob(pattern))
unlink_if_exists(path);
}
}
function luci_postinst() {
remove_luci_index_cache();
if (!PACKAGE_TEST_MODE) {
if (path_exists("/etc/init.d/rpcd"))
command_success_from_args([ "/etc/init.d/rpcd", "reload" ]);
command_success_from_args([ "logger", "-t", "forkop", "[info] Package defaults applied" ]);
}
return true;
}
let mode = ARGV[0] || "";
if (mode == "prerm")
exit(prerm_cleanup() ? 0 : 1);
else if (mode == "remove-rt-tables-entry")
exit(remove_rt_tables_entry() ? 0 : 1);
else if (mode == "luci-postinst")
exit(luci_postinst() ? 0 : 1);
else {
warn("Usage: service/package.uc <prerm|remove-rt-tables-entry|luci-postinst>\n");
exit(1);
}