Improve local IP routing settings
This commit is contained in:
+309
-277
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-05-21 17:44+0300\n"
|
||||
"PO-Revision-Date: 2026-05-21 17:44+0300\n"
|
||||
"POT-Creation-Date: 2026-05-23 07:37+0300\n"
|
||||
"PO-Revision-Date: 2026-05-23 07:37+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -50,6 +50,9 @@ msgstr "Найдены дополнительные правила маркир
|
||||
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
|
||||
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
|
||||
|
||||
msgid "Allows limiting the list of servers for URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applicable for SOCKS and Shadowsocks links"
|
||||
msgstr "Применимо для ссылок SOCKS и Shadowsocks"
|
||||
|
||||
@@ -74,11 +77,8 @@ msgstr "Backend-проверка завершилась ошибкой: %s"
|
||||
msgid "Binary file"
|
||||
msgstr "Бинарный файл"
|
||||
|
||||
msgid "Blacklist (Exclude)"
|
||||
msgstr "Черный список (Исключить)"
|
||||
|
||||
msgid "Blacklist: test all servers except selected ones. Whitelist: test only selected servers."
|
||||
msgstr "Черный список: проверять все серверы, кроме указанных. Белый список: проверять только указанные серверы."
|
||||
msgid "Blacklist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bootsrap DNS"
|
||||
msgstr "Bootstrap DNS"
|
||||
@@ -233,6 +233,9 @@ msgstr "Отключить QUIC"
|
||||
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
|
||||
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "DNS on router"
|
||||
msgstr "DNS на роутере"
|
||||
|
||||
@@ -314,12 +317,18 @@ msgstr "Изменить"
|
||||
msgid "Enable"
|
||||
msgstr "Включить"
|
||||
|
||||
msgid "Enable automatic updates for remote lists and rule sets"
|
||||
msgstr "Включить автоматическое обновление удалённых списков и наборов правил"
|
||||
|
||||
msgid "Enable autostart"
|
||||
msgstr "Включить автостарт"
|
||||
|
||||
msgid "Enable built-in DNS resolver for domains handled by this section"
|
||||
msgstr "Включить встроенный DNS-резолвер для доменов, обрабатываемых в этой секции"
|
||||
|
||||
msgid "Enable list updates"
|
||||
msgstr "Включить обновление списков"
|
||||
|
||||
msgid "Enable Mixed Proxy"
|
||||
msgstr "Включить смешанный прокси"
|
||||
|
||||
@@ -398,6 +407,9 @@ msgstr "Как часто обновлять этот удалённый наб
|
||||
msgid "HTTP error"
|
||||
msgstr "Ошибка HTTP"
|
||||
|
||||
msgid "Include country in URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Include server by regular expression in URLTest"
|
||||
msgstr "Включить сервер по регулярному выражению в URLTest"
|
||||
|
||||
@@ -650,6 +662,9 @@ msgstr "Не отвечает"
|
||||
msgid "Not running"
|
||||
msgstr "Не запущено"
|
||||
|
||||
msgid "Only servers from selected countries will be tested by URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Only servers whose names match these regular expressions will be checked through URLTest"
|
||||
msgstr "Только те серверы, имена которых совпадают с этими регулярными выражениями, будут проверяться через URLTest"
|
||||
|
||||
@@ -842,6 +857,9 @@ msgstr "Выберите предустановленный набор прав
|
||||
msgid "Select DNS protocol to use"
|
||||
msgstr "Выберите протокол DNS"
|
||||
|
||||
msgid "Select local devices or enter IP addresses/subnets to be excluded from routing"
|
||||
msgstr "Выберите локальные устройства или введите IP-адреса/подсети для исключения из маршрутизации"
|
||||
|
||||
msgid "Select network interface for VPN connection"
|
||||
msgstr "Выберите сетевой интерфейс для VPN-подключения"
|
||||
|
||||
@@ -920,9 +938,6 @@ msgstr "Исходные IP-адреса"
|
||||
msgid "Source Network Interface"
|
||||
msgstr "Сетевой интерфейс источника"
|
||||
|
||||
msgid "Specify a local IP address to be excluded from routing"
|
||||
msgstr "Укажите локальный IP-адрес, который следует исключить из маршрутизации."
|
||||
|
||||
msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
|
||||
msgstr "Укажите локальные IP-адреса или подсети, трафик которых всегда будет маршрутизироваться через маршрут этой секции"
|
||||
|
||||
@@ -1103,8 +1118,8 @@ msgstr "URL должен использовать один из следующи
|
||||
msgid "URLTest interval"
|
||||
msgstr "Интервал URLTest"
|
||||
|
||||
msgid "URLTest server filter mode"
|
||||
msgstr "Режим фильтра серверов URLTest"
|
||||
msgid "URLTest server filtering"
|
||||
msgstr ""
|
||||
|
||||
msgid "URLTest tolerance"
|
||||
msgstr "Допуск URLTest"
|
||||
@@ -1118,9 +1133,6 @@ msgstr "Используйте формат: URL | User-Agent"
|
||||
msgid "Use sing-box duration format like 1d, 12h or 30m"
|
||||
msgstr "Используйте формат длительности sing-box, например 1d, 12h или 30m"
|
||||
|
||||
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
|
||||
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить автоматическое обновление."
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "Валидно"
|
||||
|
||||
@@ -1145,8 +1157,8 @@ msgstr "Предупреждение: Russia inside может быть испо
|
||||
msgid "What Podkop Plus should do when this section matches"
|
||||
msgstr "Что Podkop Plus должен делать при совпадении этой секции"
|
||||
|
||||
msgid "Whitelist (Only include)"
|
||||
msgstr "Белый список (Только включить)"
|
||||
msgid "Whitelist"
|
||||
msgstr ""
|
||||
|
||||
msgid "YACD Secret Key"
|
||||
msgstr "Секретный ключ YACD"
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
"use strict";
|
||||
"require baseclass";
|
||||
"require rpc";
|
||||
"require ui";
|
||||
"require view.podkop_plus.main as main";
|
||||
|
||||
const callHostHints = rpc.declare({
|
||||
object: "luci-rpc",
|
||||
method: "getHostHints",
|
||||
expect: { "": {} },
|
||||
});
|
||||
const callDHCPLeases = rpc.declare({
|
||||
object: "luci-rpc",
|
||||
method: "getDHCPLeases",
|
||||
expect: { "": {} },
|
||||
});
|
||||
const callNetworkInterfaceDump = rpc.declare({
|
||||
object: "network.interface",
|
||||
method: "dump",
|
||||
expect: { interface: [] },
|
||||
});
|
||||
|
||||
let localDeviceChoicesCache = null;
|
||||
let localDeviceChoicesPromise = null;
|
||||
|
||||
function normalizeOptionValues(value) {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.filter(Boolean)
|
||||
.map((item) => `${item}`.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return `${value}`
|
||||
.split(/\s+/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function normalizeLocalDeviceName(name) {
|
||||
return `${name || ""}`.trim().replace(/\.lan$/i, "");
|
||||
}
|
||||
|
||||
function addLocalDeviceChoice(choices, ip, name) {
|
||||
const normalizedIp = `${ip || ""}`.trim();
|
||||
const normalizedName = normalizeLocalDeviceName(name);
|
||||
|
||||
if (!normalizedIp || !normalizedName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!main.validateIPV4(normalizedIp).valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
choices[normalizedIp] = normalizedName;
|
||||
}
|
||||
|
||||
function addRouterIp(routerIps, ip) {
|
||||
const normalizedIp = `${ip || ""}`.trim();
|
||||
|
||||
if (!normalizedIp || !main.validateIPV4(normalizedIp).valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
routerIps[normalizedIp] = true;
|
||||
}
|
||||
|
||||
function buildRouterIpMap(networkInterfaces) {
|
||||
const routerIps = {};
|
||||
|
||||
if (!Array.isArray(networkInterfaces)) {
|
||||
return routerIps;
|
||||
}
|
||||
|
||||
networkInterfaces.forEach((networkInterface) => {
|
||||
const ipv4Addresses =
|
||||
networkInterface &&
|
||||
typeof networkInterface === "object" &&
|
||||
Array.isArray(networkInterface["ipv4-address"])
|
||||
? networkInterface["ipv4-address"]
|
||||
: [];
|
||||
|
||||
ipv4Addresses.forEach((address) => {
|
||||
addRouterIp(
|
||||
routerIps,
|
||||
address && typeof address === "object" ? address.address : address,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return routerIps;
|
||||
}
|
||||
|
||||
function buildLocalDeviceChoices(hostHints, dhcpLeases, networkInterfaces) {
|
||||
const choices = {};
|
||||
const routerIps = buildRouterIpMap(networkInterfaces);
|
||||
|
||||
if (hostHints && typeof hostHints === "object") {
|
||||
Object.values(hostHints).forEach((hint) => {
|
||||
if (!hint || typeof hint !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
normalizeOptionValues(hint.ipaddrs || hint.ipv4).forEach((ip) => {
|
||||
addLocalDeviceChoice(choices, ip, hint.name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (dhcpLeases && Array.isArray(dhcpLeases.dhcp_leases)) {
|
||||
dhcpLeases.dhcp_leases.forEach((lease) => {
|
||||
if (!lease || typeof lease !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
addLocalDeviceChoice(choices, lease.ipaddr, lease.hostname);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(routerIps).forEach((ip) => {
|
||||
delete choices[ip];
|
||||
});
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
function loadLocalDeviceChoices() {
|
||||
if (localDeviceChoicesCache) {
|
||||
return Promise.resolve(localDeviceChoicesCache);
|
||||
}
|
||||
|
||||
if (localDeviceChoicesPromise) {
|
||||
return localDeviceChoicesPromise;
|
||||
}
|
||||
|
||||
localDeviceChoicesPromise = Promise.all([
|
||||
callHostHints().catch(() => ({})),
|
||||
callDHCPLeases().catch(() => ({})),
|
||||
callNetworkInterfaceDump().catch(() => []),
|
||||
])
|
||||
.then(([hostHints, dhcpLeases, networkInterfaces]) => {
|
||||
localDeviceChoicesCache = buildLocalDeviceChoices(
|
||||
hostHints,
|
||||
dhcpLeases,
|
||||
networkInterfaces,
|
||||
);
|
||||
return localDeviceChoicesCache;
|
||||
})
|
||||
.finally(() => {
|
||||
localDeviceChoicesPromise = null;
|
||||
});
|
||||
|
||||
return localDeviceChoicesPromise;
|
||||
}
|
||||
|
||||
function sortLocalDeviceChoiceValues(choices) {
|
||||
return Object.keys(choices).sort((a, b) => {
|
||||
const byName = `${choices[a]}`.localeCompare(`${choices[b]}`);
|
||||
return byName || a.localeCompare(b);
|
||||
});
|
||||
}
|
||||
|
||||
function hasSingleIpValue(values) {
|
||||
return normalizeOptionValues(values).some(
|
||||
(value) => main.validateIPV4(value).valid,
|
||||
);
|
||||
}
|
||||
|
||||
function preloadLocalDeviceChoicesForValues(values) {
|
||||
return hasSingleIpValue(values)
|
||||
? loadLocalDeviceChoices()
|
||||
: Promise.resolve(null);
|
||||
}
|
||||
|
||||
function createLocalDeviceDynamicListWidget(option, section_id, cfgvalue) {
|
||||
const values = normalizeOptionValues(
|
||||
cfgvalue != null ? cfgvalue : option.default,
|
||||
);
|
||||
const shouldResolveExistingLabels = hasSingleIpValue(values);
|
||||
|
||||
return (
|
||||
shouldResolveExistingLabels ? loadLocalDeviceChoices() : Promise.resolve({})
|
||||
).then((initialChoices) => {
|
||||
const choices = localDeviceChoicesCache || initialChoices || {};
|
||||
const widget = new ui.DynamicList(values, choices, {
|
||||
id: option.cbid(section_id),
|
||||
sort: sortLocalDeviceChoiceValues(choices),
|
||||
optional: option.optional || option.rmempty,
|
||||
datatype: option.datatype,
|
||||
placeholder: option.placeholder,
|
||||
validate: option.validate.bind(option, section_id),
|
||||
disabled: option.readonly != null ? option.readonly : option.map.readonly,
|
||||
});
|
||||
const node = widget.render();
|
||||
let choicesLoaded = Boolean(localDeviceChoicesCache);
|
||||
let choicesLoading = false;
|
||||
|
||||
const loadChoices = () => {
|
||||
if (choicesLoaded || choicesLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
choicesLoading = true;
|
||||
loadLocalDeviceChoices()
|
||||
.then((loadedChoices) => {
|
||||
widget.clearChoices();
|
||||
widget.addChoices(
|
||||
sortLocalDeviceChoiceValues(loadedChoices),
|
||||
loadedChoices,
|
||||
);
|
||||
choicesLoaded = true;
|
||||
})
|
||||
.finally(() => {
|
||||
choicesLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const maybeLoadChoices = (ev) => {
|
||||
if (
|
||||
ev.target &&
|
||||
typeof ev.target.closest === "function" &&
|
||||
ev.target.closest(".cbi-dropdown")
|
||||
) {
|
||||
loadChoices();
|
||||
}
|
||||
};
|
||||
|
||||
node.addEventListener("mousedown", maybeLoadChoices, true);
|
||||
node.addEventListener("focusin", maybeLoadChoices, true);
|
||||
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
const EntryPoint = {
|
||||
createLocalDeviceDynamicListWidget,
|
||||
hasSingleIpValue,
|
||||
loadLocalDeviceChoices,
|
||||
normalizeOptionValues,
|
||||
preloadLocalDeviceChoicesForValues,
|
||||
};
|
||||
|
||||
return baseclass.extend(EntryPoint);
|
||||
@@ -2,28 +2,13 @@
|
||||
"require form";
|
||||
"require baseclass";
|
||||
"require fs";
|
||||
"require rpc";
|
||||
"require ui";
|
||||
"require tools.widgets as widgets";
|
||||
"require uci";
|
||||
"require view.podkop_plus.local_devices as localDevices";
|
||||
"require view.podkop_plus.main as main";
|
||||
|
||||
const UCI_PACKAGE = main.PODKOP_UCI_PACKAGE;
|
||||
const callHostHints = rpc.declare({
|
||||
object: "luci-rpc",
|
||||
method: "getHostHints",
|
||||
expect: { "": {} },
|
||||
});
|
||||
const callDHCPLeases = rpc.declare({
|
||||
object: "luci-rpc",
|
||||
method: "getDHCPLeases",
|
||||
expect: { "": {} },
|
||||
});
|
||||
const callNetworkInterfaceDump = rpc.declare({
|
||||
object: "network.interface",
|
||||
method: "dump",
|
||||
expect: { interface: [] },
|
||||
});
|
||||
|
||||
function valuesToText(values) {
|
||||
if (!values) {
|
||||
@@ -556,193 +541,6 @@ function writeListOption(section_id, key, values) {
|
||||
}
|
||||
}
|
||||
|
||||
let localDeviceChoicesCache = null;
|
||||
let localDeviceChoicesPromise = null;
|
||||
|
||||
function normalizeLocalDeviceName(name) {
|
||||
return `${name || ""}`.trim().replace(/\.lan$/i, "");
|
||||
}
|
||||
|
||||
function addLocalDeviceChoice(choices, ip, name) {
|
||||
const normalizedIp = `${ip || ""}`.trim();
|
||||
const normalizedName = normalizeLocalDeviceName(name);
|
||||
|
||||
if (!normalizedIp || !normalizedName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!main.validateIPV4(normalizedIp).valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
choices[normalizedIp] = normalizedName;
|
||||
}
|
||||
|
||||
function addRouterIp(routerIps, ip) {
|
||||
const normalizedIp = `${ip || ""}`.trim();
|
||||
|
||||
if (!normalizedIp || !main.validateIPV4(normalizedIp).valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
routerIps[normalizedIp] = true;
|
||||
}
|
||||
|
||||
function buildRouterIpMap(networkInterfaces) {
|
||||
const routerIps = {};
|
||||
|
||||
if (!Array.isArray(networkInterfaces)) {
|
||||
return routerIps;
|
||||
}
|
||||
|
||||
networkInterfaces.forEach((networkInterface) => {
|
||||
const ipv4Addresses =
|
||||
networkInterface &&
|
||||
typeof networkInterface === "object" &&
|
||||
Array.isArray(networkInterface["ipv4-address"])
|
||||
? networkInterface["ipv4-address"]
|
||||
: [];
|
||||
|
||||
ipv4Addresses.forEach((address) => {
|
||||
addRouterIp(
|
||||
routerIps,
|
||||
address && typeof address === "object" ? address.address : address,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return routerIps;
|
||||
}
|
||||
|
||||
function buildLocalDeviceChoices(hostHints, dhcpLeases, networkInterfaces) {
|
||||
const choices = {};
|
||||
const routerIps = buildRouterIpMap(networkInterfaces);
|
||||
|
||||
if (hostHints && typeof hostHints === "object") {
|
||||
Object.values(hostHints).forEach((hint) => {
|
||||
if (!hint || typeof hint !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
normalizeOptionValues(hint.ipaddrs || hint.ipv4).forEach((ip) => {
|
||||
addLocalDeviceChoice(choices, ip, hint.name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (dhcpLeases && Array.isArray(dhcpLeases.dhcp_leases)) {
|
||||
dhcpLeases.dhcp_leases.forEach((lease) => {
|
||||
if (!lease || typeof lease !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
addLocalDeviceChoice(choices, lease.ipaddr, lease.hostname);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(routerIps).forEach((ip) => {
|
||||
delete choices[ip];
|
||||
});
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
function loadLocalDeviceChoices(refresh) {
|
||||
if (!refresh && localDeviceChoicesCache) {
|
||||
return Promise.resolve(localDeviceChoicesCache);
|
||||
}
|
||||
|
||||
if (!refresh && localDeviceChoicesPromise) {
|
||||
return localDeviceChoicesPromise;
|
||||
}
|
||||
|
||||
localDeviceChoicesPromise = Promise.all([
|
||||
callHostHints().catch(() => ({})),
|
||||
callDHCPLeases().catch(() => ({})),
|
||||
callNetworkInterfaceDump().catch(() => []),
|
||||
])
|
||||
.then(([hostHints, dhcpLeases, networkInterfaces]) => {
|
||||
localDeviceChoicesCache = buildLocalDeviceChoices(
|
||||
hostHints,
|
||||
dhcpLeases,
|
||||
networkInterfaces,
|
||||
);
|
||||
return localDeviceChoicesCache;
|
||||
})
|
||||
.finally(() => {
|
||||
localDeviceChoicesPromise = null;
|
||||
});
|
||||
|
||||
return localDeviceChoicesPromise;
|
||||
}
|
||||
|
||||
function sortLocalDeviceChoiceValues(choices) {
|
||||
return Object.keys(choices).sort((a, b) => {
|
||||
const byName = `${choices[a]}`.localeCompare(`${choices[b]}`);
|
||||
return byName || a.localeCompare(b);
|
||||
});
|
||||
}
|
||||
|
||||
function hasSingleIpValue(values) {
|
||||
return values.some((value) => main.validateIPV4(value).valid);
|
||||
}
|
||||
|
||||
function createLocalDeviceDynamicListWidget(option, section_id, cfgvalue) {
|
||||
const values = normalizeOptionValues(
|
||||
cfgvalue != null ? cfgvalue : option.default,
|
||||
);
|
||||
const shouldResolveExistingLabels = hasSingleIpValue(values);
|
||||
|
||||
return (
|
||||
shouldResolveExistingLabels ? loadLocalDeviceChoices() : Promise.resolve({})
|
||||
).then((initialChoices) => {
|
||||
const widget = new ui.DynamicList(values, initialChoices || {}, {
|
||||
id: option.cbid(section_id),
|
||||
sort: sortLocalDeviceChoiceValues(initialChoices || {}),
|
||||
optional: option.optional || option.rmempty,
|
||||
datatype: option.datatype,
|
||||
placeholder: option.placeholder,
|
||||
validate: option.validate.bind(option, section_id),
|
||||
disabled: option.readonly != null ? option.readonly : option.map.readonly,
|
||||
});
|
||||
const node = widget.render();
|
||||
let choicesLoaded = shouldResolveExistingLabels;
|
||||
let choicesLoading = false;
|
||||
|
||||
const loadChoices = () => {
|
||||
if (choicesLoaded || choicesLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
choicesLoading = true;
|
||||
loadLocalDeviceChoices(true)
|
||||
.then((choices) => {
|
||||
widget.clearChoices();
|
||||
widget.addChoices(sortLocalDeviceChoiceValues(choices), choices);
|
||||
choicesLoaded = true;
|
||||
})
|
||||
.finally(() => {
|
||||
choicesLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const maybeLoadChoices = (ev) => {
|
||||
if (
|
||||
ev.target &&
|
||||
typeof ev.target.closest === "function" &&
|
||||
ev.target.closest(".cbi-dropdown")
|
||||
) {
|
||||
loadChoices();
|
||||
}
|
||||
};
|
||||
|
||||
node.addEventListener("mousedown", maybeLoadChoices, true);
|
||||
node.addEventListener("focusin", maybeLoadChoices, true);
|
||||
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
function validateRegex(_section_id, value) {
|
||||
if (!value || !value.length) {
|
||||
return true;
|
||||
@@ -2441,7 +2239,11 @@ function addLocalDeviceSubnetDynamicField(section, config) {
|
||||
uci.unset(UCI_PACKAGE, section_id, `${config.key}_text_mode`);
|
||||
};
|
||||
o.renderWidget = function (section_id, _option_index, cfgvalue) {
|
||||
return createLocalDeviceDynamicListWidget(this, section_id, cfgvalue);
|
||||
return localDevices.createLocalDeviceDynamicListWidget(
|
||||
this,
|
||||
section_id,
|
||||
cfgvalue,
|
||||
);
|
||||
};
|
||||
|
||||
return o;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"require uci";
|
||||
"require baseclass";
|
||||
"require tools.widgets as widgets";
|
||||
"require view.podkop_plus.local_devices as localDevices";
|
||||
"require view.podkop_plus.main as main";
|
||||
|
||||
const UCI_PACKAGE = main.PODKOP_UCI_PACKAGE;
|
||||
@@ -154,9 +155,7 @@ function createSettingsContent(section) {
|
||||
}
|
||||
|
||||
// Reject lan*
|
||||
if (
|
||||
value.startsWith("lan")
|
||||
) {
|
||||
if (value.startsWith("lan")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -250,7 +249,9 @@ function createSettingsContent(section) {
|
||||
form.Flag,
|
||||
"enable_yacd_wan_access",
|
||||
_("Enable YACD WAN Access"),
|
||||
_("Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."),
|
||||
_(
|
||||
"Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
|
||||
),
|
||||
);
|
||||
o.depends("enable_yacd", "1");
|
||||
o.default = "0";
|
||||
@@ -260,7 +261,9 @@ function createSettingsContent(section) {
|
||||
form.Value,
|
||||
"yacd_secret_key",
|
||||
_("YACD Secret Key"),
|
||||
_("Secret key for authenticating remote access to YACD when WAN access is enabled."),
|
||||
_(
|
||||
"Secret key for authenticating remote access to YACD when WAN access is enabled.",
|
||||
),
|
||||
);
|
||||
o.depends("enable_yacd_wan_access", "1");
|
||||
o.rmempty = false;
|
||||
@@ -276,18 +279,27 @@ function createSettingsContent(section) {
|
||||
o.default = "0";
|
||||
o.rmempty = false;
|
||||
|
||||
o = section.option(
|
||||
form.Flag,
|
||||
"list_update_enabled",
|
||||
_("Enable list updates"),
|
||||
_("Enable automatic updates for remote lists and rule sets"),
|
||||
);
|
||||
o.default = "1";
|
||||
o.rmempty = false;
|
||||
|
||||
o = section.option(
|
||||
form.Value,
|
||||
"update_interval",
|
||||
_("List Update Frequency"),
|
||||
_(
|
||||
"Use sing-box duration format. Leave empty to disable automatic updates.",
|
||||
),
|
||||
_("Use sing-box duration format like 1d, 12h or 30m"),
|
||||
);
|
||||
o.depends("list_update_enabled", "1");
|
||||
o.placeholder = "1d";
|
||||
o.rmempty = true;
|
||||
o.default = "1d";
|
||||
o.rmempty = false;
|
||||
o.cfgvalue = function (section_id) {
|
||||
return uci.get(UCI_PACKAGE, section_id, "update_interval") || "";
|
||||
return uci.get(UCI_PACKAGE, section_id, "update_interval") || "1d";
|
||||
};
|
||||
o.write = function (section_id, value) {
|
||||
const normalized = value ? `${value}`.trim() : "";
|
||||
@@ -295,14 +307,14 @@ function createSettingsContent(section) {
|
||||
if (normalized.length) {
|
||||
uci.set(UCI_PACKAGE, section_id, "update_interval", normalized);
|
||||
} else {
|
||||
uci.unset(UCI_PACKAGE, section_id, "update_interval");
|
||||
uci.set(UCI_PACKAGE, section_id, "update_interval", "1d");
|
||||
}
|
||||
};
|
||||
o.validate = function (_section_id, value) {
|
||||
const normalized = value ? `${value}`.trim() : "";
|
||||
|
||||
if (!normalized.length) {
|
||||
return true;
|
||||
return _("Use sing-box duration format like 1d, 12h or 30m");
|
||||
}
|
||||
|
||||
if (isSingBoxDuration(normalized)) {
|
||||
@@ -420,9 +432,7 @@ function createSettingsContent(section) {
|
||||
form.ListValue,
|
||||
"log_level",
|
||||
_("Log Level"),
|
||||
_(
|
||||
"Select the log level for sing-box",
|
||||
),
|
||||
_("Select the log level for sing-box"),
|
||||
);
|
||||
o.value("trace", "Trace");
|
||||
o.value("debug", "Debug");
|
||||
@@ -449,9 +459,11 @@ function createSettingsContent(section) {
|
||||
form.DynamicList,
|
||||
"routing_excluded_ips",
|
||||
_("Routing Excluded IPs"),
|
||||
_("Specify a local IP address to be excluded from routing"),
|
||||
_(
|
||||
"Select local devices or enter IP addresses/subnets to be excluded from routing",
|
||||
),
|
||||
);
|
||||
o.placeholder = "IP";
|
||||
o.placeholder = _("Device, IP, or subnet");
|
||||
o.rmempty = true;
|
||||
o.validate = function (section_id, value) {
|
||||
// Optional
|
||||
@@ -459,7 +471,7 @@ function createSettingsContent(section) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const validation = main.validateIPV4(value);
|
||||
const validation = main.validateSubnet(value);
|
||||
|
||||
if (validation.valid) {
|
||||
return true;
|
||||
@@ -467,6 +479,22 @@ function createSettingsContent(section) {
|
||||
|
||||
return validation.message;
|
||||
};
|
||||
o.load = function (section_id) {
|
||||
const values = localDevices.normalizeOptionValues(
|
||||
uci.get(UCI_PACKAGE, section_id, "routing_excluded_ips"),
|
||||
);
|
||||
|
||||
return localDevices
|
||||
.preloadLocalDeviceChoicesForValues(values)
|
||||
.then(() => values);
|
||||
};
|
||||
o.renderWidget = function (section_id, _option_index, cfgvalue) {
|
||||
return localDevices.createLocalDeviceDynamicListWidget(
|
||||
this,
|
||||
section_id,
|
||||
cfgvalue,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const EntryPoint = {
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-05-21 17:44+0300\n"
|
||||
"PO-Revision-Date: 2026-05-21 17:44+0300\n"
|
||||
"POT-Creation-Date: 2026-05-23 07:37+0300\n"
|
||||
"PO-Revision-Date: 2026-05-23 07:37+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -50,6 +50,9 @@ msgstr "Найдены дополнительные правила маркир
|
||||
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
|
||||
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
|
||||
|
||||
msgid "Allows limiting the list of servers for URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applicable for SOCKS and Shadowsocks links"
|
||||
msgstr "Применимо для ссылок SOCKS и Shadowsocks"
|
||||
|
||||
@@ -74,11 +77,8 @@ msgstr "Backend-проверка завершилась ошибкой: %s"
|
||||
msgid "Binary file"
|
||||
msgstr "Бинарный файл"
|
||||
|
||||
msgid "Allows limiting the list of servers for URLTest"
|
||||
msgstr "Позволяет ограничить список серверов для URLTest"
|
||||
|
||||
msgid "Blacklist"
|
||||
msgstr "Черный список"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bootsrap DNS"
|
||||
msgstr "Bootstrap DNS"
|
||||
@@ -234,7 +234,7 @@ msgid "Disable the QUIC protocol to improve compatibility or fix issues with vid
|
||||
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Отключено"
|
||||
msgstr ""
|
||||
|
||||
msgid "DNS on router"
|
||||
msgstr "DNS на роутере"
|
||||
@@ -317,12 +317,18 @@ msgstr "Изменить"
|
||||
msgid "Enable"
|
||||
msgstr "Включить"
|
||||
|
||||
msgid "Enable automatic updates for remote lists and rule sets"
|
||||
msgstr "Включить автоматическое обновление удалённых списков и наборов правил"
|
||||
|
||||
msgid "Enable autostart"
|
||||
msgstr "Включить автостарт"
|
||||
|
||||
msgid "Enable built-in DNS resolver for domains handled by this section"
|
||||
msgstr "Включить встроенный DNS-резолвер для доменов, обрабатываемых в этой секции"
|
||||
|
||||
msgid "Enable list updates"
|
||||
msgstr "Включить обновление списков"
|
||||
|
||||
msgid "Enable Mixed Proxy"
|
||||
msgstr "Включить смешанный прокси"
|
||||
|
||||
@@ -401,6 +407,9 @@ msgstr "Как часто обновлять этот удалённый наб
|
||||
msgid "HTTP error"
|
||||
msgstr "Ошибка HTTP"
|
||||
|
||||
msgid "Include country in URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Include server by regular expression in URLTest"
|
||||
msgstr "Включить сервер по регулярному выражению в URLTest"
|
||||
|
||||
@@ -653,6 +662,9 @@ msgstr "Не отвечает"
|
||||
msgid "Not running"
|
||||
msgstr "Не запущено"
|
||||
|
||||
msgid "Only servers from selected countries will be tested by URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Only servers whose names match these regular expressions will be checked through URLTest"
|
||||
msgstr "Только те серверы, имена которых совпадают с этими регулярными выражениями, будут проверяться через URLTest"
|
||||
|
||||
@@ -845,6 +857,9 @@ msgstr "Выберите предустановленный набор прав
|
||||
msgid "Select DNS protocol to use"
|
||||
msgstr "Выберите протокол DNS"
|
||||
|
||||
msgid "Select local devices or enter IP addresses/subnets to be excluded from routing"
|
||||
msgstr "Выберите локальные устройства или введите IP-адреса/подсети для исключения из маршрутизации"
|
||||
|
||||
msgid "Select network interface for VPN connection"
|
||||
msgstr "Выберите сетевой интерфейс для VPN-подключения"
|
||||
|
||||
@@ -923,9 +938,6 @@ msgstr "Исходные IP-адреса"
|
||||
msgid "Source Network Interface"
|
||||
msgstr "Сетевой интерфейс источника"
|
||||
|
||||
msgid "Specify a local IP address to be excluded from routing"
|
||||
msgstr "Укажите локальный IP-адрес, который следует исключить из маршрутизации."
|
||||
|
||||
msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
|
||||
msgstr "Укажите локальные IP-адреса или подсети, трафик которых всегда будет маршрутизироваться через маршрут этой секции"
|
||||
|
||||
@@ -1107,7 +1119,7 @@ msgid "URLTest interval"
|
||||
msgstr "Интервал URLTest"
|
||||
|
||||
msgid "URLTest server filtering"
|
||||
msgstr "Фильтрация серверов URLTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "URLTest tolerance"
|
||||
msgstr "Допуск URLTest"
|
||||
@@ -1121,9 +1133,6 @@ msgstr "Используйте формат: URL | User-Agent"
|
||||
msgid "Use sing-box duration format like 1d, 12h or 30m"
|
||||
msgstr "Используйте формат длительности sing-box, например 1d, 12h или 30m"
|
||||
|
||||
msgid "Use sing-box duration format. Leave empty to disable automatic updates."
|
||||
msgstr "Используйте формат длительности sing-box. Оставьте поле пустым, чтобы отключить автоматическое обновление."
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "Валидно"
|
||||
|
||||
@@ -1149,7 +1158,7 @@ msgid "What Podkop Plus should do when this section matches"
|
||||
msgstr "Что Podkop Plus должен делать при совпадении этой секции"
|
||||
|
||||
msgid "Whitelist"
|
||||
msgstr "Белый список"
|
||||
msgstr ""
|
||||
|
||||
msgid "YACD Secret Key"
|
||||
msgstr "Секретный ключ YACD"
|
||||
@@ -1171,9 +1180,3 @@ msgstr "Бинарник провайдера Zapret доступен"
|
||||
|
||||
msgid "Zapret provider binary is not available"
|
||||
msgstr "Бинарник провайдера Zapret недоступен"
|
||||
|
||||
msgid "Include country in URLTest"
|
||||
msgstr "Включить страну в URLTest"
|
||||
|
||||
msgid "Only servers from selected countries will be tested by URLTest"
|
||||
msgstr "Только серверы из выбранных стран будут проверяться через URLTest"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ config settings 'settings'
|
||||
#option badwan_reload_delay '2000'
|
||||
option enable_yacd '0'
|
||||
option disable_quic '0'
|
||||
option list_update_enabled '1'
|
||||
option update_interval '1d'
|
||||
option download_lists_via_proxy '0'
|
||||
#option download_lists_via_proxy_section 'main'
|
||||
|
||||
@@ -1095,7 +1095,7 @@ build_sing_box_signature() {
|
||||
config_get output_network_interface "settings" "output_network_interface"
|
||||
config_get_bool disable_quic "settings" "disable_quic" 0
|
||||
config_get routing_excluded_ips "settings" "routing_excluded_ips"
|
||||
config_get update_interval "settings" "update_interval"
|
||||
update_interval="$(get_settings_update_interval)"
|
||||
config_get cache_path "settings" "cache_path" "/tmp/sing-box/cache.db"
|
||||
config_get config_path "settings" "config_path"
|
||||
config_get log_level "settings" "log_level" "warn"
|
||||
@@ -1288,7 +1288,7 @@ build_cron_signature() {
|
||||
local update_interval
|
||||
|
||||
signature_begin
|
||||
config_get update_interval "settings" "update_interval"
|
||||
update_interval="$(get_settings_update_interval)"
|
||||
signature_add "settings.update_interval" "$update_interval"
|
||||
config_foreach append_list_update_signature "section"
|
||||
config_foreach append_subscription_update_cron_signature "section"
|
||||
@@ -1389,10 +1389,20 @@ has_remote_sing_box_ruleset_sources() {
|
||||
}
|
||||
|
||||
get_settings_update_interval() {
|
||||
local update_interval
|
||||
local enabled update_interval
|
||||
|
||||
config_get update_interval "settings" "update_interval"
|
||||
printf '%s\n' "$update_interval"
|
||||
config_get_bool enabled "settings" "list_update_enabled" 1
|
||||
if [ "$enabled" -eq 0 ]; then
|
||||
printf '\n'
|
||||
return 0
|
||||
fi
|
||||
|
||||
config_get update_interval "settings" "update_interval" "1d"
|
||||
if [ -n "$update_interval" ]; then
|
||||
printf '%s\n' "$update_interval"
|
||||
else
|
||||
printf '1d\n'
|
||||
fi
|
||||
}
|
||||
|
||||
get_duration_option_with_default_if_unset() {
|
||||
@@ -1700,7 +1710,19 @@ validate_download_lists_via_proxy_section() {
|
||||
fi
|
||||
}
|
||||
|
||||
validate_list_update_settings() {
|
||||
local enabled update_interval
|
||||
|
||||
config_get_bool enabled "settings" "list_update_enabled" 1
|
||||
[ "$enabled" -eq 1 ] || return 0
|
||||
|
||||
config_get update_interval "settings" "update_interval" "1d"
|
||||
[ -n "$update_interval" ] || update_interval="1d"
|
||||
validate_required_sing_box_duration_option "$update_interval" "settings.update_interval"
|
||||
}
|
||||
|
||||
validate_runtime_settings() {
|
||||
validate_list_update_settings
|
||||
validate_download_lists_via_proxy_section
|
||||
}
|
||||
|
||||
@@ -2078,6 +2100,27 @@ migrate_0_7_17_8_rule_section() {
|
||||
migrate_0_7_17_8_rule "$section" 1
|
||||
}
|
||||
|
||||
migrate_list_update_enabled() {
|
||||
local list_update_enabled update_interval
|
||||
|
||||
if podkop_uci_option_exists "settings" "list_update_enabled"; then
|
||||
config_get_bool list_update_enabled "settings" "list_update_enabled" 1
|
||||
if [ "$list_update_enabled" -eq 1 ]; then
|
||||
config_get update_interval "settings" "update_interval"
|
||||
[ -n "$update_interval" ] || podkop_uci_set_option "settings" "update_interval" "1d"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
config_get update_interval "settings" "update_interval"
|
||||
if [ -n "$update_interval" ]; then
|
||||
podkop_uci_set_option "settings" "list_update_enabled" "1"
|
||||
else
|
||||
podkop_uci_set_option "settings" "list_update_enabled" "0"
|
||||
podkop_uci_set_option "settings" "update_interval" "1d"
|
||||
fi
|
||||
}
|
||||
|
||||
check_requirements() {
|
||||
log "Check Requirements"
|
||||
|
||||
@@ -2596,12 +2639,13 @@ migration() {
|
||||
podkop_config_migration_added_lists=""
|
||||
|
||||
ensure_runtime_cache_format
|
||||
migrate_list_update_enabled
|
||||
|
||||
config_foreach migrate_0_7_17_8_rule_section "rule"
|
||||
config_foreach migrate_0_7_17_8_rule "section"
|
||||
|
||||
if [ "$podkop_config_migration_changed" -eq 1 ]; then
|
||||
log "Migrated Podkop Plus UCI config from 0.7.17-8 schema"
|
||||
log "Migrated Podkop Plus UCI config"
|
||||
commit_podkop_config
|
||||
fi
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user