fix(routing): scope excluded_ips to section rules, humanize section names, and add component visibility
This commit is contained in:
+1907
-1813
File diff suppressed because it is too large
Load Diff
+1867
-1812
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-16 15:18+0300\n"
|
||||
"PO-Revision-Date: 2026-09-16 15:18+0300\n"
|
||||
"POT-Creation-Date: 2026-09-16 16:15+0300\n"
|
||||
"PO-Revision-Date: 2026-09-16 16:15+0300\n"
|
||||
"Last-Translator: Dushnilin\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -311,6 +311,9 @@ msgstr "Chat ID администраторов"
|
||||
msgid "Adult content"
|
||||
msgstr "Взрослый контент"
|
||||
|
||||
msgid "Advanced DPI bypass engine with Lua scripts (nfqws2)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Advanced System"
|
||||
msgstr "Расширенные настройки"
|
||||
|
||||
@@ -1079,6 +1082,9 @@ msgstr "Community-списки"
|
||||
msgid "Component action timed out"
|
||||
msgstr "Превышено время ожидания действия компонента"
|
||||
|
||||
msgid "Component Cards Visibility"
|
||||
msgstr ""
|
||||
|
||||
msgid "Component update available"
|
||||
msgstr "Доступно обновление компонента"
|
||||
|
||||
@@ -1184,6 +1190,9 @@ msgstr "Скопировать отчет для поддержки"
|
||||
msgid "Core configuration and preferences"
|
||||
msgstr "Основные настройки и параметры"
|
||||
|
||||
msgid "Core orchestration service and LuCI application"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not compare FakeIP and control public IPs"
|
||||
msgstr "Не удалось сравнить публичные IP FakeIP и контрольной проверки"
|
||||
|
||||
@@ -1253,6 +1262,9 @@ msgstr "Пользовательская стратегия добавлена!"
|
||||
msgid "Customize the order and visibility of tabs in the main navigation menu. Use the arrows to reorder and checkboxes to show or hide tabs."
|
||||
msgstr "Настройте порядок и видимость вкладок в главном меню навигации. Используйте стрелки для перемещения и галочки для показа или скрытия."
|
||||
|
||||
msgid "Customize which component cards are displayed in the Components tab. Checkboxes show or hide components."
|
||||
msgstr ""
|
||||
|
||||
msgid "Daily Quota"
|
||||
msgstr "Дневная квота"
|
||||
|
||||
@@ -1286,6 +1298,9 @@ msgstr "Дни недели"
|
||||
msgid "Dedicated cgroup unavailable"
|
||||
msgstr "Выделенная cgroup недоступна"
|
||||
|
||||
msgid "Dedicated direct HTTP/SOCKS5 outbound proxy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Default"
|
||||
msgstr "По умолчанию"
|
||||
|
||||
@@ -1370,6 +1385,9 @@ msgstr "Прямое (по умолчанию)"
|
||||
msgid "Direct Bypass"
|
||||
msgstr "Прямой WAN"
|
||||
|
||||
msgid "Direct cgroup bypass for TorrServer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Direct connection"
|
||||
msgstr "Прямое подключение"
|
||||
|
||||
@@ -1622,6 +1640,9 @@ msgstr "Загрузка и применение наборов правил и
|
||||
msgid "DPI bandwidth throttling or delay detected — connection throttled during data transfer."
|
||||
msgstr "Обнаружено замедление DPI — соединение заблокировано или задушено при передаче данных."
|
||||
|
||||
msgid "DPI bypass engine (nfqws v1)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DPI Detected"
|
||||
msgstr "DPI обнаружен"
|
||||
|
||||
@@ -2126,6 +2147,9 @@ msgstr "Семейные профили"
|
||||
msgid "Family profiles and device grouping"
|
||||
msgstr "Семейные профили и группировка устройств"
|
||||
|
||||
msgid "Fast packet transport network client"
|
||||
msgstr ""
|
||||
|
||||
msgid "Faster DNS health monitoring than the default cycle. Switches bootstrap DNS on failure."
|
||||
msgstr "Более быстрый мониторинг состояния DNS, чем стандартный цикл. Переключает bootstrap DNS при сбое."
|
||||
|
||||
@@ -3071,6 +3095,9 @@ msgstr "Загрузка..."
|
||||
msgid "Local Address"
|
||||
msgstr "Локальный адрес"
|
||||
|
||||
msgid "Local SOCKS proxy desynchronizer (ciadpi)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Location"
|
||||
msgstr "Местоположение"
|
||||
|
||||
@@ -3959,6 +3986,9 @@ msgstr "Ошибка чтения"
|
||||
msgid "Ready to benchmark DNS providers"
|
||||
msgstr "Готов к тестированию DNS провайдеров"
|
||||
|
||||
msgid "Real-time WebRTC anti-censorship tunnel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reality fake site"
|
||||
msgstr "Фейковый сайт Reality"
|
||||
|
||||
@@ -5489,6 +5519,9 @@ msgstr "Великобритания (gb)"
|
||||
msgid "United States (us)"
|
||||
msgstr "США (us)"
|
||||
|
||||
msgid "Universal proxy platform routing engine"
|
||||
msgstr ""
|
||||
|
||||
msgid "unknown"
|
||||
msgstr "неизвестно"
|
||||
|
||||
@@ -5834,6 +5867,9 @@ msgstr "Будни (Пн-Пт)"
|
||||
msgid "Weekends (Sat-Sun)"
|
||||
msgstr "Выходные (Сб-Вс)"
|
||||
|
||||
msgid "Western Digital Tunnel Transport"
|
||||
msgstr ""
|
||||
|
||||
msgid "What Tachyon should do when this section matches"
|
||||
msgstr "Что должен делать Tachyon при совпадении этой секции"
|
||||
|
||||
@@ -5942,6 +5978,9 @@ msgstr "Правила маршрутизации sing-box для Zapret2 нас
|
||||
msgid "Zapret2 sing-box route rules are not configured"
|
||||
msgstr "Правила маршрутизации sing-box для Zapret2 не настроены"
|
||||
|
||||
msgid "Zero config mesh VPN daemon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Testing strategy"
|
||||
msgstr "Тестирование стратегии"
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ export function resetDirectClashApiState() {
|
||||
}
|
||||
|
||||
function getDisplayName(section: Tachyon.ConfigSection) {
|
||||
return section.label || section['.name'];
|
||||
return section.label || (section as any).name || section['.name'];
|
||||
}
|
||||
|
||||
function getSettingsSection(configSections: Tachyon.ConfigSection[]) {
|
||||
@@ -1806,6 +1806,7 @@ export async function getDashboardSections(
|
||||
code: outbound?.code || sectionName,
|
||||
displayName:
|
||||
section.label ||
|
||||
(section as any).name ||
|
||||
section.interface ||
|
||||
customName ||
|
||||
defaultLabel,
|
||||
|
||||
@@ -1589,11 +1589,17 @@ function handleOpenLeakCheck() {
|
||||
function handleOpenStrategyFuzzer() {
|
||||
getConfigSections()
|
||||
.then((sections) => {
|
||||
const ruleNames = sections
|
||||
const ruleSections = sections
|
||||
.filter((s) => s['.type'] === 'section' || s['.type'] === 'rule')
|
||||
.map((s) => s.name || s['.name'])
|
||||
.filter((n): n is string => Boolean(n));
|
||||
renderStrategyFuzzerModal(ruleNames);
|
||||
.map((s) => ({
|
||||
id: s['.name'],
|
||||
label:
|
||||
(typeof s.label === 'string' && s.label.trim()) ||
|
||||
(typeof s.name === 'string' && s.name.trim()) ||
|
||||
s['.name'],
|
||||
}))
|
||||
.filter((r) => Boolean(r.id));
|
||||
renderStrategyFuzzerModal(ruleSections);
|
||||
})
|
||||
.catch(() => {
|
||||
renderStrategyFuzzerModal([]);
|
||||
|
||||
@@ -3,7 +3,17 @@ import { renderButton } from '../../../../partials';
|
||||
import { showToast } from '../../../../helpers/showToast';
|
||||
import { Tachyon } from '../../../types';
|
||||
|
||||
export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
||||
export interface FuzzerRuleSection {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function renderStrategyFuzzerModal(
|
||||
ruleNames: Array<string | FuzzerRuleSection> = [],
|
||||
) {
|
||||
const normalizedRules: FuzzerRuleSection[] = ruleNames.map((r) =>
|
||||
typeof r === 'string' ? { id: r, label: r } : r,
|
||||
);
|
||||
let pollingInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let isPolling = false;
|
||||
|
||||
@@ -292,7 +302,9 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
||||
{ class: 'cbi-input-select', style: 'width: 100%;' },
|
||||
[
|
||||
E('option', { value: '', selected: true }, _('Provider Global Default')),
|
||||
...ruleNames.map((r) => E('option', { value: r }, `${_('Rule:')} ${r}`)),
|
||||
...normalizedRules.map((r) =>
|
||||
E('option', { value: r.id }, `${_('Rule:')} ${r.label}`),
|
||||
),
|
||||
],
|
||||
);
|
||||
ruleSelect.addEventListener('change', () => {
|
||||
@@ -2035,19 +2047,23 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
|
||||
selectedRuleSection,
|
||||
);
|
||||
if (res.success) {
|
||||
const targetLabel =
|
||||
normalizedRules.find((r) => r.id === selectedRuleSection)?.label ||
|
||||
selectedRuleSection ||
|
||||
_('Global Default');
|
||||
if (typeof ui?.addNotification === 'function') {
|
||||
ui.addNotification(
|
||||
_('Tachyon'),
|
||||
E(
|
||||
'p',
|
||||
{},
|
||||
`${_('Strategy applied successfully and service reloaded!')} (${item.name} -> ${selectedRuleSection || _('Global Default')})`,
|
||||
`${_('Strategy applied successfully and service reloaded!')} (${item.name} -> ${targetLabel})`,
|
||||
),
|
||||
'info',
|
||||
);
|
||||
} else {
|
||||
showToast(
|
||||
`${_('Applied')} "${item.name}" -> ${selectedRuleSection || _('Global Default')}`,
|
||||
`${_('Applied')} "${item.name}" -> ${targetLabel}`,
|
||||
'success',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -169,4 +169,17 @@ describe('renderStrategyFuzzerModal', () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
expect(TachyonShellMethods.getFuzzerStatus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders rule options with human-readable labels and UCI section IDs', () => {
|
||||
renderStrategyFuzzerModal([
|
||||
{ id: 'direct', label: 'Провайдер Direct' },
|
||||
{ id: 'jobvpn', label: 'Работа VPN' },
|
||||
]);
|
||||
|
||||
expect(mocks.showModal).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.showModal).toHaveBeenCalledWith(
|
||||
'⚡ Strategy Fuzzer & Auto-Tuner',
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -182,7 +182,11 @@ function formatEndpoint(address?: string, port?: string | number): string {
|
||||
}
|
||||
|
||||
function getDisplayName(section: Tachyon.ConfigSection) {
|
||||
return normalizeString(section.label) || section['.name'];
|
||||
return (
|
||||
normalizeString(section.label) ||
|
||||
normalizeString((section as any).name) ||
|
||||
section['.name']
|
||||
);
|
||||
}
|
||||
|
||||
function buildRouteDisplayNames(sections: Tachyon.ConfigSection[]) {
|
||||
|
||||
@@ -2029,6 +2029,33 @@ function renderComponentCard(card: ComponentCard) {
|
||||
return E('div', { class: 'tachyon_updates-page__component' }, cardChildren);
|
||||
}
|
||||
|
||||
function isComponentCardVisible(
|
||||
card: ComponentCard,
|
||||
systemInfo: any,
|
||||
): boolean {
|
||||
const component = card.component;
|
||||
if (component === 'tachyon' || component === 'sing_box') {
|
||||
return true;
|
||||
}
|
||||
if (component === 'fptn') {
|
||||
const fptnInstalled = Boolean(systemInfo.fptn_installed);
|
||||
const fptnSupported =
|
||||
systemInfo.fptn_supported === undefined
|
||||
? true
|
||||
: Boolean(systemInfo.fptn_supported);
|
||||
if (!fptnInstalled && !fptnSupported) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const optKey = `show_component_${component}`;
|
||||
const val = (systemInfo as Record<string, any>)[optKey];
|
||||
if (val === undefined || val === null || val === '') {
|
||||
return true;
|
||||
}
|
||||
return String(val) === '1' || val === 1;
|
||||
}
|
||||
|
||||
function renderUpdatesComponents() {
|
||||
const container = document.getElementById('tachyon_updates-components');
|
||||
|
||||
@@ -2036,9 +2063,24 @@ function renderUpdatesComponents() {
|
||||
return;
|
||||
}
|
||||
|
||||
const systemInfo = normalizeSingBoxVariantFields(
|
||||
store.get().diagnosticsSystemInfo,
|
||||
);
|
||||
const visibleCards = getComponentCards().filter((card) =>
|
||||
isComponentCardVisible(card, systemInfo),
|
||||
);
|
||||
|
||||
const columns: Node[][] = [[], [], []];
|
||||
getComponentCards().forEach((card) => {
|
||||
columns[card.column]?.push(renderComponentCard(card));
|
||||
const colCounts = [0, 0, 0];
|
||||
visibleCards.forEach((card) => {
|
||||
colCounts[card.column] = (colCounts[card.column] || 0) + 1;
|
||||
});
|
||||
const hasEmptyColumn =
|
||||
colCounts.some((c) => c === 0) && visibleCards.length >= 3;
|
||||
|
||||
visibleCards.forEach((card, idx) => {
|
||||
const colIdx = hasEmptyColumn ? idx % 3 : card.column;
|
||||
columns[colIdx]?.push(renderComponentCard(card));
|
||||
});
|
||||
|
||||
return preserveScrollForPage(() => {
|
||||
|
||||
@@ -661,6 +661,7 @@ export namespace Tachyon {
|
||||
tailscale_installed: 0 | 1;
|
||||
fptn_version: string;
|
||||
fptn_installed: 0 | 1;
|
||||
fptn_supported?: 0 | 1;
|
||||
fptn_backup_version?: string;
|
||||
fptn_backup_time?: number;
|
||||
direct_bypass_enabled?: 0 | 1;
|
||||
@@ -670,6 +671,15 @@ export namespace Tachyon {
|
||||
torrserver_direct_available?: 0 | 1;
|
||||
torrserver_direct_enabled?: 0 | 1;
|
||||
torrserver_direct_active?: 0 | 1;
|
||||
show_component_zapret?: 0 | 1;
|
||||
show_component_zapret2?: 0 | 1;
|
||||
show_component_byedpi?: 0 | 1;
|
||||
show_component_wdtt?: 0 | 1;
|
||||
show_component_olcrtc?: 0 | 1;
|
||||
show_component_fptn?: 0 | 1;
|
||||
show_component_tailscale?: 0 | 1;
|
||||
show_component_direct_bypass?: 0 | 1;
|
||||
show_component_torrserver_direct?: 0 | 1;
|
||||
openwrt_version: string;
|
||||
device_model: string;
|
||||
generated_at?: number;
|
||||
|
||||
Reference in New Issue
Block a user