интеграция всего вместе и всё что сломалось в ночь запуска

3 июля 2026 г. devloggo

Интеграция всего вместе

Шесть этапов, некоторые развёрнутые на месяцы друг от друга (auth, потом blog, потом фронтенд, админ-панель, загрузки, потом Caddy и compose связывающие всё вместе), и ни один из них никогда не работал полностью вместе в реальном стеке в одной сессии до самого запуска. Простая проверка: зарегистрировать настоящий аккаунт, закомментировать пост, лайкнуть, отрепортить, залогиниться как админ, написать пост с картинками и видео, модерировать комментарий, забанить юзера прямо из его комментария. Это должно быть просто как финальный шаг как только у каждого сервиса был свой набор тестов что проходит.

В основном прошло хорошо, что было почти разочаровывающе после такого долгого строительства когда половина ожидала взрывов. Регистрация работала, комментарии работали, счётчик лайков обновлялся без перезагрузки, жалоба на комментарий попадала в админ-панель ровно куда надо, пост с реальным фото и реальным видео прошёл путь загрузки без осечек. Забанить юзера прямо из его комментария, что-то что я раньше только тестировал в отдельных хендлерах, прошло конец-в-конец с первой попытки.

Cookie что тихо истекает сессия

Единственный реальный баг что валидация нашла был не внутри какого-то одного сервиса, а только когда всё вместе работало больше чем примерно 15 минут. Логин должен держать тебя залогиненным 30 дней, это весь смысл рефреш-токена, но логика молчаливого рефреша в hooks.server.ts пробует прочитать рефреш-куки при каждой обычной загрузке страницы:

if (!sessionFromAccessToken) {
	const refreshToken = event.cookies.get('refresh_token');
	if (refreshToken) {
		const response = await event.fetch('/auth/refresh', { method: 'POST' });
		...
	}
}

За исключением того что этот куки заскопирован на Path=/auth, специально, так что браузер никогда не отправляет его в запросах где он реально не нужен, держит его подальше от, скажем, логов доступа MinIO на неафилированный запрос медиа. Что также означает браузер не отправляет его сюда тоже, на обычный маршрут страницы, потому что этот код вообще не запускается под /auth. event.cookies.get('refresh_token') никогда ничего не найдёт, каждый раз, на любой обычной странице сайта.

Токен доступа сам по себе живёт только 15 минут. Так что практический результат, не замечаемый пока я не оставил таб открытым и не вернулся позже, это то что «оставаться залогиненным 30 дней» молча означает «оставаться залогиненным примерно 15 минут», потом совершенно нормально выглядящий редирект обратно на страницу логина без причины что кто-либо смог бы угадать без прочтения ровно этой цепочки правил scope для куки. Чинить это значит либо расширить scope куки либо делать рефреш-вызов откуда-то что находится внутри scope /auth, и я хочу посидеть с тем trade-off немного перед тем как закоммитить к чему-то, вместо чтобы расширять path потому что это меньший diff.

413-комментарий что никогда не произошёл на самом деле

Где-то в более ранней тестовой сессии я получил 413 постя совершенно обычный по длине комментарий, ничего странного в нём вообще, и это меня достаточно беспокоило что я записал это как реальный баг чтобы исправить. Вернулся позже специально чтобы приследовать это и не мог воспроизвести, ни при 100 байтах, ни при 9 килобайтах, ни прямо у лимита контента в 10000 символов. Собственный лимит JSON-декода блога для комментария это 64 килобайта, милю выше чего бы то ни было что реальный комментарий когда достигал, и ничто другое в стеке не должно быть никде близ этому тесно.

Лучшее предположение, и это действительно просто предположение: что бы то ни было что я того дня наткнулся практически наверняка было остатком от более ранего, намного более ограничивающего BODY_SIZE_LIMIT что я тогда постоянно использовал при тестировании, что-то вроде 3 мегабайт или даже плейсхолдер ноля, не реальный лимит что никогда не отправлялся. Написал регрессионный тест постя 9-килобайтный комментарий прямо через Caddy чтобы зафиксировать что это теперь работает корректно, и оставил тайну ровно так же нерешённой как и то, старый конфиг-привидение а не живой баг ещё скрывающийся где-то. Не всё страшное что ты наткнёшься при строительстве чего-то оказывается реальной штукой как только ты нормально идёшь разбираться.

Что всё ещё сидит на списке

Валидация прошла сама по себе. Что остаётся это не баг, это просто реальный релиз, и я специально беру его как собственное отдельное решение чем что-то что автоматически случается когда тесты становятся зелёными: мёржу dev в main, пушу в реальный git-ремоут и смотрю как CI-пайплайн действительно становится зелёным против реального контейнер-реджистри вместо моей машины, потом запускаю production compose-оверлей против реального домена или одноразового просто чтобы посмотреть как Caddy автоматическая TLS действительно пробует делать свою работу первый раз. Ничего из этого еще не произошло. Все шесть этапов построены и смёржены в dev, и каждый из тех оставшихся шагов явно мне запускать когда я готов, не скрипту для меня.

Что реально застряло со мной

Ничто отсюда не сложное в изолляции. Хэшировать пароль, проксировать запрос, класть лимит на размер тела, ничего из этого не трудное само по себе. Что реально заняло время это всё встречающееся всё остальное: решение path куки сделанное для собственных логирующих забот одного сервиса тихо ломающее совершенно неафилированное предположение фронтенда три сервиса в сторону, лимит размера на краю тихо закрывающий намного щедрый на два слоя дальше, клиентская проверка роля которая выглядит совершенно как безопасность пока ты не追кловишь что происходит как только ты соврёшь про неё.

Начал весь этот проект в основном потому что я не хотел просто юзать WordPress. Закончился тем что научился больше про то как части реального веб-сервиса на самом деле укладываются вместе, и ломаются вместе, чем любой учебник когда мне показывал. Django и CS50 учили меня синтаксис. Строение этого учило меня где вещи реально ломаются.

Всё ещё не смёржил это в main. Та часть всё ещё моя чтобы решить когда.

0 комментариев

Войдите , чтобы оставить комментарий.

Войти

Забыли пароль?

Нет аккаунта?