приложение собрано каркасом и вшито в бинарник
- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное вшивается через go:embed и раздаётся корневым маршрутом: разметка на неизвестном пути вне корней сервиса, отказ контракта внутри корня - перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов, а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь - шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка входят в гейт, а в Dockerfile появилась ступень приложения
This commit is contained in:
@@ -22,6 +22,7 @@ SpeechKit, Yandex Object Storage и `ffmpeg`. Мерить нужно то, чт
|
||||
|
||||
| Дата | Запись | О чём |
|
||||
| --- | --- | --- |
|
||||
| 2026-08-15 | [Раздача приложения: что делают за нас библиотека и сборщик](webapp-serving.md) | Раскодированный путь у маршрутизатора, второй журнал у PocketBase, нулевое время у вшитого файла, зависание установщика без сети |
|
||||
| 2026-08-13 | [Разбор TOML: какое семейство отказов несёт значения из файла](toml-decode-errors.md) | Значения только в `ParseError.Message`, врущее поле `Line`, отказ значением в BurntSushi/toml v1.5.0 |
|
||||
| 2026-08-12 | [PocketBase: умолчания, которые ломают штатный сценарий](pocketbase-defaults.md) | Потолок файла 5 МиБ, тело 32 МиБ, таймаут чтения, суффикс имени, хук правки |
|
||||
| 2026-08-11 | [gRPC-клиент SpeechKit: когда закрытие вообще может отказать](grpc-client-close.md) | Ленивое соединение и два исхода `Close` в grpc v1.74.2 |
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Раздача приложения: что делают за нас библиотека и сборщик
|
||||
|
||||
Отвечает на вопросы, возникшие по ходу задачи `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` весило того же порядка.
|
||||
|
||||
## Чего эта записка не узнала
|
||||
|
||||
- **Во что ступень сборки обходится образу по времени.** Прогон до конца не
|
||||
доходит: из контейнеров этой машины нет исходящей сети при рабочем разрешении
|
||||
имён. По весу вопрос закрыт иначе — ступень в рабочий слой не копируется, и
|
||||
финальный образ от неё не растёт вовсе.
|
||||
- **Как поведёт себя раздача под настоящим потоком.** Ограничителя частоты на
|
||||
корневом маршруте нет, а профиля нагрузки у проекта нет тоже.
|
||||
- **Что делает настоящий браузер** с этими заголовками: проверено кодами ответов
|
||||
и заголовками, а не браузером.
|
||||
Reference in New Issue
Block a user