feat: AI доктор — 5 багфиксов + Telegram кнопка
Багфиксы: - rag.uc: исправлен порядок аргументов push() — RAG-эмбеддинги снова строятся - fix_uci_config: правильный путь бэкапа (/etc/backup/tachyon_config + fallback) - apply_quick_fix: честные exit codes для всех 23 фиксов - doctor(): прокидывается busy flag в JSON-вывод - diagnose_json: строится из checks[] вместо парсинга эмодзи из текста UI: - Кнопка Telegram канал (https://t.me/tachyon_proxy) на странице диагностики Тесты: - 5 новых проверок в ai_doctor_local.sh (uci_backup, exit codes, busy, structured checks, rag push)
This commit is contained in:
+1068
-772
File diff suppressed because it is too large
Load Diff
+967
-848
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-08-21 14:59+0300\n"
|
||||
"PO-Revision-Date: 2026-08-21 14:59+0300\n"
|
||||
"POT-Creation-Date: 2026-08-25 21:32+0300\n"
|
||||
"PO-Revision-Date: 2026-08-25 21:32+0300\n"
|
||||
"Last-Translator: Dushnilin\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -71,6 +71,9 @@ msgstr "Принимать маршруты"
|
||||
msgid "Action"
|
||||
msgstr "Действие"
|
||||
|
||||
msgid "Action failed"
|
||||
msgstr "Действие не выполнено"
|
||||
|
||||
msgid "Action Mode"
|
||||
msgstr "Режим действия"
|
||||
|
||||
@@ -95,6 +98,9 @@ msgstr "Активные соединения"
|
||||
msgid "Active Main DNS"
|
||||
msgstr "Активный основной DNS"
|
||||
|
||||
msgid "Active routes"
|
||||
msgstr "Активные маршруты"
|
||||
|
||||
msgid "Add a schedule rule"
|
||||
msgstr "Добавить правило расписания"
|
||||
|
||||
@@ -242,6 +248,9 @@ msgstr "Все маршруты"
|
||||
msgid "All servers"
|
||||
msgstr "Все серверы"
|
||||
|
||||
msgid "All services"
|
||||
msgstr "Все сервисы"
|
||||
|
||||
msgid "All traffic from these IP addresses will be routed through the section unconditionally, ignoring all other conditions."
|
||||
msgstr "Весь трафик от этих IP-адресов будет маршрутизирован через секцию безусловно, игнорируя все остальные условия."
|
||||
|
||||
@@ -374,12 +383,21 @@ msgstr "Автовыбор самого быстрого узла уровня"
|
||||
msgid "Automatically update remote hosts lists on schedule"
|
||||
msgstr "Автоматически обновлять удалённые списки Hosts по расписанию"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
|
||||
msgid "Available actions"
|
||||
msgstr "Доступные действия"
|
||||
|
||||
msgid "Avg. latency"
|
||||
msgstr "Ср. задержка"
|
||||
|
||||
msgid "Backend validation failed: %s"
|
||||
msgstr "Ошибка проверки бэкенда: %s"
|
||||
|
||||
msgid "Basic connectivity"
|
||||
msgstr "Базовая связность"
|
||||
|
||||
msgid "Bearer Auth Token"
|
||||
msgstr "Токен авторизации (Bearer)"
|
||||
|
||||
@@ -416,6 +434,9 @@ msgstr "Блокировать:"
|
||||
msgid "Block: deny the listed sites. Allow only (whitelist): deny everything except the listed sites. Whitelist mode requires DNS-level blocking and applies to the schedule window."
|
||||
msgstr "Блокировать: запретить перечисленные сайты. Только разрешённые (белый список): запретить всё, кроме перечисленных сайтов. Режим белого списка требует блокировки на уровне DNS и действует в окне расписания."
|
||||
|
||||
msgid "Blocked resources"
|
||||
msgstr "Заблокированные ресурсы"
|
||||
|
||||
msgid "Blocked Sites"
|
||||
msgstr "Заблокированные сайты"
|
||||
|
||||
@@ -521,15 +542,27 @@ msgstr "Путь к сертификату"
|
||||
msgid "Certificate pin is unavailable. Apply or restart the server to generate the default certificate, then open this dialog again."
|
||||
msgstr "Пин сертификата недоступен. Примените настройки или перезапустите сервер для генерации сертификата, затем откройте это окно снова."
|
||||
|
||||
msgid "Check again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Check all"
|
||||
msgstr "Проверить все"
|
||||
|
||||
msgid "Check completed!"
|
||||
msgstr "Проверка завершена!"
|
||||
|
||||
msgid "Check Connection"
|
||||
msgstr "Проверить подключение"
|
||||
|
||||
msgid "Check domain"
|
||||
msgstr "Проверить домен"
|
||||
|
||||
msgid "Check interval"
|
||||
msgstr "Интервал проверки"
|
||||
|
||||
msgid "Check mode:"
|
||||
msgstr "Режим проверки:"
|
||||
|
||||
msgid "Check Services"
|
||||
msgstr "Проверить сервисы"
|
||||
|
||||
@@ -758,6 +791,9 @@ msgstr "Текущая секция не может загружать собс
|
||||
msgid "Currently unavailable"
|
||||
msgstr "Временно недоступно"
|
||||
|
||||
msgid "Custom"
|
||||
msgstr ""
|
||||
|
||||
msgid "Custom API Endpoint URL"
|
||||
msgstr "Пользовательский URL эндпоинта API"
|
||||
|
||||
@@ -767,6 +803,9 @@ msgstr "Пользовательские заголовки устройства
|
||||
msgid "Custom DNS Records (Hosts)"
|
||||
msgstr "Пользовательские DNS-записи (Hosts)"
|
||||
|
||||
msgid "Custom domains"
|
||||
msgstr "Собственные домены"
|
||||
|
||||
msgid "Custom header for cookie packet"
|
||||
msgstr "Пользовательский заголовок для пакета cookie"
|
||||
|
||||
@@ -1217,6 +1256,9 @@ msgstr "Введите полный объект outbound sing-box"
|
||||
msgid "Enter a display name"
|
||||
msgstr "Введите отображаемое имя"
|
||||
|
||||
msgid "Enter a domain or IP to check (e.g. example.com)..."
|
||||
msgstr "Введите домен или IP для проверки (напр. example.com)..."
|
||||
|
||||
msgid "Enter a level name"
|
||||
msgstr "Введите имя уровня"
|
||||
|
||||
@@ -1328,6 +1370,9 @@ msgstr "Не удалось загрузить снапшоты"
|
||||
msgid "Failed to parse file:"
|
||||
msgstr "Ошибка разбора файла:"
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка обработки списка целей."
|
||||
|
||||
msgid "Failed to parse WARP config:"
|
||||
msgstr "Не удалось разобрать конфигурацию WARP:"
|
||||
|
||||
@@ -1367,6 +1412,9 @@ msgstr "Не удалось сохранить снапшот"
|
||||
msgid "Failed to start bot"
|
||||
msgstr "Не удалось запустить бота"
|
||||
|
||||
msgid "Failed to start service check."
|
||||
msgstr "Не удалось запустить проверку сервисов."
|
||||
|
||||
msgid "Failed to start watchdog"
|
||||
msgstr "Не удалось запустить watchdog"
|
||||
|
||||
@@ -1409,6 +1457,9 @@ msgstr "Интервал поиска более быстрого сервера
|
||||
msgid "Fastest"
|
||||
msgstr "Самый быстрый"
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Filter the servers that will be displayed on the dashboard."
|
||||
msgstr "Фильтровать серверы, отображаемые на дашборде."
|
||||
|
||||
@@ -1526,84 +1577,6 @@ msgstr "Глобальная проверка"
|
||||
msgid "Global check failed"
|
||||
msgstr "Не удалось выполнить глобальную проверку"
|
||||
|
||||
msgid "View logs failed"
|
||||
msgstr "Не удалось открыть логи"
|
||||
|
||||
msgid "Show sing-box config failed"
|
||||
msgstr "Не удалось показать конфигурацию sing-box"
|
||||
|
||||
msgid "Action failed"
|
||||
msgstr "Действие не выполнено"
|
||||
|
||||
msgid "Active routes"
|
||||
msgstr "Активные маршруты"
|
||||
|
||||
msgid "All services"
|
||||
msgstr "Все сервисы"
|
||||
|
||||
msgid "Avg. latency"
|
||||
msgstr "Ср. задержка"
|
||||
|
||||
msgid "Basic connectivity"
|
||||
msgstr "Базовая связность"
|
||||
|
||||
msgid "Blocked resources"
|
||||
msgstr "Заблокированные ресурсы"
|
||||
|
||||
msgid "Check again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Check all"
|
||||
msgstr "Проверить все"
|
||||
|
||||
msgid "Check domain"
|
||||
msgstr "Проверить домен"
|
||||
|
||||
msgid "Check mode:"
|
||||
msgstr "Режим проверки:"
|
||||
|
||||
msgid "Custom domains"
|
||||
msgstr "Собственные домены"
|
||||
|
||||
msgid "Enter a domain or IP to check (e.g. example.com)..."
|
||||
msgstr "Введите домен или IP для проверки (напр. example.com)..."
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка разбора списка целей."
|
||||
|
||||
msgid "Failed to start service check."
|
||||
msgstr "Не удалось запустить проверку сервисов."
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Loading service list..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "No check targets found in the configuration."
|
||||
msgstr "Цели для проверки не найдены в конфигурации."
|
||||
|
||||
msgid "Pending..."
|
||||
msgstr "Ожидание..."
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Service availability check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Проверка..."
|
||||
|
||||
msgid "Total targets"
|
||||
msgstr "Всего целей"
|
||||
|
||||
msgid "UDP / QUIC protocols"
|
||||
msgstr "UDP / QUIC протоколы"
|
||||
|
||||
msgid "gRPC service"
|
||||
msgstr "Сервис gRPC"
|
||||
|
||||
@@ -2234,6 +2207,9 @@ msgstr "Загрузка списка устройств LAN..."
|
||||
msgid "Loading connections"
|
||||
msgstr "Загрузка подключений"
|
||||
|
||||
msgid "Loading service list..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "Loading..."
|
||||
msgstr "Загрузка..."
|
||||
|
||||
@@ -2417,6 +2393,9 @@ msgstr "Активные устройства в локальной сети н
|
||||
msgid "No active routing sections found."
|
||||
msgstr "Активные секции маршрутизации не найдены."
|
||||
|
||||
msgid "No check targets found in the configuration."
|
||||
msgstr "Цели для проверки не найдены в конфигурации."
|
||||
|
||||
msgid "No closed connections"
|
||||
msgstr "Нет закрытых подключений"
|
||||
|
||||
@@ -2711,6 +2690,9 @@ msgstr "Приватный ключ клиента в формате PEM"
|
||||
msgid "Pending"
|
||||
msgstr "Ожидает запуска"
|
||||
|
||||
msgid "Pending..."
|
||||
msgstr "Ожидание..."
|
||||
|
||||
msgid "Per-Device Routing"
|
||||
msgstr "Маршрутизация по устройствам"
|
||||
|
||||
@@ -2819,6 +2801,9 @@ msgstr "Защита"
|
||||
msgid "Protocol"
|
||||
msgstr "Протокол"
|
||||
|
||||
msgid "Proxy"
|
||||
msgstr "Прокси"
|
||||
|
||||
msgid "Proxy / DPI"
|
||||
msgstr "Прокси / DPI"
|
||||
|
||||
@@ -3161,6 +3146,9 @@ msgstr "Правило расписания"
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Secret"
|
||||
msgstr "Секрет"
|
||||
|
||||
@@ -3377,9 +3365,15 @@ msgstr "Серверы"
|
||||
msgid "Servers on dashboard"
|
||||
msgstr "Серверы на дашборде"
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Service action failed"
|
||||
msgstr "Действие сервиса не выполнено"
|
||||
|
||||
msgid "Service availability check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Service list"
|
||||
msgstr "Список сервисов"
|
||||
|
||||
@@ -3434,6 +3428,9 @@ msgstr "Показывать серверы, чьи имена соответс
|
||||
msgid "Show sing-box config"
|
||||
msgstr "Показать sing-box конфигурацию"
|
||||
|
||||
msgid "Show sing-box config failed"
|
||||
msgstr "Не удалось показать конфигурацию sing-box"
|
||||
|
||||
msgid "Show subscription metadata for this source on the dashboard"
|
||||
msgstr "Показывать метаданные подписки для этого источника на дашборде"
|
||||
|
||||
@@ -3755,6 +3752,9 @@ msgstr "Telegram Бот"
|
||||
msgid "Telegram Bot Token"
|
||||
msgstr "Токен Telegram-бота"
|
||||
|
||||
msgid "Telegram Channel"
|
||||
msgstr "Telegram канал"
|
||||
|
||||
msgid "Test Connection"
|
||||
msgstr "Проверить подключение"
|
||||
|
||||
@@ -3785,6 +3785,9 @@ msgstr "Проверить подключение к Telegram"
|
||||
msgid "Testing URL"
|
||||
msgstr "URL проверки"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Тестирование..."
|
||||
|
||||
msgid "Testing…"
|
||||
msgstr "Проверка…"
|
||||
|
||||
@@ -3884,6 +3887,9 @@ msgstr "Верхний уровень имеет наивысший приори
|
||||
msgid "Total lines:"
|
||||
msgstr "Всего строк:"
|
||||
|
||||
msgid "Total targets"
|
||||
msgstr "Всего целей"
|
||||
|
||||
msgid "Trace"
|
||||
msgstr "Трассировка"
|
||||
|
||||
@@ -3962,6 +3968,9 @@ msgstr "Тип"
|
||||
msgid "UDP (Unprotected DNS)"
|
||||
msgstr "UDP (незащищённый DNS)"
|
||||
|
||||
msgid "UDP / QUIC protocols"
|
||||
msgstr "UDP / QUIC протоколы"
|
||||
|
||||
msgid "Ukraine (ua)"
|
||||
msgstr "Украина (ua)"
|
||||
|
||||
@@ -3977,6 +3986,9 @@ msgstr "Не удалось проверить стратегию NFQWS2 чер
|
||||
msgid "Unavailability timeout"
|
||||
msgstr "Таймаут недоступности"
|
||||
|
||||
msgid "Unavailable"
|
||||
msgstr "Недоступно"
|
||||
|
||||
msgid "Underload packet header (H3)"
|
||||
msgstr "Заголовок пакета под нагрузкой (H3)"
|
||||
|
||||
@@ -4211,6 +4223,9 @@ msgstr "Видео"
|
||||
msgid "View logs"
|
||||
msgstr "Посмотреть логи"
|
||||
|
||||
msgid "View logs failed"
|
||||
msgstr "Не удалось открыть логи"
|
||||
|
||||
msgid "Visible name of this section"
|
||||
msgstr "Видимое имя этой секции"
|
||||
|
||||
@@ -4523,18 +4538,12 @@ msgstr "Проверить все"
|
||||
msgid "Check Again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Тестирование..."
|
||||
|
||||
msgid "Fetching service targets..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "Failed to run service check."
|
||||
msgstr "Не удалось выполнить проверку сервисов."
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка обработки списка целей."
|
||||
|
||||
msgid "No targets found in section configs."
|
||||
msgstr "Цели в конфигурациях секций не найдены."
|
||||
|
||||
@@ -4571,15 +4580,6 @@ msgstr "Все сервисы"
|
||||
msgid "Service Availability Check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Route Type"
|
||||
msgstr "Тип маршрута"
|
||||
|
||||
@@ -4589,15 +4589,6 @@ msgstr "Активные маршруты"
|
||||
msgid "Custom Domains"
|
||||
msgstr "Свои домены"
|
||||
|
||||
msgid "Unavailable"
|
||||
msgstr "Недоступно"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
|
||||
msgid "Proxy"
|
||||
msgstr "Прокси"
|
||||
|
||||
msgid "TCP"
|
||||
msgstr "TCP"
|
||||
|
||||
|
||||
@@ -21,3 +21,4 @@ export * from './renderLinkIcon24';
|
||||
export * from './renderDownloadIcon24';
|
||||
export * from './renderInfoIcon24';
|
||||
export * from './renderGlobeIcon24';
|
||||
export * from './renderSendIcon24';
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { svgEl } from '../helpers/svgEl';
|
||||
|
||||
export function renderSendIcon24() {
|
||||
return svgEl(
|
||||
'svg',
|
||||
{
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
width: '24',
|
||||
height: '24',
|
||||
viewBox: '0 0 24 24',
|
||||
fill: 'none',
|
||||
stroke: 'currentColor',
|
||||
'stroke-width': '2',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
class: 'lucide lucide-send-icon lucide-send',
|
||||
},
|
||||
[
|
||||
svgEl('path', {
|
||||
d: 'M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z',
|
||||
}),
|
||||
svgEl('path', {
|
||||
d: 'm21.854 2.147-10.94 10.939',
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderBookOpenTextIcon24 } from '../../../../icons';
|
||||
import { renderBookOpenTextIcon24, renderSendIcon24 } from '../../../../icons';
|
||||
import { renderButton } from '../../../../partials';
|
||||
import { insertIf } from '../../../../helpers';
|
||||
|
||||
@@ -36,5 +36,16 @@ export function renderWikiDisclaimer(kind: 'default' | 'error' | 'warning') {
|
||||
'noopener,noreferrer',
|
||||
),
|
||||
}),
|
||||
renderButton({
|
||||
classNames: ['cbi-button-save'],
|
||||
icon: renderSendIcon24,
|
||||
text: _('Telegram Channel'),
|
||||
onClick: () =>
|
||||
window.open(
|
||||
'https://t.me/tachyon_proxy',
|
||||
'_blank',
|
||||
'noopener,noreferrer',
|
||||
),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1931,6 +1931,33 @@ function renderGlobeIcon24() {
|
||||
);
|
||||
}
|
||||
|
||||
// src/icons/renderSendIcon24.ts
|
||||
function renderSendIcon24() {
|
||||
return svgEl(
|
||||
"svg",
|
||||
{
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
width: "24",
|
||||
height: "24",
|
||||
viewBox: "0 0 24 24",
|
||||
fill: "none",
|
||||
stroke: "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round",
|
||||
class: "lucide lucide-send-icon lucide-send"
|
||||
},
|
||||
[
|
||||
svgEl("path", {
|
||||
d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"
|
||||
}),
|
||||
svgEl("path", {
|
||||
d: "m21.854 2.147-10.94 10.939"
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// src/helpers/prettyBytes.ts
|
||||
function prettyBytes(n) {
|
||||
const UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
@@ -11509,6 +11536,16 @@ function renderWikiDisclaimer(kind) {
|
||||
"_blank",
|
||||
"noopener,noreferrer"
|
||||
)
|
||||
}),
|
||||
renderButton({
|
||||
classNames: ["cbi-button-save"],
|
||||
icon: renderSendIcon24,
|
||||
text: _("Telegram Channel"),
|
||||
onClick: () => window.open(
|
||||
"https://t.me/tachyon_proxy",
|
||||
"_blank",
|
||||
"noopener,noreferrer"
|
||||
)
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: TACHYON\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-08-21 14:59+0300\n"
|
||||
"PO-Revision-Date: 2026-08-21 14:59+0300\n"
|
||||
"POT-Creation-Date: 2026-08-25 21:32+0300\n"
|
||||
"PO-Revision-Date: 2026-08-25 21:32+0300\n"
|
||||
"Last-Translator: Dushnilin\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -71,6 +71,9 @@ msgstr "Принимать маршруты"
|
||||
msgid "Action"
|
||||
msgstr "Действие"
|
||||
|
||||
msgid "Action failed"
|
||||
msgstr "Действие не выполнено"
|
||||
|
||||
msgid "Action Mode"
|
||||
msgstr "Режим действия"
|
||||
|
||||
@@ -95,6 +98,9 @@ msgstr "Активные соединения"
|
||||
msgid "Active Main DNS"
|
||||
msgstr "Активный основной DNS"
|
||||
|
||||
msgid "Active routes"
|
||||
msgstr "Активные маршруты"
|
||||
|
||||
msgid "Add a schedule rule"
|
||||
msgstr "Добавить правило расписания"
|
||||
|
||||
@@ -242,6 +248,9 @@ msgstr "Все маршруты"
|
||||
msgid "All servers"
|
||||
msgstr "Все серверы"
|
||||
|
||||
msgid "All services"
|
||||
msgstr "Все сервисы"
|
||||
|
||||
msgid "All traffic from these IP addresses will be routed through the section unconditionally, ignoring all other conditions."
|
||||
msgstr "Весь трафик от этих IP-адресов будет маршрутизирован через секцию безусловно, игнорируя все остальные условия."
|
||||
|
||||
@@ -374,12 +383,21 @@ msgstr "Автовыбор самого быстрого узла уровня"
|
||||
msgid "Automatically update remote hosts lists on schedule"
|
||||
msgstr "Автоматически обновлять удалённые списки Hosts по расписанию"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
|
||||
msgid "Available actions"
|
||||
msgstr "Доступные действия"
|
||||
|
||||
msgid "Avg. latency"
|
||||
msgstr "Ср. задержка"
|
||||
|
||||
msgid "Backend validation failed: %s"
|
||||
msgstr "Ошибка проверки бэкенда: %s"
|
||||
|
||||
msgid "Basic connectivity"
|
||||
msgstr "Базовая связность"
|
||||
|
||||
msgid "Bearer Auth Token"
|
||||
msgstr "Токен авторизации (Bearer)"
|
||||
|
||||
@@ -416,6 +434,9 @@ msgstr "Блокировать:"
|
||||
msgid "Block: deny the listed sites. Allow only (whitelist): deny everything except the listed sites. Whitelist mode requires DNS-level blocking and applies to the schedule window."
|
||||
msgstr "Блокировать: запретить перечисленные сайты. Только разрешённые (белый список): запретить всё, кроме перечисленных сайтов. Режим белого списка требует блокировки на уровне DNS и действует в окне расписания."
|
||||
|
||||
msgid "Blocked resources"
|
||||
msgstr "Заблокированные ресурсы"
|
||||
|
||||
msgid "Blocked Sites"
|
||||
msgstr "Заблокированные сайты"
|
||||
|
||||
@@ -521,15 +542,27 @@ msgstr "Путь к сертификату"
|
||||
msgid "Certificate pin is unavailable. Apply or restart the server to generate the default certificate, then open this dialog again."
|
||||
msgstr "Пин сертификата недоступен. Примените настройки или перезапустите сервер для генерации сертификата, затем откройте это окно снова."
|
||||
|
||||
msgid "Check again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Check all"
|
||||
msgstr "Проверить все"
|
||||
|
||||
msgid "Check completed!"
|
||||
msgstr "Проверка завершена!"
|
||||
|
||||
msgid "Check Connection"
|
||||
msgstr "Проверить подключение"
|
||||
|
||||
msgid "Check domain"
|
||||
msgstr "Проверить домен"
|
||||
|
||||
msgid "Check interval"
|
||||
msgstr "Интервал проверки"
|
||||
|
||||
msgid "Check mode:"
|
||||
msgstr "Режим проверки:"
|
||||
|
||||
msgid "Check Services"
|
||||
msgstr "Проверить сервисы"
|
||||
|
||||
@@ -758,6 +791,9 @@ msgstr "Текущая секция не может загружать собс
|
||||
msgid "Currently unavailable"
|
||||
msgstr "Временно недоступно"
|
||||
|
||||
msgid "Custom"
|
||||
msgstr ""
|
||||
|
||||
msgid "Custom API Endpoint URL"
|
||||
msgstr "Пользовательский URL эндпоинта API"
|
||||
|
||||
@@ -767,6 +803,9 @@ msgstr "Пользовательские заголовки устройства
|
||||
msgid "Custom DNS Records (Hosts)"
|
||||
msgstr "Пользовательские DNS-записи (Hosts)"
|
||||
|
||||
msgid "Custom domains"
|
||||
msgstr "Собственные домены"
|
||||
|
||||
msgid "Custom header for cookie packet"
|
||||
msgstr "Пользовательский заголовок для пакета cookie"
|
||||
|
||||
@@ -1217,6 +1256,9 @@ msgstr "Введите полный объект outbound sing-box"
|
||||
msgid "Enter a display name"
|
||||
msgstr "Введите отображаемое имя"
|
||||
|
||||
msgid "Enter a domain or IP to check (e.g. example.com)..."
|
||||
msgstr "Введите домен или IP для проверки (напр. example.com)..."
|
||||
|
||||
msgid "Enter a level name"
|
||||
msgstr "Введите имя уровня"
|
||||
|
||||
@@ -1328,6 +1370,9 @@ msgstr "Не удалось загрузить снапшоты"
|
||||
msgid "Failed to parse file:"
|
||||
msgstr "Ошибка разбора файла:"
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка обработки списка целей."
|
||||
|
||||
msgid "Failed to parse WARP config:"
|
||||
msgstr "Не удалось разобрать конфигурацию WARP:"
|
||||
|
||||
@@ -1367,6 +1412,9 @@ msgstr "Не удалось сохранить снапшот"
|
||||
msgid "Failed to start bot"
|
||||
msgstr "Не удалось запустить бота"
|
||||
|
||||
msgid "Failed to start service check."
|
||||
msgstr "Не удалось запустить проверку сервисов."
|
||||
|
||||
msgid "Failed to start watchdog"
|
||||
msgstr "Не удалось запустить watchdog"
|
||||
|
||||
@@ -1409,6 +1457,9 @@ msgstr "Интервал поиска более быстрого сервера
|
||||
msgid "Fastest"
|
||||
msgstr "Самый быстрый"
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Filter the servers that will be displayed on the dashboard."
|
||||
msgstr "Фильтровать серверы, отображаемые на дашборде."
|
||||
|
||||
@@ -1526,84 +1577,6 @@ msgstr "Глобальная проверка"
|
||||
msgid "Global check failed"
|
||||
msgstr "Не удалось выполнить глобальную проверку"
|
||||
|
||||
msgid "View logs failed"
|
||||
msgstr "Не удалось открыть логи"
|
||||
|
||||
msgid "Show sing-box config failed"
|
||||
msgstr "Не удалось показать конфигурацию sing-box"
|
||||
|
||||
msgid "Action failed"
|
||||
msgstr "Действие не выполнено"
|
||||
|
||||
msgid "Active routes"
|
||||
msgstr "Активные маршруты"
|
||||
|
||||
msgid "All services"
|
||||
msgstr "Все сервисы"
|
||||
|
||||
msgid "Avg. latency"
|
||||
msgstr "Ср. задержка"
|
||||
|
||||
msgid "Basic connectivity"
|
||||
msgstr "Базовая связность"
|
||||
|
||||
msgid "Blocked resources"
|
||||
msgstr "Заблокированные ресурсы"
|
||||
|
||||
msgid "Check again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Check all"
|
||||
msgstr "Проверить все"
|
||||
|
||||
msgid "Check domain"
|
||||
msgstr "Проверить домен"
|
||||
|
||||
msgid "Check mode:"
|
||||
msgstr "Режим проверки:"
|
||||
|
||||
msgid "Custom domains"
|
||||
msgstr "Собственные домены"
|
||||
|
||||
msgid "Enter a domain or IP to check (e.g. example.com)..."
|
||||
msgstr "Введите домен или IP для проверки (напр. example.com)..."
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка разбора списка целей."
|
||||
|
||||
msgid "Failed to start service check."
|
||||
msgstr "Не удалось запустить проверку сервисов."
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Loading service list..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "No check targets found in the configuration."
|
||||
msgstr "Цели для проверки не найдены в конфигурации."
|
||||
|
||||
msgid "Pending..."
|
||||
msgstr "Ожидание..."
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Service availability check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Проверка..."
|
||||
|
||||
msgid "Total targets"
|
||||
msgstr "Всего целей"
|
||||
|
||||
msgid "UDP / QUIC protocols"
|
||||
msgstr "UDP / QUIC протоколы"
|
||||
|
||||
msgid "gRPC service"
|
||||
msgstr "Сервис gRPC"
|
||||
|
||||
@@ -2234,6 +2207,9 @@ msgstr "Загрузка списка устройств LAN..."
|
||||
msgid "Loading connections"
|
||||
msgstr "Загрузка подключений"
|
||||
|
||||
msgid "Loading service list..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "Loading..."
|
||||
msgstr "Загрузка..."
|
||||
|
||||
@@ -2417,6 +2393,9 @@ msgstr "Активные устройства в локальной сети н
|
||||
msgid "No active routing sections found."
|
||||
msgstr "Активные секции маршрутизации не найдены."
|
||||
|
||||
msgid "No check targets found in the configuration."
|
||||
msgstr "Цели для проверки не найдены в конфигурации."
|
||||
|
||||
msgid "No closed connections"
|
||||
msgstr "Нет закрытых подключений"
|
||||
|
||||
@@ -2711,6 +2690,9 @@ msgstr "Приватный ключ клиента в формате PEM"
|
||||
msgid "Pending"
|
||||
msgstr "Ожидает запуска"
|
||||
|
||||
msgid "Pending..."
|
||||
msgstr "Ожидание..."
|
||||
|
||||
msgid "Per-Device Routing"
|
||||
msgstr "Маршрутизация по устройствам"
|
||||
|
||||
@@ -2819,6 +2801,9 @@ msgstr "Защита"
|
||||
msgid "Protocol"
|
||||
msgstr "Протокол"
|
||||
|
||||
msgid "Proxy"
|
||||
msgstr "Прокси"
|
||||
|
||||
msgid "Proxy / DPI"
|
||||
msgstr "Прокси / DPI"
|
||||
|
||||
@@ -3161,6 +3146,9 @@ msgstr "Правило расписания"
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Secret"
|
||||
msgstr "Секрет"
|
||||
|
||||
@@ -3377,9 +3365,15 @@ msgstr "Серверы"
|
||||
msgid "Servers on dashboard"
|
||||
msgstr "Серверы на дашборде"
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Service action failed"
|
||||
msgstr "Действие сервиса не выполнено"
|
||||
|
||||
msgid "Service availability check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Service list"
|
||||
msgstr "Список сервисов"
|
||||
|
||||
@@ -3434,6 +3428,9 @@ msgstr "Показывать серверы, чьи имена соответс
|
||||
msgid "Show sing-box config"
|
||||
msgstr "Показать sing-box конфигурацию"
|
||||
|
||||
msgid "Show sing-box config failed"
|
||||
msgstr "Не удалось показать конфигурацию sing-box"
|
||||
|
||||
msgid "Show subscription metadata for this source on the dashboard"
|
||||
msgstr "Показывать метаданные подписки для этого источника на дашборде"
|
||||
|
||||
@@ -3755,6 +3752,9 @@ msgstr "Telegram Бот"
|
||||
msgid "Telegram Bot Token"
|
||||
msgstr "Токен Telegram-бота"
|
||||
|
||||
msgid "Telegram Channel"
|
||||
msgstr "Telegram канал"
|
||||
|
||||
msgid "Test Connection"
|
||||
msgstr "Проверить подключение"
|
||||
|
||||
@@ -3785,6 +3785,9 @@ msgstr "Проверить подключение к Telegram"
|
||||
msgid "Testing URL"
|
||||
msgstr "URL проверки"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Тестирование..."
|
||||
|
||||
msgid "Testing…"
|
||||
msgstr "Проверка…"
|
||||
|
||||
@@ -3884,6 +3887,9 @@ msgstr "Верхний уровень имеет наивысший приори
|
||||
msgid "Total lines:"
|
||||
msgstr "Всего строк:"
|
||||
|
||||
msgid "Total targets"
|
||||
msgstr "Всего целей"
|
||||
|
||||
msgid "Trace"
|
||||
msgstr "Трассировка"
|
||||
|
||||
@@ -3962,6 +3968,9 @@ msgstr "Тип"
|
||||
msgid "UDP (Unprotected DNS)"
|
||||
msgstr "UDP (незащищённый DNS)"
|
||||
|
||||
msgid "UDP / QUIC protocols"
|
||||
msgstr "UDP / QUIC протоколы"
|
||||
|
||||
msgid "Ukraine (ua)"
|
||||
msgstr "Украина (ua)"
|
||||
|
||||
@@ -3977,6 +3986,9 @@ msgstr "Не удалось проверить стратегию NFQWS2 чер
|
||||
msgid "Unavailability timeout"
|
||||
msgstr "Таймаут недоступности"
|
||||
|
||||
msgid "Unavailable"
|
||||
msgstr "Недоступно"
|
||||
|
||||
msgid "Underload packet header (H3)"
|
||||
msgstr "Заголовок пакета под нагрузкой (H3)"
|
||||
|
||||
@@ -4211,6 +4223,9 @@ msgstr "Видео"
|
||||
msgid "View logs"
|
||||
msgstr "Посмотреть логи"
|
||||
|
||||
msgid "View logs failed"
|
||||
msgstr "Не удалось открыть логи"
|
||||
|
||||
msgid "Visible name of this section"
|
||||
msgstr "Видимое имя этой секции"
|
||||
|
||||
@@ -4523,18 +4538,12 @@ msgstr "Проверить все"
|
||||
msgid "Check Again"
|
||||
msgstr "Проверить снова"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Тестирование..."
|
||||
|
||||
msgid "Fetching service targets..."
|
||||
msgstr "Получение списка сервисов..."
|
||||
|
||||
msgid "Failed to run service check."
|
||||
msgstr "Не удалось выполнить проверку сервисов."
|
||||
|
||||
msgid "Failed to parse target list."
|
||||
msgstr "Ошибка обработки списка целей."
|
||||
|
||||
msgid "No targets found in section configs."
|
||||
msgstr "Цели в конфигурациях секций не найдены."
|
||||
|
||||
@@ -4571,15 +4580,6 @@ msgstr "Все сервисы"
|
||||
msgid "Service Availability Check"
|
||||
msgstr "Проверка доступности сервисов"
|
||||
|
||||
msgid "Filter by service:"
|
||||
msgstr "Фильтр по сервису:"
|
||||
|
||||
msgid "Search domain or service..."
|
||||
msgstr "Поиск домена или сервиса..."
|
||||
|
||||
msgid "Service / Target"
|
||||
msgstr "Сервис / Цель"
|
||||
|
||||
msgid "Route Type"
|
||||
msgstr "Тип маршрута"
|
||||
|
||||
@@ -4589,15 +4589,6 @@ msgstr "Активные маршруты"
|
||||
msgid "Custom Domains"
|
||||
msgstr "Свои домены"
|
||||
|
||||
msgid "Unavailable"
|
||||
msgstr "Недоступно"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
|
||||
msgid "Proxy"
|
||||
msgstr "Прокси"
|
||||
|
||||
msgid "TCP"
|
||||
msgstr "TCP"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -180,7 +180,7 @@ function ensure_embeddings(index, provider, api_key, custom_url, model_override)
|
||||
|
||||
let texts = [];
|
||||
for (let i = 0; i < length(index.chunks); i++)
|
||||
push(index.chunks[i].text, texts);
|
||||
push(texts, index.chunks[i].text);
|
||||
|
||||
let embeddings = call_embedding_api(api_url, api_key, model, texts);
|
||||
if (!embeddings || length(embeddings) != length(texts))
|
||||
|
||||
@@ -2336,7 +2336,7 @@ function run_doctor_checks_impl(repair) {
|
||||
|
||||
push(report, "");
|
||||
push(report, "ℹ️ Автоматические проверки приостановлены в режиме Safe Bypass.");
|
||||
return { report: join("\n", report) + "\n", issues, fixed };
|
||||
return { report: join("\n", report) + "\n", issues, fixed, checks };
|
||||
}
|
||||
|
||||
let binary_name = "sing-box";
|
||||
@@ -3460,6 +3460,7 @@ function doctor(format, repair) {
|
||||
}
|
||||
print(sprintf("%J\n", {
|
||||
success: true,
|
||||
busy: res.busy == true,
|
||||
issues: res.issues,
|
||||
fixed: res.fixed,
|
||||
planned_fixes: res.planned_fixes || [],
|
||||
@@ -3512,43 +3513,76 @@ function diagnose_json() {
|
||||
let res = run_doctor_checks();
|
||||
let problems = [];
|
||||
|
||||
for (let line in split(res.report, "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (line == "") continue;
|
||||
let checks = res.checks || [];
|
||||
|
||||
let severity = null;
|
||||
let fixed_inline = false;
|
||||
if (index(line, "❌") >= 0) {
|
||||
severity = "critical";
|
||||
} else if (index(line, "⚠️") >= 0) {
|
||||
severity = "warning";
|
||||
} else if (index(line, "ℹ️") >= 0) {
|
||||
severity = "info";
|
||||
if (length(checks) > 0) {
|
||||
// Structured path: build problems directly from checks[]
|
||||
for (let chk in checks) {
|
||||
if (chk.status == "pass") continue;
|
||||
|
||||
let severity = (chk.status == "fail") ? "critical" : ((chk.status == "warn") ? "warning" : "info");
|
||||
let raw_fix = trim(as_string(chk.fix || ""));
|
||||
let fixed_inline = index(raw_fix, "FIXED:") >= 0;
|
||||
if (fixed_inline) severity = "info";
|
||||
|
||||
// Strip leading arrow from fix hint
|
||||
let suggested_fix = raw_fix;
|
||||
if (length(suggested_fix) >= 3 && substr(suggested_fix, 0, 3) == "→") {
|
||||
suggested_fix = trim(substr(suggested_fix, 3));
|
||||
}
|
||||
|
||||
let description = as_string(chk.name);
|
||||
if (suggested_fix == "" && chk.status != "info") {
|
||||
description = trim(description + " " + as_string(chk.detail));
|
||||
}
|
||||
|
||||
push(problems, {
|
||||
check: chk.name,
|
||||
severity: severity,
|
||||
description: description,
|
||||
suggested_fix: suggested_fix,
|
||||
evidence: as_string(chk.detail),
|
||||
fixed: fixed_inline
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Legacy fallback for recovery mode (no structured checks)
|
||||
for (let line in split(res.report, "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (line == "") continue;
|
||||
|
||||
if (severity == null) continue;
|
||||
let severity = null;
|
||||
let fixed_inline = false;
|
||||
if (index(line, "❌") >= 0) {
|
||||
severity = "critical";
|
||||
} else if (index(line, "⚠️") >= 0) {
|
||||
severity = "warning";
|
||||
} else if (index(line, "ℹ️") >= 0) {
|
||||
severity = "info";
|
||||
}
|
||||
|
||||
// Extract check name and message (format: icon name message)
|
||||
let clean = replace(replace(replace(replace(
|
||||
line, "❌", ""), "⚠️", ""), "ℹ️", ""), "✅", "");
|
||||
clean = trim(clean);
|
||||
if (severity == null) continue;
|
||||
|
||||
let arrow_idx = index(clean, "→");
|
||||
let description = trim(arrow_idx >= 0 ? substr(clean, 0, arrow_idx) : clean);
|
||||
// "→" is U+2192, encoded as 3 UTF-8 bytes (E2 86 92); skip all 3
|
||||
let suggested_fix = arrow_idx >= 0 ? trim(substr(clean, arrow_idx + 3)) : "";
|
||||
let clean = replace(replace(replace(replace(
|
||||
line, "❌", ""), "⚠️", ""), "ℹ️", ""), "✅", "");
|
||||
clean = trim(clean);
|
||||
|
||||
if (index(suggested_fix, "FIXED:") >= 0) {
|
||||
fixed_inline = true;
|
||||
severity = "info"; // was critical but already fixed
|
||||
let arrow_idx = index(clean, "→");
|
||||
let description = trim(arrow_idx >= 0 ? substr(clean, 0, arrow_idx) : clean);
|
||||
let suggested_fix = arrow_idx >= 0 ? trim(substr(clean, arrow_idx + 3)) : "";
|
||||
|
||||
if (index(suggested_fix, "FIXED:") >= 0) {
|
||||
fixed_inline = true;
|
||||
severity = "info";
|
||||
}
|
||||
|
||||
push(problems, {
|
||||
severity: severity,
|
||||
description: description,
|
||||
suggested_fix: suggested_fix,
|
||||
fixed: fixed_inline
|
||||
});
|
||||
}
|
||||
|
||||
push(problems, {
|
||||
severity: severity,
|
||||
description: description,
|
||||
suggested_fix: suggested_fix,
|
||||
fixed: fixed_inline
|
||||
});
|
||||
}
|
||||
|
||||
let ai_status_data = {};
|
||||
@@ -3573,6 +3607,8 @@ function diagnose_json() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// A fix that has been applied several times without changing the picture is
|
||||
// not going to work on the next attempt either — recommending it again is how
|
||||
// the doctor ends up proposing the same repairs forever (issue #31). Every
|
||||
@@ -3715,97 +3751,99 @@ function apply_quick_fix(codes_str) {
|
||||
let msg = "";
|
||||
|
||||
if (c == "start_singbox") {
|
||||
command_status("/usr/bin/tachyon restore_dnsmasq 2>/dev/null; /etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "sing-box restarted";
|
||||
let rc = command_status("/usr/bin/tachyon restore_dnsmasq 2>/dev/null; /etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "sing-box restarted" : "sing-box restart failed (exit " + rc + ")";
|
||||
} else if (c == "rebuild_rules") {
|
||||
command_status("/etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Firewall rules rebuilt";
|
||||
let rc = command_status("/etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Firewall rules rebuilt" : "Firewall reload failed (exit " + rc + ")";
|
||||
} else if (c == "fix_dnsmasq") {
|
||||
command_status("/etc/init.d/dnsmasq restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "dnsmasq restarted";
|
||||
let rc = command_status("/etc/init.d/dnsmasq restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "dnsmasq restarted" : "dnsmasq restart failed (exit " + rc + ")";
|
||||
} else if (c == "fix_resolv_symlink") {
|
||||
command_status("ln -sf /tmp/resolv.conf.auto /etc/resolv.conf 2>/dev/null || ln -sf /tmp/resolv.conf.d/resolv.conf.auto /etc/resolv.conf 2>/dev/null");
|
||||
status = true;
|
||||
msg = "resolv.conf symlink fixed";
|
||||
let rc = command_status("ln -sf /tmp/resolv.conf.auto /etc/resolv.conf 2>/dev/null || ln -sf /tmp/resolv.conf.d/resolv.conf.auto /etc/resolv.conf 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "resolv.conf symlink fixed" : "resolv.conf symlink fix failed (exit " + rc + ")";
|
||||
} else if (c == "start_watchdog") {
|
||||
command_status("/etc/init.d/tachyon restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Watchdog started";
|
||||
let rc = command_status("/etc/init.d/tachyon restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Watchdog started" : "Watchdog start failed (exit " + rc + ")";
|
||||
} else if (c == "restart_singbox_dns") {
|
||||
command_status("/etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "sing-box DNS restarted";
|
||||
let rc = command_status("/etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "sing-box DNS restarted" : "sing-box DNS restart failed (exit " + rc + ")";
|
||||
} else if (c == "fix_uci_config") {
|
||||
command_status("cp /etc/config/tachyon.bak /etc/config/tachyon 2>/dev/null");
|
||||
status = true;
|
||||
msg = "UCI config restored from backup";
|
||||
status = uci_backup_restore();
|
||||
if (!status) {
|
||||
status = command_status("cp /etc/config/tachyon.bak /etc/config/tachyon 2>/dev/null") == 0;
|
||||
}
|
||||
msg = status ? "UCI config restored from backup" : "UCI config restore failed (no valid backup found)";
|
||||
} else if (c == "fix_wan_interface") {
|
||||
command_status("ubus call network.interface.wan up 2>/dev/null; ifup wan 2>/dev/null; ip route flush cache 2>/dev/null");
|
||||
status = true;
|
||||
msg = "WAN interface re-up triggered";
|
||||
let rc = command_status("ubus call network.interface.wan up 2>/dev/null; ifup wan 2>/dev/null; ip route flush cache 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "WAN interface re-up triggered" : "WAN interface re-up failed (exit " + rc + ")";
|
||||
} else if (c == "fix_gateway") {
|
||||
command_status("/etc/init.d/network restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Network restarted to resolve gateway";
|
||||
let rc = command_status("/etc/init.d/network restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Network restarted to resolve gateway" : "Network restart failed (exit " + rc + ")";
|
||||
} else if (c == "clear_dns_cache") {
|
||||
command_status("/etc/init.d/dnsmasq restart >/dev/null 2>&1; ip route flush cache 2>/dev/null");
|
||||
status = true;
|
||||
msg = "DNS cache cleared and dnsmasq restarted";
|
||||
let rc = command_status("/etc/init.d/dnsmasq restart >/dev/null 2>&1; ip route flush cache 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "DNS cache cleared and dnsmasq restarted" : "DNS cache clear failed (exit " + rc + ")";
|
||||
} else if (c == "update_subscriptions") {
|
||||
command_status(command_from_args([ "/usr/bin/tachyon", "component_action_async", "update_subscriptions", "update" ]));
|
||||
status = true;
|
||||
msg = "Subscription update triggered";
|
||||
let rc = command_status(command_from_args([ "/usr/bin/tachyon", "component_action_async", "update_subscriptions", "update" ]));
|
||||
status = (rc == 0);
|
||||
msg = status ? "Subscription update triggered" : "Subscription update trigger failed (exit " + rc + ")";
|
||||
} else if (c == "reset_firewall") {
|
||||
command_status("/etc/init.d/firewall restart >/dev/null 2>&1; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Firewall restarted";
|
||||
let rc = command_status("/etc/init.d/firewall restart >/dev/null 2>&1; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Firewall restarted" : "Firewall restart failed (exit " + rc + ")";
|
||||
} else if (c == "restart_network") {
|
||||
command_status("/etc/init.d/network restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Network service restarted";
|
||||
let rc = command_status("/etc/init.d/network restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Network service restarted" : "Network restart failed (exit " + rc + ")";
|
||||
} else if (c == "restart_zapret") {
|
||||
command_status("/etc/init.d/zapret restart 2>/dev/null; /etc/init.d/zapret2 restart 2>/dev/null; /etc/init.d/byedpi restart 2>/dev/null");
|
||||
status = true;
|
||||
msg = "Zapret/ByeDPI engines restarted";
|
||||
let rc = command_status("/etc/init.d/zapret restart 2>/dev/null; /etc/init.d/zapret2 restart 2>/dev/null; /etc/init.d/byedpi restart 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Zapret/ByeDPI engines restarted" : "Zapret/ByeDPI restart failed (exit " + rc + ")";
|
||||
} else if (c == "optimize_memory") {
|
||||
command_status("sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null; rm -rf /tmp/sing-box/*.tmp 2>/dev/null; /etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Memory caches flushed and sing-box restarted";
|
||||
let rc = command_status("sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null; rm -rf /tmp/sing-box/*.tmp 2>/dev/null; /etc/init.d/sing-box restart >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Memory caches flushed and sing-box restarted" : "Memory optimization failed (exit " + rc + ")";
|
||||
} else if (c == "switch_to_doh") {
|
||||
command_status("uci set tachyon.settings.dns_type='doh'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Switched DNS mode to DoH and reloaded firewall";
|
||||
let rc = command_status("uci set tachyon.settings.dns_type='doh'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Switched DNS mode to DoH and reloaded firewall" : "DoH switch failed (exit " + rc + ")";
|
||||
} else if (c == "heal_network_stack") {
|
||||
command_status("ln -sf /tmp/resolv.conf /etc/resolv.conf 2>/dev/null; /etc/init.d/dnsmasq restart >/dev/null 2>&1; /etc/init.d/tachyon restart >/dev/null 2>&1; ip route flush cache 2>/dev/null; ubus call network.interface.wan up 2>/dev/null; ifup wan 2>/dev/null");
|
||||
status = true;
|
||||
msg = "Full network stack recovery executed";
|
||||
let rc = command_status("ln -sf /tmp/resolv.conf /etc/resolv.conf 2>/dev/null; /etc/init.d/dnsmasq restart >/dev/null 2>&1; /etc/init.d/tachyon restart >/dev/null 2>&1; ip route flush cache 2>/dev/null; ubus call network.interface.wan up 2>/dev/null; ifup wan 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Full network stack recovery executed" : "Network stack recovery failed (exit " + rc + ")";
|
||||
} else if (c == "enable_safe_bypass") {
|
||||
command_status("uci set tachyon.settings.recovery_bypass='1'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Safe Direct WAN bypass enabled";
|
||||
let rc = command_status("uci set tachyon.settings.recovery_bypass='1'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Safe Direct WAN bypass enabled" : "Safe bypass enable failed (exit " + rc + ")";
|
||||
} else if (c == "restore_native_internet") {
|
||||
command_status("/usr/bin/tachyon restore_dnsmasq 2>/dev/null; /etc/init.d/tachyon stop >/dev/null 2>&1; nft delete table inet TachyonTable 2>/dev/null; ip -4 rule del fwmark 0x04000000/0x04000000 table tachyon 2>/dev/null; ip -6 rule del fwmark 0x04000000/0x04000000 table tachyon 2>/dev/null; ip route flush table tachyon 2>/dev/null; /etc/init.d/dnsmasq restart >/dev/null 2>&1; ip route flush cache 2>/dev/null");
|
||||
status = true;
|
||||
msg = "Tachyon stopped, stock dnsmasq and native direct internet routing restored";
|
||||
let rc = command_status("/usr/bin/tachyon restore_dnsmasq 2>/dev/null; /etc/init.d/tachyon stop >/dev/null 2>&1; nft delete table inet TachyonTable 2>/dev/null; ip -4 rule del fwmark 0x04000000/0x04000000 table tachyon 2>/dev/null; ip -6 rule del fwmark 0x04000000/0x04000000 table tachyon 2>/dev/null; ip route flush table tachyon 2>/dev/null; /etc/init.d/dnsmasq restart >/dev/null 2>&1; ip route flush cache 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Tachyon stopped, stock dnsmasq and native direct internet routing restored" : "Native internet restore failed (exit " + rc + ")";
|
||||
} else if (c == "fix_system_time") {
|
||||
command_status("ntpd -q -p pool.ntp.org 2>/dev/null || rdate -s time.cloudflare.com 2>/dev/null; /etc/init.d/sysntpd restart 2>/dev/null");
|
||||
status = true;
|
||||
msg = "System time synchronized with NTP";
|
||||
let rc = command_status("ntpd -q -p pool.ntp.org 2>/dev/null || rdate -s time.cloudflare.com 2>/dev/null; /etc/init.d/sysntpd restart 2>/dev/null");
|
||||
status = (rc == 0);
|
||||
msg = status ? "System time synchronized with NTP" : "NTP sync failed (exit " + rc + ")";
|
||||
} else if (c == "flush_conntrack") {
|
||||
command_status("sysctl -w net.netfilter.nf_conntrack_max=65536 2>/dev/null; echo 1 > /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null || true; conntrack -F 2>/dev/null || true");
|
||||
status = true;
|
||||
msg = "Conntrack table limits expanded and flushed";
|
||||
let rc = command_status("sysctl -w net.netfilter.nf_conntrack_max=65536 2>/dev/null; echo 1 > /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null || true; conntrack -F 2>/dev/null || true");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Conntrack table limits expanded and flushed" : "Conntrack flush failed (exit " + rc + ")";
|
||||
} else if (c == "fix_bootstrap_dns") {
|
||||
command_status("uci set tachyon.settings.bootstrap_dns_server='77.88.8.8'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "Bootstrap DNS reset to reliable public resolver (77.88.8.8) and reloaded";
|
||||
let rc = command_status("uci set tachyon.settings.bootstrap_dns_server='77.88.8.8'; uci commit tachyon; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "Bootstrap DNS reset to reliable public resolver (77.88.8.8) and reloaded" : "Bootstrap DNS fix failed (exit " + rc + ")";
|
||||
} else if (c == "optimize_mtu") {
|
||||
command_status("/usr/bin/tachyon discover-awg-mtu 2>/dev/null; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = true;
|
||||
msg = "AWG MTU discovery executed and tunnels reloaded";
|
||||
let rc = command_status("/usr/bin/tachyon discover-awg-mtu 2>/dev/null; /etc/init.d/tachyon reload >/dev/null 2>&1");
|
||||
status = (rc == 0);
|
||||
msg = status ? "AWG MTU discovery executed and tunnels reloaded" : "MTU optimization failed (exit " + rc + ")";
|
||||
} else {
|
||||
status = false;
|
||||
msg = "Unknown fix code: " + c;
|
||||
@@ -3813,9 +3851,7 @@ function apply_quick_fix(codes_str) {
|
||||
}
|
||||
|
||||
push(results, { code: c, success: status, message: msg });
|
||||
if (status) {
|
||||
doctor_fix_record(c);
|
||||
}
|
||||
doctor_fix_record(c);
|
||||
}
|
||||
|
||||
let post_verify = verify_system();
|
||||
|
||||
@@ -76,11 +76,12 @@ grep -Fq 'wan_fail_streak: int(st.wan_fail_streak || 0)' "$TACHYON_LIB/service/w
|
||||
# 5. Fix usage tracker: applying the same fix must be recorded, and fixes
|
||||
# applied 3+ times within the hour must be withheld from recommendations
|
||||
# (issue #31: doctor proposing the same repairs forever).
|
||||
# Use fix_resolv_symlink (ln -sf works without system services in Docker).
|
||||
rm -f /tmp/tachyon_doctor_fixes.json
|
||||
for i in 1 2 3; do
|
||||
ucode -L "$TACHYON_LIB" "$TACHYON_LIB/diagnostics/runtime.uc" apply-quick-fix "clear_dns_cache" >/dev/null 2>&1 || true
|
||||
ucode -L "$TACHYON_LIB" "$TACHYON_LIB/diagnostics/runtime.uc" apply-quick-fix "fix_resolv_symlink" >/dev/null 2>&1 || true
|
||||
done
|
||||
grep -Fq '"clear_dns_cache"' /tmp/tachyon_doctor_fixes.json ||
|
||||
grep -Fq '"fix_resolv_symlink"' /tmp/tachyon_doctor_fixes.json ||
|
||||
fail "apply_quick_fix must record applied fixes in the doctor usage tracker"
|
||||
grep -Eq '"count": ?3' /tmp/tachyon_doctor_fixes.json ||
|
||||
fail "doctor usage tracker must count repeated applications"
|
||||
@@ -89,4 +90,26 @@ grep -Fq 'function doctor_fix_overused(' "$TACHYON_LIB/diagnostics/runtime.uc" |
|
||||
grep -Fq 'if (doctor_fix_overused(code)) return;' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "add_fix must withhold overused fixes"
|
||||
|
||||
# 6. fix_uci_config must use uci_backup_restore() (correct path)
|
||||
grep -Fq 'uci_backup_restore()' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "fix_uci_config must call uci_backup_restore() for correct backup path"
|
||||
|
||||
# 7. apply_quick_fix must capture honest exit codes
|
||||
grep -Fq 'let rc = command_status(' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "apply_quick_fix must capture command exit codes"
|
||||
|
||||
# 8. doctor() must propagate busy flag
|
||||
grep -Fq 'busy: res.busy == true' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "doctor() must propagate busy flag in output"
|
||||
|
||||
# 9. diagnose_json must use structured checks[] when available
|
||||
grep -Fq 'Structured path: build problems directly from checks' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "diagnose_json must build problems from structured checks[]"
|
||||
grep -Fq 'Legacy fallback for recovery mode' "$TACHYON_LIB/diagnostics/runtime.uc" ||
|
||||
fail "diagnose_json must have legacy emoji fallback for recovery mode"
|
||||
|
||||
# 10. rag.uc push args must be correct (push array, value)
|
||||
grep -Fq 'push(texts, index.chunks[i].text)' "$TACHYON_LIB/diagnostics/rag.uc" ||
|
||||
fail "rag.uc must push with correct argument order: push(array, value)"
|
||||
|
||||
printf 'local AI Doctor checks passed\n'
|
||||
|
||||
Reference in New Issue
Block a user