first commit

This commit is contained in:
2026-07-18 23:32:11 +03:00
commit d401e9009c
9 changed files with 496 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
---
description: Frontend and UI rules for Mixmaker
globs: frontend/**/*.{ts,tsx,css}
alwaysApply: false
---
# Frontend And UI
- Build mobile-first screens first, then scale up to desktop.
- Use React, TypeScript, Vite, TanStack Router, TanStack Query, Tailwind CSS, shadcn/ui, and accessible primitives.
- Organize code by product capabilities; keep reusable primitives in `shared`.
- Keep the live match screen focused: show the current step, active team, allowed actions, score, and concise validation feedback.
- Use orange and blue team accents on a dark graphite base, with accessible contrast and non-color status indicators.
- Make touch targets large and keep the primary action reachable on phones.
- Treat server state as authoritative; use optimistic updates only when rollback is clear and safe.
- Keep API access behind typed client functions generated from or aligned with OpenAPI.
- Prefer subtle motion that communicates state changes; respect reduced-motion preferences.
- Do not encode map pools, hero catalogs, or draft rules directly in UI components.

View File

@@ -0,0 +1,18 @@
---
description: Go Clean Architecture rules for Mixmaker backend
globs: backend/**/*.go
alwaysApply: false
---
# Go Backend Architecture
- Keep dependency direction strict: `domain` has no outward dependencies; `application` depends on domain and ports; adapters depend on application contracts; `cmd/api` performs wiring.
- `backend/internal/domain` must not import HTTP routers, SQL drivers, configuration, loggers, or framework code.
- `backend/internal/application` must depend on domain types and consumer-owned ports, never concrete adapters.
- Put request and response DTOs in HTTP adapters, not in domain entities.
- Prefer explicit domain errors and small interfaces owned by their consumers.
- Keep SQL, migrations, and PostgreSQL-specific types inside output adapters.
- Make transactions explicit at the application boundary for multi-step state changes.
- Keep balancing and draft validation deterministic and free of infrastructure dependencies.
- Pass clocks, random sources, and ID generators through ports when behavior must be testable.
- Add focused unit tests whenever domain behavior or a use case changes.

View File

@@ -0,0 +1,21 @@
---
description: Core Mixmaker product context
alwaysApply: true
---
# Mixmaker Context
- Mixmaker is a web app for organizing Overwatch scrims for communities of 20+ players.
- Read the relevant files in `memory_bank/` before meaningful product or architecture changes.
- The core flows are Discord auth, player profiles, event calendar and RSVP, role-based team balancing, coin toss, map and hero bans, Bo3 results, and tournament brackets.
- Players sign in through Discord and may edit only their own Tank, Damage, and Support ratings.
- Scheduled events support Going, Maybe, and NotGoing participation statuses.
- Admins may manage events and change RSVP for another player; store the acting account for every administrative override.
- The balancer creates full 5v5 teams with 1 Tank, 2 Damage, and 2 Support; unmatched players remain in reserve.
- An Admin assigns one captain from each team's current roster; captain permission is scoped to that team.
- Keep the UI polished, responsive, mobile-first, and styled as a dark esports control panel.
- Business rules and draft state are enforced by the backend; the frontend must not be the source of truth.
- Preserve draft, result corrections, and match history as auditable actions instead of silently rewriting completed steps.
- Production deploys to Coolify as one Docker Compose stack with PostgreSQL; do not declare custom Compose networks unless explicitly required.
- Do not commit production secrets, publish PostgreSQL ports, or omit persistent storage, healthchecks, and backup documentation.
- After meaningful product, architecture, API, data-model, deployment, or workflow changes, update the relevant files in `memory_bank/` in the same task.

View File

@@ -0,0 +1,16 @@
---
description: Testing and quality expectations for Mixmaker
alwaysApply: true
---
# Testing And Quality
- Keep changes scoped and aligned with `memory_bank/`.
- After substantive edits, run the narrowest useful checks available.
- Backend domain rules and use cases must be unit-testable without PostgreSQL.
- Add focused tests for balancing constraints, draft transitions, role limits, and repeated-ban rules.
- Repository and migration behavior should use integration tests once the database layer exists.
- Critical frontend flows should receive component or end-to-end coverage as the UI stabilizes.
- Test concurrent or repeated draft commands for idempotency and version conflicts.
- Do not add broad abstractions until repeated behavior proves they are needed.
- Prefer explicit domain invariants over compatibility with unfinished code.

View File

