Принятые решения¶
Что решено не делать и почему. Страница существует, чтобы очевидная идея не приходила второй раз: каждая из них уже приходила, была проверена и отклонена — с причиной.
Формат записи: решение — почему — что было бы иначе.
Архитектура и API¶
Поля vetted_status, vetted_user, vetted_datetime из API не удаляются. Парк
станций не обновится целиком никогда, а часть клиентов читает именно их (инвариант 7,
см. Контекст для ИИ-агента). Они помечены deprecated в схеме; дата снятия появится, когда
телеметрия покажет, что их больше никто не запрашивает.
Иначе: станция, которая не просила ничего менять, получает ответ без знакомых полей.
DEFAULT_PAGINATION_CLASS в DRF не вводится. Ответ /api/satellites/ — голый
массив, и клиент станции разбирает его как массив; общий пагинатор сменил бы тело на
{count, results} для всех эндпоинтов разом. Вместо него — тест, который требует у
каждого ViewSet либо pagination_class, либо явный лимит с причиной.
Иначе: одна строка в настройках ломает контракт v1.
/health/ не заводится. Есть /ready/ (база и применённые миграции), его читает
healthcheck контейнера. Полной проверки зависимостей никто не потребляет: мониторинга,
который бы её опрашивал, у проекта нет.
Иначе: эндпоинт, который никто не читает, и который надо поддерживать.
Ретраи Celery стоят на обёртках в network/celery.py, а не на самих задачах. Beat
зовёт обёртку, а не shared_task напрямую, и retry() на вызванной напрямую задаче не
срабатывает.
Иначе: настройка, которая выглядит рабочей и молчит.
Зависимости¶
django-shortuuidfield остаётся. Снять его через SeparateDatabaseAndState нельзя:
миграции 0001, 0008 и 0017 импортируют shortuuidfield.fields на уровне модуля, а
Django загружает весь граф миграций при каждом migrate — пакет обязан остаться
установленным, даже если поле в состоянии модели заменить на CharField. Единственная
альтернатива — squash, а он запрещён (см. ниже).
Иначе: migrate падает на импорте у любого, кто поднимает базу с нуля.
satellite.js остаётся на версии 6. В 7 пакет стал ESM-only, без UMD-сборки, а
скрипты портала подключаются классическими тегами без сборщика.
Иначе: страницы с расчётом пролётов перестают работать до появления бандлера.
Squash миграций base не делается. Стенды и станции обновляются вразнобой; squash
ломает любую базу, которая остановилась внутри схлопнутого отрезка. Правило и шаблон
батчей — в Как вносить изменения.
Иначе: часть парка не мигрирует и восстанавливается только из дампа.
Фронтенд¶
Каталоги спутников и передатчиков не переводятся на serverSide: true. Данные и так
приходят ajax-фидом, <tbody> в шаблоне пустой, и приёмка «страница каталога меньше
200 КБ» выполнена. Остаток выигрыша — 80–120 КБ gzip — стоил бы сортировки на сервере,
счётчиков, переписывания восьми тестов и переноса группировки передатчиков.
Иначе: неделя работы ради страницы, которая и так грузится.
Сборка статики включается только двумя переменными сразу: COMPRESS_ENABLED=True
и COMPRESS_OFFLINE=True. Одна первая ломается на следующем же деплое: collectstatic --clear стирает CACHE/, а кэш компрессора в Redis всё ещё называет стёртые файлы.
Иначе: страницы без стилей и 404 на бандлах после выкатки.
Имена кнопок — из Bootstrap, а не из палитры. btn-primary, btn-info,
btn-secondary, btn-success, btn-danger и их outline-пары плюс btn-sm/btn-lg;
btn-orange, btn-blue, btn-small и прочие цветовые имена запрещены тестом. Роль
действия определяет вариант: удаление сохранённых данных — всегда btn-danger.
Иначе: одно и то же действие красное на одной странице и синее на другой.
Сегменты полосы успешности станции не доводятся до 24px (WCAG 2.5.8). Ширина сегмента — это и есть данные: 8,7 % неуспешных нельзя нарисовать шириной в 24px, не соврав. Здесь работает исключение «Essential». Иначе: полоса показывает не то соотношение, которое описывает.
Две навигации DataTables с одинаковой подписью (landmark-unique) остаются.
Пагинация рисуется дважды, сверху и снизу таблицы, потому что так задана строка dom;
крючка для подписи nav библиотека не даёт.
Иначе: постобработка разметки после каждой перерисовки таблицы.
Безопасность¶
unsafe-eval снят из script-src полностью. Он пришёл с mapbox-gl 0.40 в 2017 году;
в 2.15 ни eval, ни new Function не вызываются — проверено по всему static/lib.
Аварийный клапан на случай неожиданностей — CSP_STYLE_SRC="'self','unsafe-inline'"
в .env сервера; на страницах с плеером он не действует, там nonce.
Иначе: политика разрешает выполнение строк на всех страницах ради библиотеки, которой
это не нужно.
/api/jobs/ не ограничен троттлингом. Ключ авторизованного клиента — владелец, один
на все его станции, а интервал опроса у старого клиента настраивается: общий лимит на
владельца отрезал бы станции, которые ничего не нарушают. Лимиты стоят только на запись —
приём SIDS и статус станции.
Иначе: станция, которая не обновлялась, начинает получать 429 на штатной частоте.