INGAMEREADME для разработчиков

InGame: прототип административной системы

Статус: локальный технический прототип. Это не production-система и не готовый личный кабинет клиента.

Репозиторий содержит рабочую основу будущей административной системы InGame: нормализованную PostgreSQL-базу, односторонний импорт исторических данных из WordPress, экраны админки и часть серверной логики бронирований. Документ фиксирует фактическое состояние кода на текущий момент, чтобы следующий разработчик понимал границу между уже реализованным и запланированным.

Что уже реализовано

Основа и данные

  • Next.js 16, React 19, TypeScript и PostgreSQL 17 в Docker Compose.
  • Нормализованная схема данных для клиентов, заказов, бронирований, ресурсов, расписания, оплат, транзакций, товаров, дополнительных услуг, промокодов, сертификатов, отчётов и аудита.
  • Суммы хранятся целыми центами; важные связи защищены внешними ключами, ограничениями статусов и индексами.
  • Запрет пересечений бронирований и блокировок одного ресурса обеспечивается на уровне PostgreSQL: exclusion constraints и триггер берут блокировку ресурса внутри транзакции.
  • Односторонний локальный импорт WordPress-данных с SHA-256-чексуммами, идемпотентностью, карантином некорректных записей и сверкой результатов.
  • Сохраняются legacy ID, WPML-алиасы и исходные снимки данных, поэтому исторические строки разных языков связываются с одной канонической сущностью без дублирования каталога.

Работа администратора

  • Расписание: календарь по дням, временная шкала квестов и лаунжей, создание и изменение заказа, блокировка слота и дня квеста, фильтрация по utm_source, итоги по способам оплаты.
  • Заказы и бронирования: список, поиск, фильтры по статусу/дате/UTM; в одном заказе может быть несколько бронирований. При сохранении проверяются конфликты ресурсов, тарифы, количество игроков, скидки и промокоды.
  • Клиенты: поиск, сортировка, создание, редактирование, удаление, история заказов и транзакционное объединение дублей.
  • Квесты: список и редактор; тарифы, цены для числа игроков, недельное расписание, связанные услуги, локализации et / ru / en, активность.
  • Продукты и дополнительные услуги: создание и редактирование, категории, атрибуты, варианты, цены, локализации и медиа из импортированного хранилища.
  • Промокоды: CRUD, фиксированная или процентная скидка, период действия, лимит, использованное количество и вычисляемый статус.
  • Магазин и сертификаты: просмотр и редактирование заказов магазина, импортированные сертификаты и файлы, журнал действий, тестовая отправка сертификата и тестовый возврат платежа.
  • Статистика: доходы по квестам, услугам, товарам и способам оплаты; предупреждения о расхождении суммы заказа и оплаты; фильтры и CSV-экспорт с защитой от CSV-injection.
  • Публичное API прототипа: получение доступности и создание одного простого бронирования с серверной валидацией и защитой от устаревшего/занятого слота.
flowchart LR
  WP["WordPress-архив\nлокальный источник"] -->|"односторонний импорт"| DB[("PostgreSQL")]
  DB --> ADMIN["Админка Next.js"]
  DB --> API["Публичное API\nдоступность и бронь"]
  STATIC["Статический прототип сайта"] --> SITE["/site"]

Что пока не реализовано

Следующие пункты не должны восприниматься как готовый функционал:

  • нет авторизации, персональных учётных записей, ролей и проверки прав доступа;
  • нет полноценного публичного сайта, регистрации или личного кабинета клиента; маршрут /site только отдаёт внешний статический HTML-файл;
  • нет боевой интеграции с amoCRM, Montonio, email или Telegram; в Docker включены только симуляции email/Montonio, а amoCRM не вызывается;
  • нет очереди/outbox, обработчика webhooks, повторных попыток, журналов доставки и production-секретов;
  • нет реального проведения платежей, выдачи возвратов или отправки сертификатов: эти действия помечены как симуляция;
  • нет production-развёртывания, резервного копирования и мониторинга;
  • импорт — это снимок исторических данных в одну сторону. Удаления, выполненные позднее в WordPress, намеренно не реплицируются в локальную базу.

Структура проекта

Путь Назначение
app/admin/ Страницы админки: расписание, заказы, клиенты, квесты, каталог, сертификаты, промокоды и статистика.
app/admin/actions/ Server Actions, которые принимают формы, проверяют входные данные и обновляют нужные страницы.
app/api/public/ Публичные прототипные API: availability и bookings.
components/admin/ Переиспользуемые компоненты навигации, таблиц, календаря и редактора заказа.
lib/server/ Серверная бизнес-логика и SQL-запросы. Новые правила брони/денег следует добавлять сюда, а не в React-компоненты.
lib/import/normalize.mjs Чистые функции нормализации WordPress-данных; покрываются отдельными тестами.
db/001_init.sql Начальная схема PostgreSQL, ограничения и индексы.
scripts/import-legacy.mjs Односторонний импорт локального WordPress-снимка.
scripts/reconcile.mjs Сверка итогов последнего импорта.
tests/ Тесты схемы, импорта, бронирований, каталога, клиентов, статистики и shell админки.
docker-compose.yml Локальные контейнеры PostgreSQL и Next.js.

Локальный запуск

Требования

  • Docker Desktop / Docker Compose;
  • Node.js 24 (для запуска импортера и тестов на хосте);
  • локальная копия WordPress-архива и HTML-прототипа сайта, если требуется импорт и маршрут /site.