@@ -0,0 +1,68 @@
# Активный контекст
## Текущее состояние
Проект начинается с нуля. Сформированы исходный контекст, предметная модель, архитектурное направление и предварительная дорожная карта.
## Подтверждённые требования
- сообщество из 20+ игроков;
- backend на Go с Clean Architecture;
- современный frontend на фреймворке;
- Discord OAuth и самостоятельное заполнение игроком рейтингов Tank, Damage и Support;
- календарь запланированных миксов со статусами Going / Maybe / NotGoing;
- административная панель управления событиями, игроками и регистрациями;
- возможность Admin отметить участие за другого игрока;
- автоматическая балансировка полных команд 1 Tank / 2 Damage / 2 Support;
- резерв для участников, не вошедших в полные команды;
- назначение Admin капитана каждой сформированной команды;
- жеребьёвка на сайте;
- последовательные баны карт и героев;
- серии Bo3;
- фиксация исхода каждой карты, счёта серии и итогового победителя;
- турнирная сетка;
- production-развёртывание единым Docker Compose resource в Coolify;
- PostgreSQL в том же Compose-стеке с persistent volume;
- мобильный и десктопный интерфейс.
## Исходный регламент
- основной Bo3: Control → Hybrid/Escort → Control;
- альтернативный Bo3: Control → Push → Escort;
- первый бан карт определяется жеребьёвкой;
- карты банятся по очереди до одной оставшейся;
- тайбрейкер Control не повторяет уже сыгранную Control-карту;
- перед каждой картой команды делают по два бана героев в порядке A → B → A → B;
- два бана одной команды должны относиться к разным ролям;
- команда не повторяет собственный бан героя в пределах серии;
- повторить героя, которого ранее банил соперник, разрешено;
- первой героев банит команда, проигравшая начальный жребий.
## Предлагаемый стек
- backend: Go, PostgreSQL, REST, WebSocket/SSE;
- frontend: React, TypeScript, Vite, TanStack Query/Router, Tailwind CSS, shadcn/ui;
- контракт: OpenAPI;
- локальная инфраструктура: Docker Compose;
- production: Coolify + Docker Compose без пользовательских сетей.
## Принятые решения для MVP
- игрок входит через Discord;
- игрок самостоятельно указывает рейтинг 1100 для каждой роли;
- балансировщик автоматически назначает роли и собирает команды 1/2/2;
- участники сверх полного состава остаются в резерве;
- первая турнирная сетка — single elimination;
- игрок отмечается на микс через календарь;
- Admin может изменить RSVP за игрока, а действие сохраняет автора;
- капитан назначается отдельно для каждой команды и должен входить в её состав;
- исход карты задаётся как победа одной из команд или ничья;
- сервер автоматически пересчитывает счёт Bo3 и победителя серии;
- frontend, API и PostgreSQL разворачиваются одним Compose-стеком;
- Coolify управляет сетью и публичным маршрутом, PostgreSQL хранит данные в named volume.
## Ближайший следующий шаг
Создать full-stack каркас и первый вертикальный сценарий: Discord-вход → профиль с рейтингами → календарь → отметка участия → админка регистраций → список участников события.
Открытое продуктовое решение, которое не блокирует каркас: как выбирается Hybrid/Escort для второй карты — заранее организатором, случайно или отдельным действием команд.

126
memory_bank/architecture.md Normal file
View File

