diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7a354ac --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,160 @@ +# Changelog + +Все заметки о выпусках Calendar Run. Backend и frontend имеют независимые версии. + +Дата означает дату попадания изменения в `main`; она не заменяет дату production-деплоя. +Планируемые версии не вносятся сюда до выпуска — они находятся в [docs/product-backlog.md](docs/product-backlog.md). + +## В работе + +Версии приложения пока не изменены. В этой ветке добавлены документация backlog и этот changelog. + +## Backend + +### [1.4.1] — 2026-05-24 + +#### Changed + +- Усилена безопасность аутентификации, сессий и production security profile. +- Добавлены проверки, предотвращающие использование dev-bypass Turnstile в production. + +### [1.4.0] — 2026-05-24 + +#### Added + +- Регистрация, вход/выход, подтверждение email, повторная отправка письма и сброс пароля. +- Cookie-сессии, CSRF-защита, rate limit и Turnstile. +- Владение стартами: пользователь видит и изменяет только свои записи. +- SMTP-уведомления и очистка истёкших auth-сущностей. + +### [1.3.0] — 2026-04-27 + +#### Added + +- Необязательное автоматическое извлечение обложки старта по сайту организатора. +- Поле `coverImageUrl` в модели, миграции и API. + +### [1.2.2] — 2026-04-12 + +#### Changed + +- Patch-релиз: удалено временное диагностическое логирование запросов. + +### [1.2.1] — 2026-04-10 + +#### Added + +- Временная диагностика `Host`/`Origin` и статуса ответа для расследования проблем подключения. + +### [1.2.0] — 2026-04-08 + +#### Changed + +- Унифицирован контракт API на префиксе `/api` для Express, Vite и nginx. + +### [1.1.0] — 2026-04-08 + +#### Added + +- `GET /api/meta` для отображения серверной версии в интерфейсе. + +### [1.0.1] — 2026-04-08 + +#### Fixed + +- Совместимость маршрутов API с прокси и клиентским префиксом `/api`. + +### [1.0.0] — 2026-04-01 + +#### Added + +- Базовый Express API для стартов, PostgreSQL-миграции, seed из CSV и health/readiness endpoints. + +## Frontend + +### [0.7.0] — 2026-05-24 + +#### Added + +- Экраны регистрации, входа, подтверждения email и сброса пароля. +- Защищённые маршруты, состояние сессии и выход из аккаунта. + +### [0.6.2] — 2026-05-24 + +#### Added + +- Табы «Будущие» и «Прошедшие» для компактного мобильного списка стартов. + +### [0.6.1] — 2026-04-27 + +#### Changed + +- Обложка ближайшего старта используется фоном hero-блока дашборда. + +### [0.6.0] — 2026-04-27 + +#### Added + +- Поддержка пользовательской и автоматически полученной обложки старта в списках и карточке. + +### [0.5.2] — 2026-04-27 + +#### Added + +- Favicon сервиса. + +### [0.5.1] — 2026-04-27 + +#### Fixed + +- Корректное определение версии frontend из `package.json`. + +### [0.5.0] — 2026-04-22 + +#### Changed + +- Редизайн дашборда и визуального представления стартов. + +### [0.4.0] — 2026-04-13 + +#### Added + +- Форма старта с селектами времени, просмотр календаря годом и месяцем, страница конкретного дня. +- Предзаполнение даты при добавлении из календаря. + +### [0.3.1] — 2026-04-12 + +#### Fixed + +- Исправления UX списка и календаря, автоматическая установка статуса «Пробежал» при валидном времени финиша. + +### [0.3.0] — 2026-04-08 + +#### Changed + +- Единый клиентский путь `/api` и устойчивый разбор ответов сервера. + +### [0.2.0] — 2026-04-08 + +#### Added + +- Русский UI и футер с версиями frontend и backend. + +### [0.1.1] — 2026-04-08 + +#### Fixed + +- Получение версии приложения из `package.json` вместо Vite define. + +### [0.1.0] — 2026-04-06 + +#### Added + +- React/Vite приложение, типизированный API-клиент, список и карточка стартов, формы CRUD, дашборд и базовая аналитика результатов. + +## Как добавлять запись при выпуске + +1. Изменить версию соответствующего пакета по SemVer и обновить lockfile. +2. Добавить только фактически выпущенные изменения в раздел `Backend` или `Frontend`. +3. Если изменился API — обновить [docs/backend-api-for-frontend.md](docs/backend-api-for-frontend.md) в том же PR. +4. Перед выпуском проверить, что футер показывает новую версию frontend и фактическую версию backend. diff --git a/docs/product-backlog.md b/docs/product-backlog.md new file mode 100644 index 0000000..95dfd7b --- /dev/null +++ b/docs/product-backlog.md @@ -0,0 +1,134 @@ +# Backlog Calendar Run + +**Срез репозитория: 11 июля 2026.** Это единый продуктовый и технический backlog, составленный по текущему коду, а не список абстрактных идей. + +## Текущая база + +| Контур | Версия | Уже работает | +|---|---:|---| +| Backend | `1.4.1` | Express + PostgreSQL, миграции и seed, CRUD личных стартов, фильтр по году/месяцу, cookie-сессии, CSRF, подтверждение email, сброс пароля, Turnstile, SMTP, `/health`, `/ready`, `/meta`. | +| Frontend | `0.7.0` | Защищённые маршруты и auth-экраны, список и календарь стартов, карточка и форма старта, результаты, дашборд, график по дистанции, версия FE/BE в футере. | + +### Что не является готовым продуктом + +- API-справочник описывает старый публичный CRUD: в коде маршруты стартов требуют авторизацию, сервер генерирует UUID `id`, а клиент передаёт `slug`. +- Поля старта принимаются без строгой серверной схемы; в частности, форматы времени, календарная дата, URL, длины строк и лишние поля не валидируются в одной точке. +- Загрузка обложки ходит по URL организатора на сервере; перед следующим production-релизом необходима защита от SSRF. +- В дашборде карточка «Последний личный рекорд» показывает последний завершённый старт с временем, а не факт нового PR. +- CI собирает FE и тестирует BE только на mock-БД; миграции и запросы не проходят проверку на настоящем PostgreSQL. + +## Правила версионирования и выпуска + +1. BE и FE выпускаются независимо: задачам без серверного контракта не нужен искусственный bump BE. +2. Патч (`x.y.Z`) — исправление без изменения публичного контракта; минор (`x.Y.0`) — новая обратносуместимая возможность; мажор — только при несовместимом API или модели доступа. +3. Любой релиз с изменением API включает: обновлённый `docs/backend-api-for-frontend.md`, миграцию (если нужна), тесты контракта и проверку футера с фактическими версиями. +4. До миграций в production выполнить чек-лист из [auth-rollout.md](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-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-04 | P0 | Проверять миграции на реальном PostgreSQL в CI | Pipeline поднимает чистый PostgreSQL, применяет все миграции, выполняет минимум CRUD + auth. Отдельно проверяется сценарий legacy-таблицы, который обрабатывает миграция `004`. | +| BE-142-05 | P1 | Подтвердить достаточность индексов по фактическим запросам | Для списка владельца с фильтром года/месяца снять `EXPLAIN ANALYZE`; индекс `(owner_user_id, race_date)` добавлять только если он нужен по плану запроса или размеру данных. | + +### Frontend 0.7.1 + +| ID | Приоритет | Задача | Результат / критерий приёмки | +|---|---|---|---| +| 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` интерфейс предлагает однократно обновить сессию и повторить действие, не теряя заполненную форму. | + +## 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. | + +## 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. + +## Рекомендуемый первый спринт + +1. `BE-142-01` и `BE-142-02`: зафиксировать настоящий контракт и закрыть trust boundary. +2. `BE-142-03`: убрать SSRF-риск до любого расширения автозагрузки обложек. +3. `FE-071-01`–`FE-071-03`: устранить неверную аналитику и ошибки критических форм. +4. `BE-142-04`: перенести эту базу в CI, затем выпускать `1.4.2` / `0.7.1`.