fix(ui): показывать реальный источник и ошибки DNS
This commit is contained in:
1 parent
155dfc5716
commit
1b91ba4e1d
6 files changed
+30
-5
No files matched your search
@@ -72,7 +72,7 @@ const TOOL_META: Record<DiagTool, {
|
||||
ping: { label: "Ping", ros: "/tool ping", Icon: ActivityIcon, color: "text-sky-500", description: "Проверка связи и RTT" },
|
||||
traceroute: { label: "Traceroute", ros: "/tool traceroute", Icon: RouteIcon, color: "text-violet-500", description: "Трассировка маршрута" },
|
||||
bandwidth: { label: "BW-тест", ros: "/tool bandwidth-test", Icon: ZapIcon, color: "text-emerald-500", description: "Пропускная способность" },
|
||||
dns: { label: "DNS", ros: "/resolve", Icon: SearchIcon, color: "text-amber-500", description: "Разрешение DNS-имён" },
|
||||
dns: { label: "DNS", ros: "DNS приложения", Icon: SearchIcon, color: "text-amber-500", description: "DNS на хосте приложения" },
|
||||
route: { label: "Маршрут", ros: "/ip route lookup", Icon: NetworkIcon, color: "text-blue-500", description: "Поиск активного маршрута" },
|
||||
mtu: { label: "MTU-тест", ros: "/tool ping", Icon: RulerIcon, color: "text-orange-500", description: "Определение MTU пути" },
|
||||
}
|
||||
@@ -312,7 +312,7 @@ function buildCommand(
|
||||
case "bandwidth":
|
||||
return `/tool bandwidth-test address=${opts.bwTarget ?? target} duration=${opts.bwDuration ?? 10}s protocol=${opts.bwProto ?? "tcp"} direction=both`
|
||||
case "dns":
|
||||
return `/resolve ${target} type=${opts.dnsType ?? "A"} server=1.1.1.1`
|
||||
return "DNS приложения: " + target + " · " + (opts.dnsType ?? "A") + " · резолвер хоста backend"
|
||||
case "route":
|
||||
return `/ip route lookup ip=${target}`
|
||||
case "mtu":
|
||||
@@ -1116,7 +1116,7 @@ function ProbesContent() {
|
||||
{/* RouterOS command preview */}
|
||||
<div className="flex items-center gap-2 rounded-lg bg-zinc-950 dark:bg-zinc-900 px-3 py-2 border border-zinc-800">
|
||||
<TerminalIcon className="size-3 text-zinc-500 shrink-0" />
|
||||
<span className="text-zinc-500 text-xs font-mono shrink-0">[{srcServer?.name}]</span>
|
||||
<span className="text-zinc-500 text-xs font-mono shrink-0">[{tool === "dns" ? (isLive ? "backend" : "демо") : srcServer?.name}]</span>
|
||||
<span className="text-emerald-400 text-xs font-mono shrink-0">$</span>
|
||||
<span className="text-zinc-300 text-xs font-mono truncate">{cmdPreview}</span>
|
||||
<button onClick={() => navigator.clipboard.writeText(cmdPreview)}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
| 7 | Серверы и мониторинг: детали, формы, URL-контекст, права | Завершён в границах локальной приёмки 2026-09-23; внешние ограничения ниже |
|
||||
| 8 | Трафик и статистика | Завершён 2026-09-28 в границах локальной приёмки; ограничения в блоке 8.13 |
|
||||
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
||||
| 10 | Маршрутизация, диагностика, карта и терминал | В работе: 10.1–10.61; оптимизатор, карта и терминал приняты локально; диагностика /probes в работе |
|
||||
| 10 | Маршрутизация, диагностика, карта и терминал | В работе: 10.1–10.62; оптимизатор, карта и терминал приняты локально; диагностика /probes в работе |
|
||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Не начат |
|
||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
||||
@@ -36,6 +36,14 @@
|
||||
|
||||
|
||||
|
||||
## Блок 10.62 — честный DNS preview и inline ошибка (2026-10-01)
|
||||
|
||||
- DNS в /probes подписан как DNS приложения; preview описывает параметры резолвера хоста backend. Не обещает /resolve MikroTik или server=1.1.1.1, которых текущий dnsLookupText не использует. Демонстрационный источник отмечен отдельно.
|
||||
- readProbeOutput отклоняет DNS ;; ERROR: внутри многострочного output, а не только error: в начале; inline отказ не превращается в done/live. Цель и тип остаются для ручного повтора. Backend DNS реализация/сетевые правила не изменены.
|
||||
- 4 unit, 5 профильных E2E (DNS/error/retry, malformed/legacy/timeout и GRE malformed), lint helper/tests, TypeScript в production frontend build прошли. Первый DNS тест использовал description как accessible name; исправлен на реальное имя DNS, повтор 5/5 чистый. Production build повторён отдельно после завершения E2E.
|
||||
- Остановка при 98% использования пятичасового окна; все блоки сохранены локальными коммитами без push. Следующий шаг 10.63: восстановление /probes server из URL без первого-device fallback, сохранение query/hash/back/reload, контракт/cleanup ros-src-address. Затем backend probes DI (невалидные id/аргументы, HTTP ошибки вместо legacy output, DNS/MTU/неизвестные метрики), доступность/адаптивные снимки и приёмка.
|
||||
- Этап 10 ещё в работе; /probes не принят. Остались 6 прежних set-state-in-effect ошибок и 2 предупреждения страницы, включая расписание (11Б). История локальная; HTTP моки не подтверждают реальные RouterOS/БД/DNS/портал.
|
||||
|
||||
## Блок 10.61 — достоверный GRE-каталог диагностики (2026-10-01)
|
||||
|
||||
- Переиспользован filtersGreResponseSchema; подтверждается форма ответа, уникальность и принадлежность serverId. Только enabled текущий сервер вызывает GET. Abort/cleanup запрещают поздние GRE после смены устройства/источника.
|
||||
|
||||
@@ -372,3 +372,12 @@ Shared контракт и backend observed исходные значения/д
|
||||
- Далее 10.62: проверить точность подписи/preview DNS (сейчас имитирует /resolve server=1.1.1.1, backend использует DNS приложения); затем URL выбор без fallback, ros-src-address контракт, backend probes ошибки/аргументы/метрики, адаптивные снимки и локальная приёмка. /probes пока не принят, этап 10 не завершён.
|
||||
- Остаток окна при сохранении около 4%; RouterOS/БД/внешний DNS не проверены. История локальная, отмена HTTP не подтверждает остановку устройства. Расписание/формы сбора остаются 11Б.
|
||||
|
||||
|
||||
## Блок 10.62 — честный DNS preview и inline ошибка (2026-10-01)
|
||||
|
||||
- DNS в /probes подписан как DNS приложения; preview описывает параметры резолвера хоста backend. Не обещает /resolve MikroTik или server=1.1.1.1, которых текущий dnsLookupText не использует. Демонстрационный источник отмечен отдельно.
|
||||
- readProbeOutput отклоняет DNS ;; ERROR: внутри многострочного output, а не только error: в начале; inline отказ не превращается в done/live. Цель и тип остаются для ручного повтора. Backend DNS реализация/сетевые правила не изменены.
|
||||
- 4 unit, 5 профильных E2E (DNS/error/retry, malformed/legacy/timeout и GRE malformed), lint helper/tests, TypeScript в production frontend build прошли. Первый DNS тест использовал description как accessible name; исправлен на реальное имя DNS, повтор 5/5 чистый. Production build повторён отдельно после завершения E2E.
|
||||
- Остановка при 98% использования пятичасового окна; все блоки сохранены локальными коммитами без push. Следующий шаг 10.63: восстановление /probes server из URL без первого-device fallback, сохранение query/hash/back/reload, контракт/cleanup ros-src-address. Затем backend probes DI (невалидные id/аргументы, HTTP ошибки вместо legacy output, DNS/MTU/неизвестные метрики), доступность/адаптивные снимки и приёмка.
|
||||
- Этап 10 ещё в работе; /probes не принят. Остались 6 прежних set-state-in-effect ошибок и 2 предупреждения страницы, включая расписание (11Б). История локальная; HTTP моки не подтверждают реальные RouterOS/БД/DNS/портал.
|
||||
|
||||
@@ -6,3 +6,5 @@ test("Диагностика отклоняет legacy error и oversized output
|
||||
|
||||
import { probeServersSchema } from "./probe-response"
|
||||
test("Каталог диагностики требует уникальные id и достоверный status/enabled",()=>{const row={id:1,name:"Тест",host:"192.0.2.1",type:"exit-node",enabled:true,status:null};assert.equal(probeServersSchema.safeParse([row]).success,true);for(const rows of [[row,row],[{...row,id:0}],[{...row,status:"active"}],[{...row,enabled:"yes"}]])assert.equal(probeServersSchema.safeParse(rows).success,false)})
|
||||
|
||||
test("DNS inline ERROR не подтверждает успех",()=>{assert.throws(()=>readProbeOutput({output:";; QUESTION: example.test A\n;; ERROR: ENOTFOUND"}),/ENOTFOUND/);assert.equal(readProbeOutput({output:";; QUESTION: example.test A\nexample.test IN A 192.0.2.1"}),";; QUESTION: example.test A\nexample.test IN A 192.0.2.1")})
|
||||
@@ -3,7 +3,7 @@ const response = z.object({ output: z.string().max(1024 * 1024) }).strict()
|
||||
export function readProbeOutput(raw: unknown): string {
|
||||
const result = response.safeParse(raw)
|
||||
if (!result.success) throw new Error("Некорректный ответ API: результат диагностики не подтверждён.")
|
||||
if (/^error:/i.test(result.data.output.trimStart())) throw new Error(result.data.output.trim())
|
||||
if (/^error:/i.test(result.data.output.trimStart()) || /^;;\s*ERROR:/im.test(result.data.output)) throw new Error(result.data.output.trim())
|
||||
return result.data.output
|
||||
}
|
||||
|
||||
|
||||
@@ -86,3 +86,9 @@ for(const invalid of ["disabled","no-address"])test("Диагностика: BW
|
||||
let posts=0;await page.route("**/api/filters/gre-tunnels?*",r=>r.fulfill({json:{tunnels:[{...tunnel,enabled:invalid!=="disabled",remoteAddress:invalid==="no-address"?"":tunnel.remoteAddress}]}}));await page.route("**/api/servers/1/probes/run",r=>{posts++;return r.fulfill({json:{output:"НЕ ДОЛЖНО ВЫПОЛНЯТЬСЯ"}})})
|
||||
await page.goto("/probes");await page.getByRole("button",{name:/BW-тест/}).click();await expect(page.getByRole("status").filter({hasText:"Выберите включённый GRE с адресом назначения"})).toBeVisible();await expect(run(page)).toBeDisabled();expect(posts).toBe(0)
|
||||
})
|
||||
|
||||
test("Диагностика: DNS приложения, inline ошибка и повтор",async({page})=>{
|
||||
let fail=true;await page.route("**/api/servers/1/probes/run",r=>r.fulfill({json:{output:fail?";; QUESTION: example.test A\n;; ERROR: ENOTFOUND":"example.test. IN A 192.0.2.1"}}))
|
||||
await page.goto("/probes");await page.getByRole("button",{name:"DNS",exact:true}).click();await page.getByRole("textbox",{name:"Домен"}).fill("example.test");await expect(page.getByText(/DNS приложения: example.test/)).toBeVisible();await expect(page.getByText(/server=1.1.1.1/)).toHaveCount(0)
|
||||
await run(page).click();await expect(page.getByText("ошибка",{exact:true})).toBeVisible();await expect(page.getByRole("textbox",{name:"Домен"})).toHaveValue("example.test");fail=false;await run(page).click();await expect(page.getByText("example.test. IN A 192.0.2.1",{exact:true})).toBeVisible()
|
||||
})
|
||||
Reference in new issue
Block a user