@@ -0,0 +1,126 @@
# Архитектура
## Общий подход
Backend строится на Go по принципам Clean Architecture. Зависимости направлены внутрь: инфраструктура и доставка зависят от сценариев приложения, сценарии — от доменной модели.
## Backend
Предлагаемая структура:
```text
backend/
cmd/api/ точка входа HTTP-сервера
internal/
domain/ сущности, value objects, правила, доменные ошибки
application/
usecase/ сценарии приложения
port/ интерфейсы репозиториев, часов, генераторов, событий
adapter/
in/http/ handlers, middleware, DTO
out/postgres/ реализации репозиториев
out/realtime/ доставка событий клиентам
platform/ конфигурация, логирование, подключение инфраструктуры
migrations/
```
Технологии первой версии:
- Go;
- PostgreSQL;
- REST API для команд и запросов;
- WebSocket или Server-Sent Events для синхронизации драфта и сетки;
- Discord OAuth 2.0 для входа;
- серверная cookie-сессия с HttpOnly, Secure и SameSite;
- структурированное логирование;
- OpenAPI как контракт frontend/backend.
## Frontend
Предлагаемый стек:
- React + TypeScript;
- Vite;
- TanStack Router и TanStack Query;
- Tailwind CSS;
- shadcn/ui как основа компонентов;
- Framer Motion для умеренных анимаций;
- React Hook Form + Zod;
- dnd-kit для ручной корректировки составов;
- библиотека турнирной сетки только после проверки мобильного UX; при необходимости — собственный SVG-компонент.
Структура frontend группируется по продуктовым возможностям:
```text
frontend/src/
app/
pages/
widgets/
features/
entities/
shared/
```
## Границы модулей
- Players: профили, рейтинги и предпочтения ролей;
- Auth: Discord OAuth, сессии и роли доступа;
- Admin: управление игроками, регистрациями, событиями и капитанами;
- Events: календарь, регистрации участников и состояние игрового вечера;
- Balancing: генерация и оценка составов;
- Draft: жеребьёвка, карты и герои;
- Matches: карты, счёт и завершение серии;
- Tournaments: сетка и продвижение команд;
- Realtime: подписка клиентов на изменения.
## Ключевые решения
### Балансировщик
В MVP — детерминированный оптимизатор с весовой функцией качества и ограниченным поиском вариантов. Алгоритм располагается в доменном слое и не зависит от БД. Позже его можно заменить на constraint solver без изменения API сценария.
### Драфт как конечный автомат
Жеребьёвка и баны моделируются состояниями и допустимыми командами. Сервер является источником истины и отклоняет действие, если оно нарушает порядок или регламент.
### История действий
Все действия драфта сохраняются как неизменяемые записи аудита. Текущее состояние можно хранить в обычных таблицах; полноценный Event Sourcing для MVP не требуется.
### Авторизация
Вход выполняется через Discord OAuth 2.0. После callback backend создаёт собственную серверную сессию и безопасную HttpOnly-cookie; OAuth-токен Discord не передаётся frontend. Discord ID является внешним идентификатором аккаунта.
Глобальные роли MVP — Admin и Player; Captain назначается Admin для конкретной команды, а read-only spectator-доступ доступен авторизованным игрокам. Игрок редактирует только собственный профиль, рейтинги и RSVP. Admin управляет событиями, может изменить RSVP за игрока, подтверждает участников, назначает капитанов и фиксирует результаты. Начальные Admin задаются через `ADMIN_DISCORD_IDS`, чтобы роль нельзя было получить через публичный интерфейс.
### Календарь и участие
События хранятся в UTC и отображаются в локальном часовом поясе пользователя. REST API отдаёт будущие события и статусы Going/Maybe/NotGoing. Изменения регистрации публикуются через realtime-канал, чтобы Admin сразу видел актуальный список. При изменении статуса за другого игрока API сохраняет actor ID и источник `admin`, а UI показывает, кем сделана отметка.
### Капитаны
Капитан — назначение внутри конкретной команды, а не глобальная роль аккаунта. Admin может назначить или заменить капитана только участником этой команды. Только текущий капитан выполняет командные действия драфта; Admin имеет аварийное право выполнить действие с обязательной записью в аудит.
### Результаты матчей
Исход каждой карты фиксируется отдельной командой use case. Домен серии пересчитывает счёт и победителя, а турнир продвигает победившую команду только после завершения серии. Исправление результата не удаляет историю первоначального действия.
### Развёртывание в Coolify
Production разворачивается из репозитория как один Docker Compose resource: frontend/reverse proxy, Go API, миграции и PostgreSQL с named volume. Coolify сам создаёт сеть стека и подключает свой proxy, поэтому в Compose не объявляются `networks`, `container_name` и вручную заданные Traefik labels.
Frontend-контейнер обслуживает SPA и проксирует `/api` и SSE к сервису `api`, поэтому приложению достаточно одного публичного домена. PostgreSQL и API не публикуют host-порты; сервисы обращаются друг к другу по Compose-именам. Секреты и production-переменные задаются в Coolify UI, а в репозитории хранится только `.env.example`.
Для всех долгоживущих сервисов задаются healthcheck и корректный graceful shutdown. Данные PostgreSQL сохраняются в named volume; для production обязательно настраивается регулярный backup тома/БД вне сервера.
## Нефункциональные требования
- идемпотентность команд, которые могут повториться из-за сети;
- optimistic UI только там, где возможен безопасный откат;
- серверная валидация всех правил;
- миграции схемы БД;
- unit-тесты доменных правил и балансировки;
- интеграционные тесты репозиториев и API;
- production Docker Compose совместим с Coolify без пользовательских сетей;
- PostgreSQL имеет persistent volume, healthcheck и внешние резервные копии;
- адаптивность от мобильного экрана до большого дисплея.

109
memory_bank/domain_model.md Normal file
View File

