- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное вшивается через go:embed и раздаётся корневым маршрутом: разметка на неизвестном пути вне корней сервиса, отказ контракта внутри корня - перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов, а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь - шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка входят в гейт, а в Dockerfile появилась ступень приложения
14 KiB
14 KiB
1. Каталог приложения и его сборка
- 1.1 Завести
web/сpackage.json, файлом замка, настройкой Vite иindex.html; выходной каталог сборщика —web/embed/dist - 1.2 Команда сборки приложения гоняет проверку типов и сборку одним вызовом
(
vue-tscвходит в команду сборки, а не стоит отдельным шагом) - 1.3 В git лежит
web/embed/.gitkeep— этажом выше выходного каталога, чтобы очистка перед сборкой его не уносила; собранное и каталог зависимостей внесены в.gitignore - 1.4 Написан экран-заглушка: показывает имя вошедшего, на
401ведёт ко входу, на прочий отказ показывает строку о неудаче; одна таблица маршрутов черезcreateRouter, адреса обычные (createWebHistory) - 1.5 Обращение к API идёт через одну свою обёртку над
fetch; она же единственное место, где читается код ответа - 1.6 Заведён Biome: форматирование и статический анализ кода приложения. Не покрывает разметку однофайловых компонентов — сказать строкой и остановиться, замена инструмента решается человеком
- 1.7 Заведены юнит-тесты Vue: показ вошедшего, увод ко входу на
401, строка о неудаче на прочем отказе, учётная запись без имени
2. Раздача приложения из бинарника
- 2.1
web/embed.goвшиваетweb/embedцеликом (all:), включая метку пустого каталога - 2.2 Перечень корней сервиса заведён единой точкой в пакете транспорта:
/api,/app,/auth,/_и отдельные адреса/health,/metrics - 2.2.1 Наши маршруты вешаются из перечня: проба здоровья, метрики, группа приложения, группа входа. Корни библиотеки остаются её литералами и держатся своим тестом; второе перечисление адресов наблюдения в уровне журнала снято
- 2.3 Принадлежность корню — точное совпадение либо префикс вместе с косой чертой; оба условия проверяются
- 2.4 Обработчик приложения зарегистрирован маршрутом
/(тем самым занимает место библиотечного отказа) и принимает файловую систему параметром - 2.5 Порядок обработчика: корень сервиса или адрес наблюдения — отказ формой
библиотеки; метод не
GETи неHEAD—405; несовпавший путь под каталогом ресурсов —404; совпавший файл — файл; прочее — разметка - 2.6 Заголовки кэширования назначены по каталогу: ресурсы сборщика — год и «неизменяемо», всё прочее, включая разметку, — спрашивать заново
- 2.7 Отсутствие собранного приложения даёт
503с честной страницей и строку в журнале при подъёме, а не404молча; проба здоровья остаётся зелёной - 2.8 При подъёме в журнал уходит отпечаток вшитой разметки
- 2.9 Строка журнала корневого маршрута несёт исход и длину пути, но не сам путь
- 2.10 Разметка и ресурсы отдаются без сессии; адреса данных под корнем приложения сессии по-прежнему требуют
3. Сборка и выкладка
- 3.1 Шаг сборки приложения заведён в
Taskfile.ymlи стоит первым в наборе проверок, до сборки Go; рядом — шаги Biome и юнит-тестов приложения - 3.1.1 Проверки приложения гоняются тем же контейнером, что и сборка; второго окружения не заводится
- 3.2 Шаг зовёт установщик и сборщик контейнером, а не из
PATH: имя образа берётся изDockerfile, контейнер ходит под вызвавшим пользователем, кэш установщика уведён в каталог под/tmp - 3.3 Зависимости ставятся из файла замка командой, которая его не правит; ту
же команду зовёт ступень
Dockerfile - 3.4 Недостающий docker и отказ сети или реестра — отказ окружения, код 3; красная сборка приложения — код 1
- 3.5 В
Dockerfileдобавлена ступень сборки приложения перед сборкой бинарника; в рабочий слой Node не попадает - 3.6 Вес выяснен, время замера снято с задачи решением владельца 2026-08-15: финальный образ от ступени приложения не растёт вовсе (Node в него не копируется), вшитое добавляет к бинарнику 86 072 байта. Время сборки остаётся неизвестным сознательно
- 3.7 Последствие ADR-2026-08-11-spa-on-vue поправлено: требованием к машине разработчика становится docker, а не Node
4. Проверки
- 4.1 Тест: неизвестный путь вне корней отдаёт разметку с кодом
200 - 4.2 Тест: неизвестный путь под каждым корнем сервиса разметки не отдаёт
- 4.3 Тест: голый корень без косой черты разметки не отдаёт, а посторонний путь, начинающийся именем корня без косой черты, — отдаёт
- 4.4 Тест: неизвестный путь под корнем приложения отвечает
404с сессией и401без неё - 4.5 Тест: несуществующий файл под каталогом ресурсов отвечает
404 - 4.6 Тест: проба здоровья и метрики приложением не подменяются
- 4.7 Тест: чужой метод отвечает
405, аHEADна разметку —200 - 4.8 Тест: разметка и ресурс отдаются без сессии, а список записей — нет
- 4.9 Тест: у разметки стоит требование спрашивать заново, у ресурса из каталога сборщика — год и «неизменяемо»
- 4.10 Тест: пустая сборка даёт
503, а не404, и проба здоровья при этом зелёная - 4.11 Юнит-тесты приложения краснеют на сломанном экране: намеренная поломка показа вошедшего роняет набор проверок
- 4.12
task gateзелёный целиком
5. Документы
- 5.1
docs/conventions/web-ui.md— снята оговорка «кода приложения ещё нет»; дописаны правила: отпечаток в именах ресурсов, несовпавший ресурс отвечает404, зависимости ставятся из файла замка - 5.2
docs/architecture.md— компонент приложения, внешняя зависимость сборки, новая capability и перечень корней в единых точках - 5.3
docs/database.md— срок хранения ресурсов в настройках с числовым значением - 5.4
CLAUDE.md— команды и требования к машине разработчика, состав гейта вместе с проверками приложения, второй сетезависимый шаг названный поимённо - 5.5
docs/review.md— заведён род узла «раздача собранной статики и шаг сборки приложения» в типовых узлах - 5.6
docs/conventions/go-linters.mdлибо соседняя запись — чем проверяется код приложения: Biome и юнит-тесты, что каждый ловит и где настроен
Критерии приёмки
Дословно из записи задачи spa-skeleton:
- Статика собирается одной командой и вшивается в бинарник: запущенный бинарник отдаёт приложение без каталога рядом. Оракул — сборка, запуск бинарника из пустого каталога, запрос корня возвращает разметку приложения.
- Образ собирается на чистой машине без предустановленного окружения фронтенда.
Оракул —
task imageбез локально установленных зависимостей фронтенда. - Приложение показывает, кто вошёл, и обращается к API живого сервера. Оракул — тест либо ручная проверка на запущенном сервере с сессией.
- Шаг сборки статики входит в
task gateи краснеет при ошибке сборки. Оракул — намеренно сломанный исходник роняетtask gate. - Обновление страницы на любом маршруте приложения открывает тот же экран, а
адрес внутри корня сервиса в приложение не проваливается. Оракул — тест на три
запроса: неизвестный путь вне корней отдаёт разметку, неизвестный путь внутри
/api/и внутри/app/отдаёт ошибку контракта.
Рубрика ревью дизайна, тем же списком:
- Перечень корней живёт одной точкой, и добавление корня не требует правки в двух местах.
- Запрос несуществующего файла под каталогом ресурсов отвечает
404, а не разметкой с200. - После прогона сборки приложения
git statusчист поweb/. - После
task gategit statusчист по файлу замка зависимостей. - Отсутствие сборки громкое: код ответа, страница и строка журнала; «сборки нет» отличается от «файла нет».
- Долгий неотзываемый срок ставится только файлу из каталога ресурсов сборщика.
- Разметка и ресурсы отдаются анонимно, данные под корнем приложения — нет; по анонимному ответу нельзя узнать, вошёл ли кто-то.
- В бинарник попадает только собранное: ни карт исходников, ни каталога зависимостей, ни файлов вне выходного каталога.
- Отпечаток вшитой сборки виден строкой журнала при подъёме.
- Шаг сборки следует словарю кодов: недостающий инструмент и отказ сети — 3, красная сборка — 1; молчаливого пропуска нет.
- Ступень сборки не протекает в рабочий слой образа, а версия сборочного окружения объявлена одним местом.
- Файловая система строится один раз, а не на каждый запрос; поведение на
HEADопределено. - Запрос на путь длиной в килобайт не добавляет значения ни метке метрики, ни строке журнала.