Подготовка источников для импорта

Для работы админки без импорта достаточно PostgreSQL. Для db:import и db:reconcile дополнительно обязателен соседний локальный проект WordPress по пути ../../Админка и сайт:

  • в нём есть docker-compose.yml и запущен сервис wordpress — импортёр вызывает в нём PHP-экспортёр через docker compose exec;
  • доступен каталог ../../Админка и сайт/runtime/htdocs с PDF и медиа исторических заказов;
  • в текущем docker-compose.yml проверены read-only mount: ../../ingame-prototype-v5-rewrite.html/app/site-prototype.html и ../Админка и сайт/original/htdocs/legacy/htdocs.

db:reconcile рассчитан на контрольный исторический снимок и содержит ожидаемые количества записей. На другой выгрузке он намеренно сообщит о расхождении; для новой миграции сначала согласуйте новые контрольные значения. Не подключайте импортёр к удалённой БД: скрипты намеренно принимают только локальные адреса.

Если HTML-файла сайта нет, /site вернёт 503, а healthcheck контейнера приложения станет unhealthy; PostgreSQL и страницы админки при этом могут быть доступны.

Запуск демонстрационного контура в Docker

npm ci
docker compose up -d --build
docker compose exec postgres pg_isready -U ingame -d ingame_admin
npm run db:import
npm run db:reconcile

Этот режим запускает уже собранную версию через next start, а не режим разработки с hot reload. После изменения кода пересоберите и перезапустите контейнер: docker compose up -d --build.

После запуска:

Активная разработка на хосте

Сначала поднимите PostgreSQL. Затем создайте .env.local на основе .env.local.example: DATABASE_URL обязателен для серверных страниц; SITE_PROTOTYPE_PATH нужен только для маршрута /site. После этого выполните npm run dev.

Скрипты импорта и сверки используют тот же локальный адрес PostgreSQL по умолчанию. Схема из db/001_init.sql применяется Postgres только при первом создании тома postgres_data; изменение этого файла не меняет уже созданную базу. Для чистого локального запуска требуется новый том либо отдельная миграция.

Контракты и важные правила

Публичное API

Метод Маршрут Назначение
GET /api/public/availability?date=YYYY-MM-DD Доступные слоты квестов на дату.
POST /api/public/bookings Создаёт одно бронирование источника SITE. Требует дату, время, questId, число игроков, имя, телефон, email и язык et/ru/en.

Маршруты предназначены только для локального прототипа: CORS разрешён для 127.0.0.1:3100, localhost:3100 и null. Production-эксплуатация, авторизация и защита от злоупотреблений для этих API пока не реализованы.

Бронь и деньги

  • Бронь, заказ и оплата — разные сущности. Не добавляйте в код проверку доступности только на уровне UI: окончательная проверка обязана оставаться в транзакции и в ограничениях PostgreSQL.
  • Ручная скидка требует причины; промокод проверяется на активность, срок и лимит.
  • Денежные суммы принимаются и сохраняются в центах; не используйте floating point для расчётов.
  • Исторические данные могут быть неполными. Сохраняйте legacy ID и снимки строк заказа, а спорные записи отправляйте в import_quarantine вместо молчаливого удаления.
  • Все операции, которые меняют деньги или историю, должны быть аудируемыми. В текущем прототипе аудит уже ведётся для части операций; при расширении не обходите этот механизм.

Локализация и каталог

  • Допустимые локали: et, ru, en.
  • Каноническая сущность каталога одна; языковые варианты и WPML-ссылки — это алиасы/переводы, а не отдельные товары.
  • Вариации товара синхронизируются через lib/server/catalog-variations.ts. Не дублируйте логику комбинаций атрибутов в страницах.

Проверка перед изменением

Тесты разделены по внешним зависимостям. Не запускайте все команды как один «минимальный набор» — часть из них специально требует локальный WordPress-контур и исторические файлы.

Проверка Команда Что требуется
Сборка npm run build Установленные зависимости; база и WordPress не нужны.
Схема PostgreSQL npm run test:db Docker в PATH и локальный Postgres-контур.
Импорт npm run test:import Docker, локальный Postgres, соседний WordPress-проект с сервисом wordpress и его файлами.
Весь набор node --test tests/*.test.mjs Все перечисленные зависимости, плюс HTML-файл сайта ../../ingame-prototype-v5-rewrite.html.

Для изменений бронирования, денег, промокодов, импорта или схемы дополнительно вручную проверьте: создание слота, попытку конкурирующей брони, блокировку/разблокировку, изменение заказа с несколькими бронями, корректность итогов и строку аудита.

Как продолжать разработку

  1. Сначала зафиксируйте правило в ТЗ и решите, влияет ли оно на деньги, бронь, миграцию или интеграции.
  2. Внесите изменение в типы и серверную бизнес-логику, затем при необходимости — в схему PostgreSQL и миграцию данных.
  3. Добавьте тест на обычную и ошибочную ветку. Для конфликтов брони используйте настоящий PostgreSQL, а не только мок.
  4. После серверной части добавьте интерфейс и ручную проверку сценария.
  5. Внешние интеграции проектируйте через отдельные адаптеры, inbox/outbox, идемпотентные ключи, проверку подписи webhook и журнал событий. Не встраивайте вызовы Montonio или amoCRM непосредственно в обработчик формы.