@@ -0,0 +1,109 @@
# Предметная модель
## Основные сущности
### Player
Игрок связан с Discord-аккаунтом и имеет отображаемое имя и собственный рейтинг для каждой роли: Tank, Damage, Support. Игрок редактирует свои рейтинги самостоятельно; организатор может видеть время последнего изменения. Дополнительно можно хранить предпочитаемые роли, BattleTag и историю участия.
### Account
Учётная запись Discord с глобальной ролью Player или Admin. Admin назначается только другим Admin либо через начальный список Discord ID в конфигурации развёртывания. Изменение роли записывается в аудит.
### Event
Запланированный игровой вечер: название, дата и время, часовой пояс, описание, дедлайн регистрации, формат команд, регламент, турнир и текущее состояние.
### EventRegistration
Отметка игрока в календаре со статусом Going, Maybe или NotGoing. Игрок меняет собственный статус, а Admin может изменить его за игрока. Регистрация хранит автора и время последнего изменения, чтобы интерфейс явно показывал административное действие. В момент закрытия регистрации Admin подтверждает список участников; только Going-игроки по умолчанию попадают в балансировку.
### Team
Состав игроков, назначенный Admin капитан, цвет/название и рассчитанные показатели силы. Капитан должен входить в текущий состав команды; переназначение фиксируется в аудите.
- общий средний рейтинг;
- средний рейтинг по каждой роли;
- распределение игроков по ролям;
- оценка доверия к балансу.
### Ruleset
Версионируемый набор правил серии:
- формат Bo3;
- последовательность режимов;
- пулы карт по режимам;
- порядок банов карт и героев;
- лимиты банов по ролям;
- ограничения повторных банов в серии.
### Series
Матч двух команд в рамках события или турнирной сетки. Хранит жеребьёвку, порядок первого бана, карты, счёт, статус и итогового победителя.
### MapResult
Исход отдельной карты: победившая команда либо ничья, счёт/заметка при необходимости и автор фиксации. Результат карты обновляет счёт серии; завершённый Bo3 определяет победителя автоматически. Организатор может исправить ошибочный результат, при этом изменение попадает в аудит.
### MapDraft
Пошаговое исключение карт из пула выбранного режима до одной оставшейся карты. Для тайбрейкера ранее сыгранная Control-карта исключается.
### HeroDraft
Четыре бана перед картой в порядке A → B → A → B. Каждая команда выбирает двух героев разных ролей. Героя, которого команда уже банила в этой серии, она не может забанить снова; героя, ранее забаненного соперником, банить можно.
### Tournament
Формат и состояние турнирной сетки, участники, раунды, пары и продвижение победителей.
## Правила текущего регламента
### Карты
Базовая последовательность Bo3:
1. Control;
2. Hybrid или Escort;
3. Control-тайбрейкер, если счёт 1:1.
Альтернативная последовательность: Control → Push → Escort.
Первый бан определяется жеребьёвкой. Затем команды по очереди исключают карты внутри пула режима, пока не останется одна.
### Герои
- перед каждой картой банятся четыре героя;
- порядок: A → B → A → B;
- каждая команда банит двух героев разных ролей;
- один и тот же герой может быть забанен командой только один раз за серию;
- бан героя соперником не расходует собственную возможность забанить этого героя позже.
## Балансировка
Целевая функция должна учитывать не только близость общего среднего рейтинга команд, но и:
- разницу средних рейтингов по ролям;
- корректность состава 1 Tank / 2 Damage / 2 Support;
- предпочтения игроков;
- запрет повторения составов при регулярных играх;
- заранее заданные пары или ограничения «вместе/раздельно».
Алгоритм должен возвращать не только составы, но и понятную оценку качества и причины оставшегося дисбаланса.
## Доменные события
- EventCreated;
- PlayerJoinedEvent;
- EventRegistrationChanged;
- TeamsBalanced;
- TeamCaptainAssigned;
- CoinTossResolved;
- MapBanned;
- MapSelected;
- HeroBanned;
- MapResultRecorded;
- MapResultCorrected;
- SeriesCompleted;
- BracketAdvanced.

View File

