fix(rc5): harden frontend runtime and diagnostics

This commit is contained in:
2026-09-08 05:22:06 +05:00
parent 079094591b
commit b7e478f014
26 changed files with 707 additions and 169 deletions
+11
View File
@@ -85,6 +85,12 @@
сборку — vue-i18n молча отдаёт сам ключ, и оператор видит `error.code.min_length`
вместо фразы.
Совпадения ключей недостаточно: строки компилируются как Vue I18n message
format только при переводе. Специальные символы (`@`, `$`, `{}`, `|`) нельзя
вставлять в текст как произвольные данные. Динамический набор знаков передаётся
через named interpolation, а builder переводит каждую leaf-строку RU/EN и
считает ошибкой как исключение, так и compiler diagnostics в `console.error`.
**Состояние сессии** сообщается кодами `unauthorized`, `session_expired`,
`token_invalid`, `account_disabled` при `code = 50401`. Панель по ним
показывает диалог и возвращает на форму входа.
@@ -132,6 +138,11 @@
продукт: в таблицу пиров ведут две двери, и они не имеют права требовать
разного.
Frontend-зеркало границ, regex и человекочитаемого набора находится в одном
модуле `constants/peer.ts` и сверяется с Go-контрактом тестом. Пунктуация
`!@#$%^&*()_+-=` попадает в подсказку как значение `{punctuation}`, а не как
часть синтаксиса message format.
---
## 4. Таблицы журнала
+15 -8
View File
@@ -136,7 +136,7 @@ Bun обновляется отдельно от остальных: оркес
полного прохода `bun test → tsc → compile → приёмка на Debian`, а не строки в
общем патче.
### Проверка типов frontend — обязательный шаг релиза
### Runtime-компиляция локализации и типы frontend — обязательные шаги релиза
```text
pnpm run typecheck → vue-tsc --noEmit → ОБЯЗАН пройти
@@ -144,9 +144,13 @@ pnpm run build:prod → vite build
pnpm run verify → typecheck, затем build
```
`bundle_ui()` запускает `typecheck` **до** сборки bundle: собирать production
bundle из кода, который не проходит проверку типов, незачем. Порядок и сам факт
наличия шага проверяются приёмкой.
После `pnpm install --frozen-lockfile` функция `bundle_ui()` сначала запускает
`bun test test/i18n-runtime.test.ts`, затем `typecheck` и только потом сборку
bundle. Словари заданы TypeScript-объектами, поэтому Vite и `vue-tsc` не
компилируют message format и не ловят специальные символы `@`, `$`, `{}`, `|`.
Runtime-gate использует реальный `vue-i18n` из lock-графа, обходит все строки
RU/EN и перехватывает также `console.error`: один лишь `try/catch` недостаточен,
поскольку компилятор может сообщить об ошибке без исключения из `t()`.
До v1 этой гарантии не было. `build:prod` означал `vite build && vue-tsc
--noEmit`, но `vue-tsc` был версии `0.35.0` (2022 год) и шаблоны Vue
@@ -222,7 +226,9 @@ bundle из кода, который не проходит проверку ти
Уязвимость обычно приходит не из прямой зависимости, а из чужого поддерева, и
поднимать ради неё весь frontend-граф не нужно. Такие случаи закрываются
списком `pnpm.overrides` в `apps/frontend/package.json`:
списком `overrides` в `apps/frontend/pnpm-workspace.yaml`. Настройки pnpm не
хранятся в поле `pnpm` package manifest: современные версии его игнорируют, и
правило осталось бы только случайно замороженным в старом lock-файле.
| override | зачем |
| --- | --- |
@@ -253,14 +259,15 @@ bundle из кода, который не проходит проверку ти
| Шаг сборки | Что запускается |
| --- | --- |
| `run_orchestrator_tests` | `bun x tsc --noEmit`, `bun test` |
| `bundle_ui` | `pnpm run typecheck` (`vue-tsc --noEmit`) до сборки bundle |
| `run_frontend_tests` | dependency-free контракты панели |
| `bundle_ui` | frozen install → runtime-компиляция всех RU/EN-сообщений → `pnpm run typecheck` → production bundle |
| `run_admin_tests` | `go vet ./...`, `go test ./...` |
Готовый пакет объявляет об этом полем `tests_gate=true` в
`metadata/package.env` — так же, как `dependency_security_gate` и
`hysteria_compat_gate`. Значение у поля ровно одно, потому что не бывает
пакета, собранного с пропущенными тестами: обе функции прогона выставляют свой
флаг **после** успешного завершения, а `write_metadata` отказывается писать
пакета, собранного с пропущенными тестами: функции прогона выставляют свои
флаги **после** успешного завершения, а `write_metadata` отказывается писать
метаданные, если хотя бы один из них не выставлен. То есть поле остаётся
утверждением о результате, а не переключателем.
+2
View File
@@ -346,6 +346,8 @@ hy2xs-orchestrator redact-config --config /etc/hysteria/config.yaml --out /root/
редакцию, а результат остаётся валидным YAML;
- в env-файлах секрет вырезается и из URL-значения, даже если имя ключа
несекретное — например, `HY2_AUTH_URL` в `post-install.env`.
- `HY2XS_FORCE_PASSWORD_CHANGE=true|false` сохраняется как диагностический
boolean; это точное исключение, остальные ключи с `PASSWORD` редактируются.
Та же редакция применяется к diagnostics-бандлу
(`hy2xs-orchestrator diagnostics collect`), который собирается автоматически при
+7
View File
@@ -144,6 +144,13 @@ anycast. Отсутствие A-записи фатально при любом
- `HY2XS_FORCE_PASSWORD_CHANGE` в production baseline установлен в `false` (forced UX-flow пока не реализован);
- после первичного seed перезапуски `hy2xs-admin` не должны переопределять пароль admin и `con_pass`.
В санитизированной диагностике корректное значение `true`/`false` у
`HY2XS_FORCE_PASSWORD_CHANGE` остаётся видимым: это boolean состояния, а не
пароль. Исключение применяется только к точному имени ключа и допустимому
boolean; неизвестное значение скрывается fail-closed.
`HY2XS_ADMIN_INITIAL_PASSWORD`, `HY2XS_ADMIN_CON_PASS`, пароль obfs,
traffic-stats secret и похожие ключи продолжают редактироваться.
### Учётные данные администратора проверяются при разборе окружения
`HY2XS_ADMIN_USER` и `HY2XS_ADMIN_INITIAL_PASSWORD` — это значения, которые
+10 -4
View File
@@ -18,9 +18,12 @@ cd apps && go vet ./... && go test ./...
# Проверка типов и сборка frontend
cd apps/frontend && pnpm install --frozen-lockfile && pnpm run verify
# Контракты панели: спрайт иконок, словари локализации, коды ошибок, атрибуция
# Dependency-free контракты панели
bun test tools/test/frontend-sprite.test.ts tools/test/frontend-contract.test.ts
# Runtime-компиляция всех RU/EN-сообщений реальным vue-i18n
cd apps/frontend && bun test test/i18n-runtime.test.ts
# Сверка среды разработки с versions.env (ничего не меняет)
./tools/dev/doctor.sh
@@ -36,9 +39,12 @@ HYSTERIA_BIN=/usr/local/bin/hysteria ./tools/test/e2e-hysteria.sh
## Почему контракты панели проверяет Bun, а не vitest
Проверяемые модули (`SvgIcon/symbol.ts`, `constants/branding.ts`, словари
локализации) намеренно чистые: ни Vite, ни DOM в них нет, поэтому их можно
выполнить вне браузера уже закреплённым в `versions.env` Bun.
Dependency-free модули (`SvgIcon/symbol.ts`, frontend constants и словари
локализации) намеренно чистые: ни Vite, ни DOM в них нет. Отдельный
`i18n-runtime.test.ts` запускается после `pnpm install`, потому что обязан
использовать именно `vue-i18n` из frozen lock-графа. Он обходит каждую строку
обоих словарей и считает отказом и исключение, и compiler diagnostics в
`console.error`.
Vitest с jsdom не вычисляет `currentColor` и визуальной корректности всё равно
не доказал бы, зато привёл бы в граф `pnpm audit` — а его порог считается по
+8 -3
View File
@@ -918,7 +918,8 @@ build tooling исполняется на build-машине и порождае
| Шаг сборки | Что запускается |
| --- | --- |
| `run_orchestrator_tests` | `bun x tsc --noEmit`, `bun test` |
| `bundle_ui` | `pnpm run typecheck` до сборки bundle |
| `run_frontend_tests` | dependency-free контракты панели |
| `bundle_ui` | после frozen install: `bun test test/i18n-runtime.test.ts`, затем `pnpm run typecheck` и bundle |
| `run_admin_tests` | `go vet ./...`, `go test ./...` |
Приёмка проверяет:
@@ -926,9 +927,13 @@ build tooling исполняется на build-машине и порождае
- отключающей тесты переменной нет ни в одном модуле сборки, ни в README/docs
(место для истории — `CHANGELOG.md`);
- `metadata/package.env` содержит `tests_gate=true`;
- общий frontend-флаг выставляется только после ранних контрактов и
runtime-компиляции всех сообщений RU/EN;
- runtime-gate стоит между frozen install и typecheck/build, использует
`vue-i18n` из lock-графа и считает ошибкой compiler diagnostics;
- утверждение о прогоне выставляется **после** самого прогона, а не до него;
- `write_metadata` отказывается писать метаданные, если хотя бы один из двух
прогонов не подтверждён.
- `write_metadata` отказывается писать метаданные, если хотя бы одна из
обязательных групп проверок не подтверждена.
## A12. Приёмка проверяет код, а не упоминания