23 KiB
23 KiB
Backlog Calendar Run
Срез репозитория: 12 июля 2026. Это единый продуктовый и технический backlog, составленный по текущему коду, а не список абстрактных идей.
Текущая база
| Контур | Версия | Уже работает |
|---|---|---|
| Backend | 1.4.2 |
Express + PostgreSQL, миграции и seed, строгий CRUD личных стартов, cookie-сессии, CSRF, подтверждение email, Turnstile, SMTP, SSRF-защита обложек, /health, /ready, /meta, CI с PostgreSQL. |
| Frontend | 0.7.1 |
Защищённые маршруты и auth-экраны, нативные поля даты/старта, удобный ввод результатов, дашборд с корректным личным рекордом, версия FE/BE в футере. |
Правила версионирования и выпуска
- BE и FE выпускаются независимо: задачам без серверного контракта не нужен искусственный bump BE.
- Патч (
x.y.Z) — исправление без изменения публичного контракта; минор (x.Y.0) — новая обратносуместимая возможность; мажор — только при несовместимом API или модели доступа. - Любой релиз с изменением API включает: обновлённый
docs/backend-api-for-frontend.md, миграцию (если нужна), тесты контракта и проверку футера с фактическими версиями. - До миграций в production выполнить чек-лист из auth-rollout.md: backup, число стартов до/после миграции и проверка владельца legacy-данных.
План релизов
| Очередь | BE | FE | Цель | Условие выпуска |
|---|---|---|---|---|
| P0 | 1.4.2 |
0.7.1 |
Исправить риски текущего релиза и привести контракт в соответствие коду. | Нет невалидных записей, SSRF-защита, критические сценарии проверяются на PostgreSQL. |
| P1 | 1.5.0 |
0.8.0 |
Дать пользователю контроль над аккаунтом и данными, улучшить работу с историей стартов. | Управление сессиями и экспорт доступны, фильтры не ломают существующий список. |
| P2 | 1.6.0 |
0.9.0 |
Подготовка к старту: чек-листы, напоминания и календарная интеграция. | Напоминание отправляется не более одного раза, подписка/токен отзываемы. |
| P3 | 1.7.0 |
1.0.0 |
Сделать персональный календарь эксплуатационно готовым. | Восстановление из backup проверено, есть E2E критического пути и аудит доступности. |
2.0.0 не планируется заранее: его поводом может быть только подтверждённая потребность в совместном доступе, командах или публичных стартах — это изменит модель владельцев и доступа.
P0 — BE 1.4.2 / FE 0.7.1
Backend 1.4.2
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| BE-142-04 | P0 | Выполнить backend-часть Ponytail-аудита | Pipeline поднимает чистый PostgreSQL, применяет все миграции и выполняет минимум CRUD + auth, включая legacy-сценарий миграции 004. Самодельный SQL-эмулятор CALENDAR_RUN_MOCK_DB удалён; тесты работают с этой БД. cross-env удалён, если Windows не входит в список поддерживаемых сред разработки. |
| BE-142-01 | P0 | Актуализировать контракт API | Документированы id (UUID сервера) и slug (ключ пользователя), обязательная авторизация для /races, CSRF для мутаций, реальный ответ /health, все коды ошибок и формат Race. Старое описание не обещает несуществующий неавторизованный CRUD. |
| BE-142-02 | P0 | Ввести строгую валидацию создания и изменения старта | Одна схема на POST/PATCH: корректные дата и дистанция, enum статуса, допустимые форматы startTime и finishTime, URL, пределы длины текстов; неизвестные поля не игнорируются. Ошибка возвращает 400 validation_error с полем/причиной. |
| BE-142-03 | P0 | Защитить получение обложки от SSRF | Автозагрузка принимает только безопасные HTTP(S)-адреса, запрещает loopback/private/link-local адреса и опасные редиректы, ограничивает число редиректов, размер и время ответа. При невозможности получить обложку старт всё равно создаётся без неё. Есть тесты на блокировку. |
| BE-142-05 | P1 | Подтвердить достаточность индексов по фактическим запросам | Для списка владельца с фильтром года/месяца снять EXPLAIN ANALYZE; индекс (owner_user_id, race_date) добавлять только если он нужен по плану запроса или размеру данных. |
Frontend 0.7.1
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| FE-071-00 | P0 | Выполнить frontend-часть Ponytail-аудита | Удалены артефакты TypeScript/Vite из Git и добавлены в .gitignore, неиспользуемый getHealth и тип HealthResponse. Кастомные поля даты и времени старта заменены нативными input[type=date] и input[type=time]; каталог названий стартов с внешними картинками удалён, остаются coverImageUrl и локальные fallback-изображения. Создание и обновление старта используют один payload полей. |
| FE-071-01 | P0 | Исправить семантику личного рекорда | Карточка «Последний личный рекорд» показывается только для результата, который действительно улучшил прошлое лучшее время на сопоставимой дистанции; иначе карточка называется «Последний результат» либо скрывается. |
| FE-071-02 | P0 | Валидировать время до отправки | Форма не принимает 1:99, отрицательные и произвольные значения; пользователю показана ошибка у поля. Правило идентично серверному, но сервер остаётся источником истины. |
| FE-071-03 | P0 | Довести ошибки auth-форм до состояния UI | На регистрации, повторной отправке письма, запросе и сбросе пароля есть loading-состояние, обработка ошибки и защита от повторной отправки. Никаких необработанных Promise в браузере. |
| FE-071-04 | P1 | Корректно обработать истёкшую сессию и CSRF | После 401 пользователь возвращается на вход с сохранением целевого маршрута; после csrf_error интерфейс предлагает однократно обновить сессию и повторить действие, не теряя заполненную форму. |
| FE-071-05 | P0 | Завершать подтверждение email понятным экраном успеха | После успешной ссылки /verify-email?token=… заголовок явно сообщает, что email подтверждён; ввод email, инструкция и кнопка повторной отправки не показываются. Форма повторной отправки остаётся только для недействительной/отсутствующей ссылки. |
| FE-071-06 | P0 | Упростить ввод результата забега | Время вводится отдельными цифровыми полями «часы», «минуты», «секунды» без ручного ввода :. Часы — целое число ≥ 0, минуты и секунды — от 0 до 59; пустые минуты/секунды автоматически нормализуются в 0. Итоговое время не показывает незаполненные старшие части: без часов — MM:SS, без часов и минут — SS. Место вводится числами «место» и «всего участников» (второе необязательно), проверяется, что место ≥ 1 и не больше общего числа; в текущий finishPlace без изменения API сохраняется место/всего либо только место. Старые значения finishPlace не теряются до редактирования результата. |
P1 — BE 1.5.0 / FE 0.8.0
Backend 1.5.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| BE-150-01 | P0 | Управление паролем и сессиями | Авторизованный пользователь меняет пароль после проверки текущего; все другие сессии отзываются. Есть список активных сессий с отзывом выбранной и «выйти везде». Токены и пароли в ответах/логах не появляются. |
| BE-150-02 | P1 | Удаление аккаунта и данных | Явно подтверждённый запрос удаляет аккаунт, его сессии и личные старты транзакционно; повторный запрос идемпотентен. Перед реализацией согласовать необходимость периода восстановления. |
| BE-150-03 | P1 | Обратносуместимая фильтрация стартов | Добавить документированные фильтры status, диапазон дат и диапазон дистанции, не меняя текущий ответ-массив GET /races. Валидация параметров и покрытие индексами — по фактическому query plan. |
Frontend 0.8.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| FE-080-01 | P0 | Экран «Аккаунт» | Пользователь видит подтверждённый email, меняет пароль, видит/отзывает сессии и инициирует удаление аккаунта с явным предупреждением. |
| FE-080-02 | P1 | Поиск, фильтры и сортировка истории | Фильтры по статусу, периоду и дистанции синхронизированы с URL; поиск по названию работает без новой зависимости. Годы в селекторе строятся из данных, а не ограничены окном «-2/+4». |
| FE-080-03 | P1 | Выгрузка личных данных | Из текущей отфильтрованной выборки пользователь скачивает CSV и одноразовый .ics в браузере. Сервер не нужен: данные уже принадлежат пользователю и загружены в UI. |
| FE-080-04 | P1 | Сделать на дашборде список последних стартов | Блок «Сравнение стартов» переименован в «Последние старты» и показывает завершённые старты в обратном хронологическом порядке. На странице не более 5 строк, есть клиентская пагинация и фильтры по дистанции и году участия; значения фильтров строятся из данных пользователя, смена фильтра возвращает на первую страницу. Строка ведёт на страницу старта по клику и клавиатуре; состояние выбора анимируется CSS с учётом prefers-reduced-motion. API и серверная пагинация не нужны. |
| FE-080-05 | P1 | Связать рекорды по дистанциям со стартами | Карточка рекорда с найденным стартом ведёт на /races/:id мышью и с клавиатуры; она использует тот же dashboard-card--linked/dashboard-card__link-surface и анимацию, что «Ближайший старт», «Последний результат» и «Последний личный рекорд». Пустая карточка ссылкой не становится. |
| FE-080-06 | P1 | Сделать график прогресса информативным и интерактивным | SVG-график показывает подписанные оси «Дата» и «Время финиша» с читаемыми делениями; точки компактнее, имеют краткие адаптивные подписи без наложений. Наведение и фокус на точке показывают tooltip с датой, названием, временем и темпом; клик или Enter/Space открывает /races/:id. На узких экранах график и подписи не выходят за контейнер, а анимации учитывают prefers-reduced-motion. Используются текущие данные без новой библиотеки и API. |
P2 — BE 1.6.0 / FE 0.9.0
Backend 1.6.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| BE-160-01 | P1 | Чек-листы подготовки к старту | Новая сущность задач старта: текст, состояние, срок и порядок. Задачи принадлежат тому же пользователю, что и старт; каскадно удаляются вместе со стартом. |
| BE-160-02 | P1 | Надёжные email-напоминания | Настройки пользователя и очередь напоминаний хранятся в БД; отправка идемпотентна, ошибки ретраятся ограниченно и логируются без PII. Расписание запуска — внешний cron/worker, а не setInterval в каждом web-инстансе. |
| BE-160-03 | P2 | Подписка на персональный iCalendar | Выдать отдельный отзывной секретный URL только для read-only календаря пользователя. Нужны лимитирование, отсутствие email/заметок в ICS и отзыв/ротация токена. |
Frontend 0.9.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| FE-090-01 | P1 | Чек-лист в карточке старта | Пользователь добавляет, завершает и переставляет задачи подготовки; на дашборде видит ближайшие незавершённые пункты. Пустое состояние объясняет, что делать. |
| FE-090-02 | P1 | Настройки напоминаний | Опт-ин по email и выбор сроков до старта; понятно отображаются часовой пояс и факт отключения рассылки. |
| FE-090-03 | P2 | Управление iCalendar-подпиской | Экран показывает URL только по явному действию, даёт скопировать, отозвать и выпустить новый. Одноразовый экспорт из 0.8.0 остаётся доступным без подписки. |
P3 — BE 1.7.0 / FE 1.0.0
Backend 1.7.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| BE-170-01 | P0 | Эксплуатационный runbook | Описаны deploy, миграция, rollback, backup, restore, ротация секретов, SMTP/Turnstile и реакция на недоступную БД. Restore протестирован на копии production-данных. |
| BE-170-02 | P0 | Наблюдаемость без утечки данных | Структурированные логи с request ID, готовность БД, счётчики ошибок и alert на недоступность. Email, пароли, токены, cookie и текст заметок не логируются. |
| BE-170-03 | P1 | Машиночитаемый API-контракт | OpenAPI-спецификация синхронизирована с маршрутам и используется в CI для проверки ключевых ответов. Это предотвращает повторное расхождение документации и реализации. |
Frontend 1.0.0
| ID | Приоритет | Задача | Результат / критерий приёмки |
|---|---|---|---|
| FE-100-01 | P0 | Критический E2E-путь | Автоматически проверяются: регистрация/подтверждение (с тестовым адаптером), вход, создание старта, внесение результата, экспорт и выход. Тест запускается в CI против реального API и PostgreSQL. |
| FE-100-02 | P0 | Accessibility и мобильный аудит | Полный путь проходится клавиатурой; формы сообщают ошибки screen reader'у, focus не теряется после действий, контраст и touch-targets соответствуют WCAG 2.2 AA. Проверены малый экран и медленное соединение. |
| FE-100-03 | P1 | Полировка надёжности UI | Единые skeleton/empty/error состояния, retry для безопасных загрузок, защита от двойной отправки мутаций, проверка загрузки внешних обложек и сохранение версии FE/BE в футере. |
Зависимости и решения до начала P2
| Решение | Нужен ответ | Почему блокирует |
|---|---|---|
| Канал напоминаний | Только email или ещё push/мессенджер? | Определяет модель согласия, worker и стоимость эксплуатации. |
| Планировщик | Какой внешний cron/worker доступен в production? | Нельзя надёжно рассылать напоминания таймером в web-процессе. |
| Удаление аккаунта | Немедленное удаление или 30-дневное восстановление? | Меняет схему данных и обязанность хранить архив. |
| Политика данных | Для каких стран и пользователей запускается сервис? | До публичного запуска нужны политика конфиденциальности, срок хранения и процесс обращения пользователя. |
Явно не берём до подтверждения спроса
- общий доступ к календарям, клубы, тренеры и публичные профили;
- поиск и парсинг стартов из сайтов организаторов, кроме уже существующей необязательной обложки;
- нативные iOS/Android-приложения, платежи и социальная лента;
- серверную пагинацию: личный календарь пока мал, а изменение формы ответа
GET /racesбез измеренной необходимости только усложнит FE и API.
Рекомендуемый первый спринт
BE-142-04иFE-071-00: выполнить выводы Ponytail-аудита — удалить самодельную инфраструктуру и использовать возможности платформы.BE-142-01иBE-142-02: зафиксировать настоящий контракт и закрыть trust boundary.BE-142-03: убрать SSRF-риск до любого расширения автозагрузки обложек.FE-071-01–FE-071-06: устранить неверную аналитику и ошибки критических форм, включая понятное завершение подтверждения email и ввод результата.