Files
transcriber/docs/research/webapp-serving.md
T
av 663021f712 приложение собрано каркасом и вшито в бинарник
- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное
  вшивается через go:embed и раздаётся корневым маршрутом: разметка на
  неизвестном пути вне корней сервиса, отказ контракта внутри корня
- перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов,
  а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь
- шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка
  входят в гейт, а в Dockerfile появилась ступень приложения
2026-08-15 18:51:05 +03:00

70 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Раздача приложения: что делают за нас библиотека и сборщик
Отвечает на вопросы, возникшие по ходу задачи `spa-skeleton`, — какие свойства
раздачи приходят не из нашего кода, а из стандартной библиотеки, из PocketBase и
из инструментов приложения. Наблюдения понадобились потому, что ревью нашло три
места, где записанное намерение расходилось с тем, что на деле делает чужой код.
## Как снималось
Прогонами на живом бинарнике (свой конфиг с выдуманными ключами, свой каталог
данных вне репозитория) и чтением исходников зависимостей, зафиксированных в
`go.mod`: `github.com/pocketbase/pocketbase` версии **v0.39.10** и стандартной
библиотеки Go. Отдельно — прогоны установщика и сборщика приложения в контейнере.
Числа ниже сняты 2026-08-15 на этом прогоне, а не взяты из чужих записок.
## Что выяснилось
- **Маршрутизатор стандартной библиотеки сравнивает сегменты пути после
раскодирования.** Поэтому `/%5f/` попадает туда же, куда `/_/`, а `/%68ealth`
— туда же, куда `/health`: ответы совпадают байт в байт. Исходная форма
остаётся в `URL.RawPath`, и решение, принимаемое **вне** сервиса по сырому пути
— правилом обратного прокси, — такой формы не видит. Цена записана в
[security.md](../security.md), «Периметр»: барьер перед панелью владельца
обходится подменой одного знака.
- **PocketBase пишет каждый запрос в свою таблицу журнала**, а не только в вывод
контейнера: слой `activityLogger` подключён ко всем маршрутам и кладёт путь
целиком
(до 3000 знаков), адрес отправителя, источник перехода и клиент. Умолчания —
хранить пять суток, адрес записывать. Готовая раздача статики
(`apis.Static`) первой же строкой ставит признак «успех не записывать»; своя
раздача этого признака не наследует, и его надо ставить руками. Отсюда правило
в [review.md](../review.md): журналов **два**, и говорить надо про оба.
- **Вшитая файловая система не несёт времени правки.** `embed.FS` отдаёт нулевое
время у любого файла, поэтому отдача файла стандартной библиотекой никогда не
отвечает подтверждением «не менялось» — всякая проверка приходит полным телом.
Заголовок, обещающий дешёвую проверку, без метки ответа обещает то, чего код не
делает.
- **Сборщик приложения чистит выходной каталог перед каждой сборкой.** Метка,
положенная рядом с собранным ради того, чтобы каталог существовал в git,
уезжает первым же прогоном. Живёт она только этажом выше выходного каталога.
- **Проверка типов однофайловых компонентов не работает с седьмой линией
TypeScript.** `vue-tsc` версии 3.3.10 зовёт у компилятора точку входа, которой
новый компилятор не отдаёт, и сборка падает на этапе проверки типов. Рабочая
пара — пятая линия TypeScript.
- **Установщик пакетов без сети не отказывает, а виснет.** Он уходит в повторы с
нарастающей паузой **на каждом пакете**, и набор проверок вместо кода отказа
просто стоит. Пределы у отдельных обращений положения не спасают: их сумма и
даёт зависание. Помогает короткое обращение-проба перед установкой.
- **Вес приложения в бинарнике равен весу собранного.** Замер: две сборки, с
собранным приложением и с пустым каталогом, разница — 86 072 байта, то есть
ровно `index.html` плюс единственный ресурс. Собранное приложение на четыре
экрана в разведке `spa-framework` весило того же порядка.
## Чего эта записка не узнала
- **Во что ступень сборки обходится образу по времени.** Прогон до конца не
доходит: из контейнеров этой машины нет исходящей сети при рабочем разрешении
имён. По весу вопрос закрыт иначе — ступень в рабочий слой не копируется, и
финальный образ от неё не растёт вовсе.
- **Как поведёт себя раздача под настоящим потоком.** Ограничителя частоты на
корневом маршруте нет, а профиля нагрузки у проекта нет тоже.
- **Что делает настоящий браузер** с этими заголовками: проверено кодами ответов
и заголовками, а не браузером.