fix: parental control global network block, luci save&apply reload, and fuzzer localization
This commit is contained in:
+1428
-1265
File diff suppressed because it is too large
Load Diff
@@ -9240,3 +9240,75 @@ msgstr ""
|
|||||||
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
|
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
|
||||||
msgid "Zapret2 sing-box route rules are not configured"
|
msgid "Zapret2 sing-box route rules are not configured"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "+ add"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "⚠️ Server received fakes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "✓ 32KB Streamed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "✗ Throttled at 16KB"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Analysis inconclusive."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Blocked Sites (Only block domains specified below)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DNS Blocking"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "endpoints OK"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "IP Block (TCP SYN Timeout)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No Blocking Detected"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Target accessible — no DPI blocking detected."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP Reset Injection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP RST received from DPI — active TCP reset injection detected."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Testing:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Throttling / Deep Inspection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unknown DPI Pattern"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Zapret v1"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Zapret v2"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Allow only: "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Block: "
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -855,7 +855,7 @@ msgid "Cache file path cannot be empty"
|
|||||||
msgstr "Путь к файлу кэша не может быть пустым"
|
msgstr "Путь к файлу кэша не может быть пустым"
|
||||||
|
|
||||||
msgid "Camou"
|
msgid "Camou"
|
||||||
msgstr ""
|
msgstr "Camou"
|
||||||
|
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Отмена"
|
msgstr "Отмена"
|
||||||
@@ -870,7 +870,7 @@ msgid "Cannot save settings"
|
|||||||
msgstr "Не удалось сохранить настройки"
|
msgstr "Не удалось сохранить настройки"
|
||||||
|
|
||||||
msgid "Captive"
|
msgid "Captive"
|
||||||
msgstr ""
|
msgstr "Captive"
|
||||||
|
|
||||||
msgid "Cascade connection"
|
msgid "Cascade connection"
|
||||||
msgstr "Каскадное подключение"
|
msgstr "Каскадное подключение"
|
||||||
@@ -993,7 +993,7 @@ msgid "Click to show"
|
|||||||
msgstr "Нажмите, чтобы показать"
|
msgstr "Нажмите, чтобы показать"
|
||||||
|
|
||||||
msgid "Client"
|
msgid "Client"
|
||||||
msgstr ""
|
msgstr "Клиент"
|
||||||
|
|
||||||
msgid "Client Address"
|
msgid "Client Address"
|
||||||
msgstr "Адрес клиента"
|
msgstr "Адрес клиента"
|
||||||
@@ -1263,7 +1263,7 @@ msgid "Dedicated cgroup unavailable"
|
|||||||
msgstr "Выделенная cgroup недоступна"
|
msgstr "Выделенная cgroup недоступна"
|
||||||
|
|
||||||
msgid "Default"
|
msgid "Default"
|
||||||
msgstr ""
|
msgstr "По умолчанию"
|
||||||
|
|
||||||
msgid "Default (auto)"
|
msgid "Default (auto)"
|
||||||
msgstr "По умолчанию (авто)"
|
msgstr "По умолчанию (авто)"
|
||||||
@@ -2130,7 +2130,7 @@ msgid "France (fr)"
|
|||||||
msgstr "Франция (fr)"
|
msgstr "Франция (fr)"
|
||||||
|
|
||||||
msgid "Freedom"
|
msgid "Freedom"
|
||||||
msgstr ""
|
msgstr "Freedom"
|
||||||
|
|
||||||
msgid "Fri"
|
msgid "Fri"
|
||||||
msgstr "Пт"
|
msgstr "Пт"
|
||||||
@@ -2787,7 +2787,7 @@ msgid "Japan (jp)"
|
|||||||
msgstr "Япония (jp)"
|
msgstr "Япония (jp)"
|
||||||
|
|
||||||
msgid "Jitsi"
|
msgid "Jitsi"
|
||||||
msgstr ""
|
msgstr "Jitsi"
|
||||||
|
|
||||||
msgid "JSON outbound"
|
msgid "JSON outbound"
|
||||||
msgstr "JSON outbound"
|
msgstr "JSON outbound"
|
||||||
@@ -5649,10 +5649,10 @@ msgid "When switching to another level, test every server and select the fastest
|
|||||||
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
|
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
|
||||||
|
|
||||||
msgid "WHEP"
|
msgid "WHEP"
|
||||||
msgstr ""
|
msgstr "WHEP"
|
||||||
|
|
||||||
msgid "WHIP"
|
msgid "WHIP"
|
||||||
msgstr ""
|
msgstr "WHIP"
|
||||||
|
|
||||||
msgid "Why Discord fails?"
|
msgid "Why Discord fails?"
|
||||||
msgstr "Почему не работает Discord?"
|
msgstr "Почему не работает Discord?"
|
||||||
@@ -5670,7 +5670,7 @@ msgid "working strategies"
|
|||||||
msgstr "рабочих стратегий"
|
msgstr "рабочих стратегий"
|
||||||
|
|
||||||
msgid "WRT"
|
msgid "WRT"
|
||||||
msgstr ""
|
msgstr "WRT"
|
||||||
|
|
||||||
msgid "XHTTP mode"
|
msgid "XHTTP mode"
|
||||||
msgstr "Режим XHTTP"
|
msgstr "Режим XHTTP"
|
||||||
@@ -6505,3 +6505,75 @@ msgstr "Скачивание и обработка списков"
|
|||||||
|
|
||||||
msgid "Applying rules and subnet lists"
|
msgid "Applying rules and subnet lists"
|
||||||
msgstr "Применение правил и списков подсетей"
|
msgstr "Применение правил и списков подсетей"
|
||||||
|
|
||||||
|
msgid "+ add"
|
||||||
|
msgstr "+ добавить"
|
||||||
|
|
||||||
|
msgid "⚠️ Server received fakes"
|
||||||
|
msgstr "⚠️ Сервер получил фейки"
|
||||||
|
|
||||||
|
msgid "✓ 32KB Streamed"
|
||||||
|
msgstr "✓ 32KB Прокачано"
|
||||||
|
|
||||||
|
msgid "✗ Throttled at 16KB"
|
||||||
|
msgstr "✗ Задушено на 16KB"
|
||||||
|
|
||||||
|
msgid "Analysis inconclusive."
|
||||||
|
msgstr "Анализ не дал однозначного результата."
|
||||||
|
|
||||||
|
msgid "Blocked Sites (Only block domains specified below)"
|
||||||
|
msgstr "Заблокированные сайты (Блокировать только указанные домены)"
|
||||||
|
|
||||||
|
msgid "DNS Blocking"
|
||||||
|
msgstr "Блокировка DNS"
|
||||||
|
|
||||||
|
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
|
||||||
|
msgstr "Сбой разрешения DNS — вероятно, блокировка или перехват на уровне DNS."
|
||||||
|
|
||||||
|
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
|
||||||
|
msgstr "Обнаружено замедление DPI — соединение заблокировано или задушено при передаче данных."
|
||||||
|
|
||||||
|
msgid "endpoints OK"
|
||||||
|
msgstr "эндпоинтов доступно"
|
||||||
|
|
||||||
|
msgid "IP Block (TCP SYN Timeout)"
|
||||||
|
msgstr "Блокировка по IP (Таймаут TCP SYN)"
|
||||||
|
|
||||||
|
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
|
||||||
|
msgstr "Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!"
|
||||||
|
|
||||||
|
msgid "No Blocking Detected"
|
||||||
|
msgstr "Блокировка не обнаружена"
|
||||||
|
|
||||||
|
msgid "Target accessible — no DPI blocking detected."
|
||||||
|
msgstr "Цель доступна — блокировок DPI не обнаружено."
|
||||||
|
|
||||||
|
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
|
||||||
|
msgstr "Таймаут TCP-подключения до рукопожатия TLS — узел заблокирован на уровне IP. Методы обхода DPI не помогут; настройте маршрутизацию через прокси/VPN Sing-box."
|
||||||
|
|
||||||
|
msgid "TCP Reset Injection"
|
||||||
|
msgstr "Внедрение пакетов TCP Reset (RST)"
|
||||||
|
|
||||||
|
msgid "TCP RST received from DPI — active TCP reset injection detected."
|
||||||
|
msgstr "Получен TCP RST от ТСПУ/DPI — зафиксирована активная инъекция сброса соединения."
|
||||||
|
|
||||||
|
msgid "Testing:"
|
||||||
|
msgstr "Тестирование:"
|
||||||
|
|
||||||
|
msgid "Throttling / Deep Inspection"
|
||||||
|
msgstr "Замедление / Глубокий анализ пакетов"
|
||||||
|
|
||||||
|
msgid "Unknown DPI Pattern"
|
||||||
|
msgstr "Неизвестный тип DPI"
|
||||||
|
|
||||||
|
msgid "Zapret v1"
|
||||||
|
msgstr "Zapret v1"
|
||||||
|
|
||||||
|
msgid "Zapret v2"
|
||||||
|
msgstr "Zapret v2"
|
||||||
|
|
||||||
|
msgid "Allow only: "
|
||||||
|
msgstr "Разрешить только: "
|
||||||
|
|
||||||
|
msgid "Block: "
|
||||||
|
msgstr "Блокировать: "
|
||||||
|
|||||||
@@ -623,7 +623,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
const input = E('input', {
|
const input = E('input', {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
class: 'cbi-input-text',
|
class: 'cbi-input-text',
|
||||||
placeholder: '+ add',
|
placeholder: _('+ add'),
|
||||||
style: 'width: 80px; padding: 2px 6px; font-size: 11px;',
|
style: 'width: 80px; padding: 2px 6px; font-size: 11px;',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -888,9 +888,9 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
style: 'flex: 0 1 140px; min-width: 120px;',
|
style: 'flex: 0 1 140px; min-width: 120px;',
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
E('option', { value: 'zapret2' }, 'Zapret v2'),
|
E('option', { value: 'zapret2' }, _('Zapret v2')),
|
||||||
E('option', { value: 'zapret' }, 'Zapret v1'),
|
E('option', { value: 'zapret' }, _('Zapret v1')),
|
||||||
E('option', { value: 'byedpi' }, 'ByeDPI'),
|
E('option', { value: 'byedpi' }, _('ByeDPI')),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
const argsInput = E('input', {
|
const argsInput = E('input', {
|
||||||
@@ -1417,7 +1417,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
'Data transfer verified: streamed >= 32KB without throttling or disconnect',
|
'Data transfer verified: streamed >= 32KB without throttling or disconnect',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'✓ 32KB Прокачано',
|
_('✓ 32KB Streamed'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (item.dpi_verdict === 'throttled_16k') {
|
} else if (item.dpi_verdict === 'throttled_16k') {
|
||||||
@@ -1432,7 +1432,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
'DPI throttling: stream cut off after ~16KB payload transfer',
|
'DPI throttling: stream cut off after ~16KB payload transfer',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'✗ Задушено на 16KB',
|
_('✗ Throttled at 16KB'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (item.dpi_verdict === 'server_fakes') {
|
} else if (item.dpi_verdict === 'server_fakes') {
|
||||||
@@ -1447,7 +1447,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
'Fake packets reached remote server (HTTP 400 Bad Request)',
|
'Fake packets reached remote server (HTTP 400 Bad Request)',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'⚠️ Сервер получил фейки',
|
_('⚠️ Server received fakes'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1459,7 +1459,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
E(
|
E(
|
||||||
'div',
|
'div',
|
||||||
{ style: 'font-size: 10px; opacity: 0.75; margin-top: 2px;' },
|
{ style: 'font-size: 10px; opacity: 0.75; margin-top: 2px;' },
|
||||||
`${passedSub}/${totalSub} endpoints OK`,
|
`${passedSub}/${totalSub} ` + _('endpoints OK'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1624,7 +1624,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
|
|
||||||
if (currentStratEl) {
|
if (currentStratEl) {
|
||||||
if (state.running && state.current_strategy) {
|
if (state.running && state.current_strategy) {
|
||||||
currentStratEl.innerText = `Testing: [${state.current_strategy.name}] -> ${state.current_strategy.args}`;
|
currentStratEl.innerText = `${_('Testing:')} [${state.current_strategy.name}] -> ${state.current_strategy.args}`;
|
||||||
} else {
|
} else {
|
||||||
currentStratEl.innerText = '';
|
currentStratEl.innerText = '';
|
||||||
}
|
}
|
||||||
@@ -1740,12 +1740,40 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const typeLabels: Record<string, string> = {
|
const typeLabels: Record<string, string> = {
|
||||||
rst: 'TCP Reset Injection',
|
rst: _('TCP Reset Injection'),
|
||||||
throttle: 'Throttling / Deep Inspection',
|
throttle: _('Throttling / Deep Inspection'),
|
||||||
dns_block: 'DNS Blocking',
|
dns_block: _('DNS Blocking'),
|
||||||
ip_block: _('Блокировка по IP (Таймаут TCP SYN)'),
|
ip_block: _('IP Block (TCP SYN Timeout)'),
|
||||||
unknown: 'Unknown DPI Pattern',
|
unknown: _('Unknown DPI Pattern'),
|
||||||
none: 'No Blocking Detected',
|
none: _('No Blocking Detected'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLocalizedDpiDetails = (
|
||||||
|
det: Tachyon.FuzzerDpiDetection | null | undefined,
|
||||||
|
) => {
|
||||||
|
if (!det) return '';
|
||||||
|
switch (det.type) {
|
||||||
|
case 'ip_block':
|
||||||
|
return _(
|
||||||
|
'TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead.',
|
||||||
|
);
|
||||||
|
case 'rst':
|
||||||
|
return _(
|
||||||
|
'TCP RST received from DPI — active TCP reset injection detected.',
|
||||||
|
);
|
||||||
|
case 'dns_block':
|
||||||
|
return _(
|
||||||
|
'DNS resolution failed — likely DNS-level blocking or hijacking.',
|
||||||
|
);
|
||||||
|
case 'throttle':
|
||||||
|
return _(
|
||||||
|
'DPI bandwidth throttling or delay detected — connection throttled during data transfer.',
|
||||||
|
);
|
||||||
|
case 'none':
|
||||||
|
return _('Target accessible — no DPI blocking detected.');
|
||||||
|
default:
|
||||||
|
return det.details || _('Analysis inconclusive.');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const meta = typeMeta[detection.type] || typeMeta.unknown;
|
const meta = typeMeta[detection.type] || typeMeta.unknown;
|
||||||
@@ -1754,7 +1782,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
const ipBlockNotice =
|
const ipBlockNotice =
|
||||||
detection.type === 'ip_block'
|
detection.type === 'ip_block'
|
||||||
? `<div class="alert-message danger" style="margin-top: 8px;">
|
? `<div class="alert-message danger" style="margin-top: 8px;">
|
||||||
⚠️ ${_('Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!')}
|
⚠️ ${_('IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!')}
|
||||||
</div>`
|
</div>`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
@@ -1768,7 +1796,7 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
|||||||
</div>
|
</div>
|
||||||
${detection.recommended_engines.length > 0 ? `<div style="font-size: 11px; opacity: 0.8;">${_('Recommended')}: ${detection.recommended_engines.join(', ')}</div>` : ''}
|
${detection.recommended_engines.length > 0 ? `<div style="font-size: 11px; opacity: 0.8;">${_('Recommended')}: ${detection.recommended_engines.join(', ')}</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 4px; opacity: 0.9; font-size: 11px;">${detection.details}</div>
|
<div style="margin-top: 4px; opacity: 0.9; font-size: 11px;">${getLocalizedDpiDetails(detection)}</div>
|
||||||
${ipBlockNotice}
|
${ipBlockNotice}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
esbuildOptions(options) {
|
esbuildOptions(options) {
|
||||||
options.legalComments = 'none';
|
options.legalComments = 'none';
|
||||||
|
options.charset = 'utf8';
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
const outDir =
|
const outDir =
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1081,7 +1081,8 @@ function createParentalContent(section) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
o.modalonly = true;
|
o.modalonly = true;
|
||||||
o.default = "all";
|
o.default = "domains";
|
||||||
|
o.value("domains", _("Blocked Sites (Only block domains specified below)"));
|
||||||
o.value("all", _("All Internet (Complete internet cutoff)"));
|
o.value("all", _("All Internet (Complete internet cutoff)"));
|
||||||
o.value("sections", _("Specific Sections (Select sections below)"));
|
o.value("sections", _("Specific Sections (Select sections below)"));
|
||||||
|
|
||||||
|
|||||||
@@ -433,58 +433,7 @@ const EntryPoint = {
|
|||||||
return form.Map.prototype.load.call(self);
|
return form.Map.prototype.load.call(self);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
tachyonMap.handleSaveApply = function (ev, mode) {
|
tachyonMap.handleSaveApply = EntryPoint.handleSaveApply;
|
||||||
const refreshUiState = function () {
|
|
||||||
main.TachyonShellMethods.getUiState()
|
|
||||||
.then((response) => {
|
|
||||||
if (
|
|
||||||
response?.success &&
|
|
||||||
typeof main.applyUiStateToStore === "function"
|
|
||||||
) {
|
|
||||||
main.applyUiStateToStore(response.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (main.store && typeof main.store.set === "function") {
|
|
||||||
const servicesInfoWidget = main.store.get().servicesInfoWidget;
|
|
||||||
main.store.set({
|
|
||||||
servicesInfoWidget: {
|
|
||||||
...servicesInfoWidget,
|
|
||||||
data: {
|
|
||||||
...servicesInfoWidget.data,
|
|
||||||
tachyonStatus: "reloading",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.handleSave(ev)
|
|
||||||
.then(() => ui.changes.apply(mode == "0"))
|
|
||||||
.then((result) => {
|
|
||||||
if (
|
|
||||||
main.TachyonShellMethods &&
|
|
||||||
typeof main.TachyonShellMethods.serviceActionStart === "function"
|
|
||||||
) {
|
|
||||||
return main.TachyonShellMethods.serviceActionStart("reload")
|
|
||||||
.then(() => {
|
|
||||||
window.setTimeout(refreshUiState, 500);
|
|
||||||
return result;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
window.setTimeout(refreshUiState, 500);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
window.setTimeout(refreshUiState, 500);
|
|
||||||
return result;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
refreshUiState();
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const rawTabOrder = uci.get(UCI_PACKAGE, "settings", "tab_order");
|
const rawTabOrder = uci.get(UCI_PACKAGE, "settings", "tab_order");
|
||||||
const savedOrder = Array.isArray(rawTabOrder)
|
const savedOrder = Array.isArray(rawTabOrder)
|
||||||
@@ -758,6 +707,66 @@ const EntryPoint = {
|
|||||||
|
|
||||||
return rendered;
|
return rendered;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleSaveApply: function (ev, mode) {
|
||||||
|
const refreshUiState = function () {
|
||||||
|
if (
|
||||||
|
main.TachyonShellMethods &&
|
||||||
|
typeof main.TachyonShellMethods.getUiState === "function"
|
||||||
|
) {
|
||||||
|
main.TachyonShellMethods.getUiState()
|
||||||
|
.then((response) => {
|
||||||
|
if (
|
||||||
|
response?.success &&
|
||||||
|
typeof main.applyUiStateToStore === "function"
|
||||||
|
) {
|
||||||
|
main.applyUiStateToStore(response.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (main.store && typeof main.store.set === "function") {
|
||||||
|
const servicesInfoWidget = main.store.get()?.servicesInfoWidget;
|
||||||
|
if (servicesInfoWidget) {
|
||||||
|
main.store.set({
|
||||||
|
servicesInfoWidget: {
|
||||||
|
...servicesInfoWidget,
|
||||||
|
data: {
|
||||||
|
...servicesInfoWidget.data,
|
||||||
|
tachyonStatus: "reloading",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.handleSave(ev)
|
||||||
|
.then(() => ui.changes.apply(mode == "0"))
|
||||||
|
.then((result) => {
|
||||||
|
if (
|
||||||
|
main.TachyonShellMethods &&
|
||||||
|
typeof main.TachyonShellMethods.serviceActionStart === "function"
|
||||||
|
) {
|
||||||
|
return main.TachyonShellMethods.serviceActionStart("reload")
|
||||||
|
.then(() => {
|
||||||
|
window.setTimeout(refreshUiState, 500);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
window.setTimeout(refreshUiState, 500);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.setTimeout(refreshUiState, 500);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
refreshUiState();
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return view.extend(EntryPoint);
|
return view.extend(EntryPoint);
|
||||||
|
|||||||
@@ -855,7 +855,7 @@ msgid "Cache file path cannot be empty"
|
|||||||
msgstr "Путь к файлу кэша не может быть пустым"
|
msgstr "Путь к файлу кэша не может быть пустым"
|
||||||
|
|
||||||
msgid "Camou"
|
msgid "Camou"
|
||||||
msgstr ""
|
msgstr "Camou"
|
||||||
|
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Отмена"
|
msgstr "Отмена"
|
||||||
@@ -870,7 +870,7 @@ msgid "Cannot save settings"
|
|||||||
msgstr "Не удалось сохранить настройки"
|
msgstr "Не удалось сохранить настройки"
|
||||||
|
|
||||||
msgid "Captive"
|
msgid "Captive"
|
||||||
msgstr ""
|
msgstr "Captive"
|
||||||
|
|
||||||
msgid "Cascade connection"
|
msgid "Cascade connection"
|
||||||
msgstr "Каскадное подключение"
|
msgstr "Каскадное подключение"
|
||||||
@@ -993,7 +993,7 @@ msgid "Click to show"
|
|||||||
msgstr "Нажмите, чтобы показать"
|
msgstr "Нажмите, чтобы показать"
|
||||||
|
|
||||||
msgid "Client"
|
msgid "Client"
|
||||||
msgstr ""
|
msgstr "Клиент"
|
||||||
|
|
||||||
msgid "Client Address"
|
msgid "Client Address"
|
||||||
msgstr "Адрес клиента"
|
msgstr "Адрес клиента"
|
||||||
@@ -1263,7 +1263,7 @@ msgid "Dedicated cgroup unavailable"
|
|||||||
msgstr "Выделенная cgroup недоступна"
|
msgstr "Выделенная cgroup недоступна"
|
||||||
|
|
||||||
msgid "Default"
|
msgid "Default"
|
||||||
msgstr ""
|
msgstr "По умолчанию"
|
||||||
|
|
||||||
msgid "Default (auto)"
|
msgid "Default (auto)"
|
||||||
msgstr "По умолчанию (авто)"
|
msgstr "По умолчанию (авто)"
|
||||||
@@ -2130,7 +2130,7 @@ msgid "France (fr)"
|
|||||||
msgstr "Франция (fr)"
|
msgstr "Франция (fr)"
|
||||||
|
|
||||||
msgid "Freedom"
|
msgid "Freedom"
|
||||||
msgstr ""
|
msgstr "Freedom"
|
||||||
|
|
||||||
msgid "Fri"
|
msgid "Fri"
|
||||||
msgstr "Пт"
|
msgstr "Пт"
|
||||||
@@ -2787,7 +2787,7 @@ msgid "Japan (jp)"
|
|||||||
msgstr "Япония (jp)"
|
msgstr "Япония (jp)"
|
||||||
|
|
||||||
msgid "Jitsi"
|
msgid "Jitsi"
|
||||||
msgstr ""
|
msgstr "Jitsi"
|
||||||
|
|
||||||
msgid "JSON outbound"
|
msgid "JSON outbound"
|
||||||
msgstr "JSON outbound"
|
msgstr "JSON outbound"
|
||||||
@@ -5649,10 +5649,10 @@ msgid "When switching to another level, test every server and select the fastest
|
|||||||
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
|
msgstr "При переключении на другой уровень тестировать каждый сервер и выбирать самый быстрый вместо первого рабочего."
|
||||||
|
|
||||||
msgid "WHEP"
|
msgid "WHEP"
|
||||||
msgstr ""
|
msgstr "WHEP"
|
||||||
|
|
||||||
msgid "WHIP"
|
msgid "WHIP"
|
||||||
msgstr ""
|
msgstr "WHIP"
|
||||||
|
|
||||||
msgid "Why Discord fails?"
|
msgid "Why Discord fails?"
|
||||||
msgstr "Почему не работает Discord?"
|
msgstr "Почему не работает Discord?"
|
||||||
@@ -5670,7 +5670,7 @@ msgid "working strategies"
|
|||||||
msgstr "рабочих стратегий"
|
msgstr "рабочих стратегий"
|
||||||
|
|
||||||
msgid "WRT"
|
msgid "WRT"
|
||||||
msgstr ""
|
msgstr "WRT"
|
||||||
|
|
||||||
msgid "XHTTP mode"
|
msgid "XHTTP mode"
|
||||||
msgstr "Режим XHTTP"
|
msgstr "Режим XHTTP"
|
||||||
@@ -6505,3 +6505,75 @@ msgstr "Скачивание и обработка списков"
|
|||||||
|
|
||||||
msgid "Applying rules and subnet lists"
|
msgid "Applying rules and subnet lists"
|
||||||
msgstr "Применение правил и списков подсетей"
|
msgstr "Применение правил и списков подсетей"
|
||||||
|
|
||||||
|
msgid "+ add"
|
||||||
|
msgstr "+ добавить"
|
||||||
|
|
||||||
|
msgid "⚠️ Server received fakes"
|
||||||
|
msgstr "⚠️ Сервер получил фейки"
|
||||||
|
|
||||||
|
msgid "✓ 32KB Streamed"
|
||||||
|
msgstr "✓ 32KB Прокачано"
|
||||||
|
|
||||||
|
msgid "✗ Throttled at 16KB"
|
||||||
|
msgstr "✗ Задушено на 16KB"
|
||||||
|
|
||||||
|
msgid "Analysis inconclusive."
|
||||||
|
msgstr "Анализ не дал однозначного результата."
|
||||||
|
|
||||||
|
msgid "Blocked Sites (Only block domains specified below)"
|
||||||
|
msgstr "Заблокированные сайты (Блокировать только указанные домены)"
|
||||||
|
|
||||||
|
msgid "DNS Blocking"
|
||||||
|
msgstr "Блокировка DNS"
|
||||||
|
|
||||||
|
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
|
||||||
|
msgstr "Сбой разрешения DNS — вероятно, блокировка или перехват на уровне DNS."
|
||||||
|
|
||||||
|
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
|
||||||
|
msgstr "Обнаружено замедление DPI — соединение заблокировано или задушено при передаче данных."
|
||||||
|
|
||||||
|
msgid "endpoints OK"
|
||||||
|
msgstr "эндпоинтов доступно"
|
||||||
|
|
||||||
|
msgid "IP Block (TCP SYN Timeout)"
|
||||||
|
msgstr "Блокировка по IP (Таймаут TCP SYN)"
|
||||||
|
|
||||||
|
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
|
||||||
|
msgstr "Обнаружена блокировка по IP! Ресурс блокируется на сетевом уровне (нет ответа на TCP SYN). Методы обхода DPI (Zapret / ByeDPI) бессильны для этого адреса — настройте маршрутизацию через прокси/VPN (Sing-box) для данного домена!"
|
||||||
|
|
||||||
|
msgid "No Blocking Detected"
|
||||||
|
msgstr "Блокировка не обнаружена"
|
||||||
|
|
||||||
|
msgid "Target accessible — no DPI blocking detected."
|
||||||
|
msgstr "Цель доступна — блокировок DPI не обнаружено."
|
||||||
|
|
||||||
|
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
|
||||||
|
msgstr "Таймаут TCP-подключения до рукопожатия TLS — узел заблокирован на уровне IP. Методы обхода DPI не помогут; настройте маршрутизацию через прокси/VPN Sing-box."
|
||||||
|
|
||||||
|
msgid "TCP Reset Injection"
|
||||||
|
msgstr "Внедрение пакетов TCP Reset (RST)"
|
||||||
|
|
||||||
|
msgid "TCP RST received from DPI — active TCP reset injection detected."
|
||||||
|
msgstr "Получен TCP RST от ТСПУ/DPI — зафиксирована активная инъекция сброса соединения."
|
||||||
|
|
||||||
|
msgid "Testing:"
|
||||||
|
msgstr "Тестирование:"
|
||||||
|
|
||||||
|
msgid "Throttling / Deep Inspection"
|
||||||
|
msgstr "Замедление / Глубокий анализ пакетов"
|
||||||
|
|
||||||
|
msgid "Unknown DPI Pattern"
|
||||||
|
msgstr "Неизвестный тип DPI"
|
||||||
|
|
||||||
|
msgid "Zapret v1"
|
||||||
|
msgstr "Zapret v1"
|
||||||
|
|
||||||
|
msgid "Zapret v2"
|
||||||
|
msgstr "Zapret v2"
|
||||||
|
|
||||||
|
msgid "Allow only: "
|
||||||
|
msgstr "Разрешить только: "
|
||||||
|
|
||||||
|
msgid "Block: "
|
||||||
|
msgstr "Блокировать: "
|
||||||
|
|||||||
@@ -9240,3 +9240,75 @@ msgstr ""
|
|||||||
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
|
#: src/tachyon/tabs/diagnostic/checks/runZapret2Check.ts:115
|
||||||
msgid "Zapret2 sing-box route rules are not configured"
|
msgid "Zapret2 sing-box route rules are not configured"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "+ add"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "⚠️ Server received fakes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "✓ 32KB Streamed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "✗ Throttled at 16KB"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Analysis inconclusive."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Blocked Sites (Only block domains specified below)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DNS Blocking"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DNS resolution failed — likely DNS-level blocking or hijacking."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "endpoints OK"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "IP Block (TCP SYN Timeout)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "IP-level blocking detected! The resource is blocked at the network level (no TCP SYN response). DPI bypass methods (Zapret / ByeDPI) cannot bypass this address — configure proxy/VPN routing (Sing-box) for this domain!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No Blocking Detected"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Target accessible — no DPI blocking detected."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP connect timed out before TLS handshake — host is blocked at the IP layer. DPI bypass cannot unblock this; route via Sing-box VPN/Proxy outbound instead."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP Reset Injection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TCP RST received from DPI — active TCP reset injection detected."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Testing:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Throttling / Deep Inspection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unknown DPI Pattern"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Zapret v1"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Zapret v2"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Allow only: "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Block: "
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ start_service() {
|
|||||||
initd_ucode start-service "$1" "$$"
|
initd_ucode start-service "$1" "$$"
|
||||||
TACHYON_LAST_START_STATUS="$?"
|
TACHYON_LAST_START_STATUS="$?"
|
||||||
trap - INT TERM
|
trap - INT TERM
|
||||||
|
[ "$TACHYON_LAST_START_STATUS" = "0" ] && sync_service_triggers
|
||||||
return "$TACHYON_LAST_START_STATUS"
|
return "$TACHYON_LAST_START_STATUS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1875,7 +1875,7 @@ function validate_schedule(schedule, sections, profiles) {
|
|||||||
if (length(sec_names) == 0) {
|
if (length(sec_names) == 0) {
|
||||||
let single_sec = option(schedule, "sections", "");
|
let single_sec = option(schedule, "sections", "");
|
||||||
if (single_sec != "") sec_names = [ single_sec ];
|
if (single_sec != "") sec_names = [ single_sec ];
|
||||||
else if (target != "all" && target != "sections" && target != "") sec_names = [ target ];
|
else if (target != "all" && target != "sections" && target != "domains" && target != "") sec_names = [ target ];
|
||||||
}
|
}
|
||||||
for (let sec_name in sec_names) {
|
for (let sec_name in sec_names) {
|
||||||
let found = false;
|
let found = false;
|
||||||
|
|||||||
@@ -1055,7 +1055,8 @@ function nft_add_schedule_rules_from_schedules(schedules, sections, table, profi
|
|||||||
[ "meta", "hour", sprintf("\"%s\"-\"%s\"", interval[0], interval[1]) ] : null;
|
[ "meta", "hour", sprintf("\"%s\"-\"%s\"", interval[0], interval[1]) ] : null;
|
||||||
let base_match = nft_schedule_rule_base_match(dev_str, is_mac, family, days_args, time_args);
|
let base_match = nft_schedule_rule_base_match(dev_str, is_mac, family, days_args, time_args);
|
||||||
|
|
||||||
if (target == "all" || (length(target_sec_names) == 0 && target != "sections")) {
|
let has_sched_domains = length(list_option(schedule, "blocked_domains")) > 0 || option(schedule, "blocked_domains", "") != "";
|
||||||
|
if ((target == "all" || (length(target_sec_names) == 0 && target != "sections" && target != "domains")) && !has_sched_domains && target != "domains") {
|
||||||
let fwd_rule = [];
|
let fwd_rule = [];
|
||||||
append_array(fwd_rule, base_match);
|
append_array(fwd_rule, base_match);
|
||||||
append_array(fwd_rule, [ "counter", verdict ]);
|
append_array(fwd_rule, [ "counter", verdict ]);
|
||||||
@@ -1067,7 +1068,7 @@ function nft_add_schedule_rules_from_schedules(schedules, sections, table, profi
|
|||||||
append_array(ctrl_rule, [ "counter", verdict ]);
|
append_array(ctrl_rule, [ "counter", verdict ]);
|
||||||
if (!nft_add_rule(table, "parental_control", ctrl_rule))
|
if (!nft_add_rule(table, "parental_control", ctrl_rule))
|
||||||
log_debug("nft_add_schedule_rules: failed to add parental_control rule for " + dev_str);
|
log_debug("nft_add_schedule_rules: failed to add parental_control rule for " + dev_str);
|
||||||
} else {
|
} else if (length(target_sec_names) > 0) {
|
||||||
for (let sec_name in target_sec_names) {
|
for (let sec_name in target_sec_names) {
|
||||||
let target_section = section_by_name(sections, sec_name);
|
let target_section = section_by_name(sections, sec_name);
|
||||||
if (!target_section) continue;
|
if (!target_section) continue;
|
||||||
|
|||||||
@@ -291,7 +291,10 @@ function plan_state_from_file(path) {
|
|||||||
zapret2_queue: as_string(state.zapret2_queue_signature),
|
zapret2_queue: as_string(state.zapret2_queue_signature),
|
||||||
zapret2_runtime: as_string(state.zapret2_runtime_signature),
|
zapret2_runtime: as_string(state.zapret2_runtime_signature),
|
||||||
byedpi_runtime: as_string(state.byedpi_runtime_signature),
|
byedpi_runtime: as_string(state.byedpi_runtime_signature),
|
||||||
|
wdtt_runtime: as_string(state.wdtt_runtime_signature),
|
||||||
|
olcrtc_runtime: as_string(state.olcrtc_runtime_signature),
|
||||||
list: as_string(state.list_signature),
|
list: as_string(state.list_signature),
|
||||||
|
hosts_list: as_string(state.hosts_list_signature),
|
||||||
cron: as_string(state.cron_signature),
|
cron: as_string(state.cron_signature),
|
||||||
urltest_sections_known: state.__has.urltest_enabled_sections === true,
|
urltest_sections_known: state.__has.urltest_enabled_sections === true,
|
||||||
urltest_sections: as_string(state.urltest_enabled_sections),
|
urltest_sections: as_string(state.urltest_enabled_sections),
|
||||||
|
|||||||
@@ -287,6 +287,40 @@ parental_jump_line="$(grep -n "mangle jump parental_control" "$NFT_LOG" | cut -d
|
|||||||
if [ "$local_return_line" -ge "$parental_jump_line" ]; then
|
if [ "$local_return_line" -ge "$parental_jump_line" ]; then
|
||||||
fail "localv4 return (line $local_return_line) must precede jump parental_control (line $parental_jump_line) to protect router panel access"
|
fail "localv4 return (line $local_return_line) must precede jump parental_control (line $parental_jump_line) to protect router panel access"
|
||||||
fi
|
fi
|
||||||
|
# ─── Domain-based schedule must NOT add global counter drop rules ────────────
|
||||||
|
cat >"$WORK_DIR/domain_schedule_fixture.json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schedule": [
|
||||||
|
{
|
||||||
|
".name": "domain_only_sched",
|
||||||
|
"enabled": "1",
|
||||||
|
"device_ip": "192.168.1.180",
|
||||||
|
"target": "all",
|
||||||
|
"blocked_domains": [ "tiktok.com", "roblox.com" ],
|
||||||
|
"action": "block"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
".name": "target_domains_sched",
|
||||||
|
"enabled": "1",
|
||||||
|
"device_ip": "192.168.1.181",
|
||||||
|
"target": "domains",
|
||||||
|
"action": "block"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
rm -f "$NFT_LOG"
|
||||||
|
touch "$NFT_LOG"
|
||||||
|
nft_ucode nft-add-schedule-rules-fixture "$WORK_DIR/domain_schedule_fixture.json" "tachyon"
|
||||||
|
|
||||||
|
if grep -Eq "parental_(forward|control).*192\.168\.1\.180.*counter.*drop" "$NFT_LOG"; then
|
||||||
|
fail "Schedule with blocked_domains must NOT add global counter drop in parental_forward/parental_control"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -Eq "parental_(forward|control).*192\.168\.1\.181.*counter.*drop" "$NFT_LOG"; then
|
||||||
|
fail "Schedule with target=domains must NOT add global counter drop in parental_forward/parental_control"
|
||||||
|
fi
|
||||||
|
|
||||||
printf 'Parental control, profiles and schedule tests passed\n'
|
printf 'Parental control, profiles and schedule tests passed\n'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user