@@ -0,0 +1,44 @@
# Mixmaker — контекст проекта
## Зачем нужен продукт
Mixmaker — веб-приложение для организации любительских Overwatch-миксов на 20+ участников. Оно должно сокращать время на ручную подготовку матчей, делать команды сопоставимыми по силе и хранить весь ход игровой встречи в одном месте.
## Основные задачи
- регистрация участников и оценка их силы по ролям;
- вход через Discord и самостоятельное редактирование игроком своих рейтингов;
- календарь запланированных миксов и отметки об участии;
- административная панель для управления событиями, участниками и их отметками;
- автоматическая балансировка нескольких команд;
- назначение капитанов сформированных команд;
- проведение жеребьёвки;
- пошаговые баны карт и героев с автоматической проверкой правил;
- ведение серии Bo3, фиксация исходов карт и итогового победителя;
- отображение турнирной сетки и итогов игрового вечера;
- управление процессом в реальном времени с телефонов и компьютеров.
## Целевая аудитория
- организатор игрового вечера;
- капитаны команд;
- игроки;
- зрители.
## Первая версия
Первая версия ориентирована на закрытое сообщество друзей. Игрок входит через Discord, заполняет рейтинги по ролям и отмечает участие в запланированном миксе. Admin при необходимости отмечает участие за игрока, подтверждает состав, формирует сбалансированные команды, назначает капитанов, запускает турнир, проводит серии по заданному регламенту и фиксирует результаты.
## Ограничения и принципы
- бизнес-правила не должны зависеть от UI, базы данных или конкретного транспорта;
- регламент карт и банов должен быть настраиваемым, а не зашитым во фронтенде;
- критичные действия матча должны быть воспроизводимыми и иметь историю;
- интерфейс должен хорошо работать на мобильных устройствах;
- production должен разворачиваться через Coolify из Docker Compose без пользовательских сетей;
- PostgreSQL входит в Compose-стек и использует постоянный том и внешние резервные копии;
- интеграция с Battle.net не обязательна для MVP.
## Визуальное направление
Современный тёмный интерфейс в стиле киберспортивной контрольной панели: глубокий графитовый фон, яркие оранжевые и голубые акценты команд, крупные счётчики, компактные карточки и заметная визуализация текущего шага драфта.

76
memory_bank/roadmap.md Normal file
View File

@@ -0,0 +1,76 @@
# Дорожная карта
## Этап 0 — уточнение продукта
- согласовать способ оценки игроков и рейтинг по ролям;
- определить размеры команд и число одновременно создаваемых команд;
- выбрать первый формат турнирной сетки;
- подтвердить редактируемые части регламента;
- подготовить wireframes ключевых экранов.
## Этап 1 — фундамент
- создать Go backend и React frontend;
- подготовить отдельный Compose для локальной разработки;
- подготовить production Compose для Coolify без пользовательских сетей;
- добавить multi-stage Dockerfile для frontend и backend;
- настроить PostgreSQL в составе production-стека, persistent volume и миграции;
- добавить healthcheck и graceful shutdown сервисов;
- описать переменные Coolify и настройку внешних резервных копий БД;
- реализовать Discord OAuth и серверные сессии;
- добавить роли Admin/Player и безопасное начальное назначение Admin по Discord ID;
- зафиксировать OpenAPI-контракт;
- реализовать базовую навигацию и дизайн-систему;
- добавить автоматические проверки и тесты.
## Этап 2 — игроки и балансировка
- самостоятельное заполнение профиля и рейтингов по ролям;
- календарь будущих миксов;
- статусы участия Going / Maybe / NotGoing;
- админку для событий, игроков и регистраций;
- изменение Admin статуса участия за другого игрока с аудитом;
- подтверждение списка участников Admin;
- назначение доступных ролей;
- генерация нескольких вариантов команд;
- оценка качества баланса;
- ручная перестановка игроков с пересчётом показателей.
- назначение и переназначение капитана каждой команды.
## Этап 3 — проведение серии
- создание пары команд;
- серверная жеребьёвка с прозрачным результатом;
- драфт карт по выбранному ruleset;
- бан героев с проверкой ролей и истории серии;
- фиксация победителя или ничьей каждой карты;
- автоматический счёт серии и определение победителя;
- исправление ошибочного результата с аудитом;
- синхронизация клиентов в реальном времени.
## Этап 4 — турнир
- single-elimination сетка;
- автоматическое создание следующих пар;
- экран трансляции/зрителя;
- история результатов игрового вечера.
## Этап 5 — удобство сообщества
- пресеты регламентов;
- статистика игроков и команд;
- повторная жеребьёвка с защитой от одинаковых составов;
- Discord-уведомления;
- импорт данных и резервное копирование.
## Идеи после MVP
- режим «капитанский драфт» игроков;
- ограничения «хочу вместе» и «не ставить вместе»;
- сезонный рейтинг сообщества;
- прогноз вероятности победы и подсветка причин дисбаланса;
- голосование зрителей за карту или MVP;
- публичный overlay для стрима;
- PWA с push-уведомлением о начале матча;
- синхронизация событий с внешним календарём через iCalendar;
- библиотека версионируемых регламентов под разные патчи.