Skip to content

Aliph0th/cb_aucs

Repository files navigation

CryptoBot Auctions

Проект представляет собой систему аукционов, похожую на Telegram Gift Auction, с веб‑клиентом и сервером, в которой пользователи делают ставки (включая pre‑bid), соревнуются за подарки и получают результаты в реальном времени через Socket.IO.

Демо

Основная логика аукционов

Сущности и понятия

  • Аукцион — объект, у которого есть подарок, количество раундов, длительность первого и последующих раундов, политика возвратов, настройки анти-снайпинга, количество подарков за раунд и прочее. Он оркестрируется полностью сервером (начало, новый раунд и тд).
  • Раунд — временной интервал внутри аукциона, где выбираются N победителей по сумме и времени ставок.
  • Подарок — приз, который выдается победителям. Всего подарков = giftsPerRound * roundCount.
  • Ставка — запись о ставке пользователя в текущем раунде. Ставка может быть обычной или автоматически перенесенной в следующий раунд.
  • Пользователь — участник аукциона с балансом, ставками и инвентарем подарков.
  • Вещь - (item) — объект, представляющий подарок в инвентаре пользователя после выигрыша, имеет номер выигрыша и цену.

Pre‑bids

  • До старта аукциона пользователи могут делать ставки. При этом такие ставки участвуют в первом раунде как полноценные.

Idempotency key

  • Защита от дублирующих запросов.
  • idempotencyKey привязан к пользователю. Это защищает от попыток получить чужой результат по чужому ключу.

MongoDB Replica Set и транзакции

Проект использует MongoDB транзакции для атомарных операций (ставки, выдача предметов, создание аукциона и т.д.). Для этого MongoDB должна работать в режиме replica set.

  • В docker-compose Mongo запускается с --replSet rs0.
  • Инициализация replica set выполняется отдельным сервисом mongo-init.
  • Для авторизации в replica set требуется keyFile. Он генерируется при старте контейнера скриптом scripts/mongo-generate-keyfile.sh и хранится в volume mongo_keyfile.

Если вы запускаете MongoDB вне Docker, убедитесь, что:

  • mongod стартует с --replSet rs0 и --keyFile ...;
  • в конфиге сервера выставлен MONGO_HOST (обычно localhost для локального запуска и mongo для Docker);
  • строка подключения содержит replicaSet=rs0.

Ранжирование и победители

  • Рейтинг ставок хранится в Redis Sorted Set для каждого раунда.
  • Сортировка — по сумме ставок (по убыванию).
  • Сумма ставок (в текущем раунде) определяет место пользователя в рейтинге (т.е пользователи могут добавлять деньги к текущей ставке).
  • При равных суммах используется tie‑break по последней ставке в БД: более ранняя ставка имеет приоритет.
  • Победители раунда — это топ‑N участников (N = giftsPerRound).
  • !!!!! Если в раунде участвует меньше пользователей, чем будет выдано подарков, то несмотря на это раунд завершится и участники получат подарки, однако, номера "сгоревших" подарков не сохраняются. Т.е, если в раунде раздается 5 подарков, а участников всего 3, то они получат подарки с номерами 1-3, а номера 4 и 5 - сгорят (мое допущение такое). Демо с аукциона в тестовом окружении Telegram, где видно, что при отсутствии участников подарки все равно "выдаются": ДЕМО

Минимальная ставка

Минимальная ставка динамическая и зависит от текущего распределения ставок:

  • Это сумма ставки пользователя на позиции N, где N = оставшиеся подарки по всему аукциону.
  • Если в рейтинге нет пользователя на позиции N, минимальная ставка равна startPrice.
  • Минимальная ставка обновляется:
    • при новой ставке;
    • при завершении раунда;
    • при старте нового раунда;

Потенциальное место по ставке

  • При вводе суммы ставка предварительно просчитывается на сервере, и возвращается потенциальное место в рейтинге.

Anti‑Sniping

  • Если до конца раунда осталось меньше порога времени, раунд автоматически продлевается на указанное количество секунд.
  • Действует на каждом раунде (кроме предварительных ставок), если не включено только для 1 раунда.
  • Продление применяется только если пользователь изменил позицию в топ‑рейтинге (по умолчанию в топ 3).
  • Количество продлений ограничено параметром limit.
  • Может быть отключено.

Политики возвратов

Поддерживаются три режима (я посчитал, что хорошим решением будет расширить возможность настройки возвратов, помимо стандартной в Telegram):

  1. no_refund — ставки не возвращаются.
  2. each_round — по завершению раунда все непобедившие ставки возвращаются.
  3. over_supply — (по умолчанию, как в Telegram) если ставок больше, чем оставшихся подарков, излишки возвращаются сразу (один пользователь вытеснил другого).

Перенос ставок между раундами

При политиках возврата (т.е не each_round), допускающих перенос, возможен bid carry‑over:

  • ставки участников, которые не получили подарок, автоматически переносятся в следующий раунд;

Инструкция по запуску

!!! В качестве аутентификации используется Telegram Login Widget, поэтому нужно создать Telegram бота:

  1. Создать бота через BotFather и получить токен.
  2. В /server/.env указать полученный BOT_TOKEN
  3. В /client/.env указать VITE_BOT_USERNAME (без @)
  4. Telegram Login Widget требует указания домена, поэтому для локального тестирования нужно использовать VS Code Port Forwarding, local tunnel, ngrok etc.
  5. После проброса порта клиента (5173 по умолчанию) в BotFather нужно выполнить команду /setdomain, выбрать бота и указать домен (без протокола и слэшей). Например, wrktftv5-5173.euw.devtunnels.ms. Возможно, придется немного подождать, пока телеграм применит изменения.

Запуск:

  1. git clone https://github.com/Aliph0th/cb_aucs.git
  2. cd cb_aucs
  3. Заполнить переменные окружения в .env в /server, /client (примеры в .env.example)
  4. Переменные окружения в корне проекта должны совпадать по значению с /server/.env (MONGO**, REDIS**) (для docker compose)
  5. docker compose up -d --build

По умолчанию сервер будет доступен по адресу http://localhost:3333, клиент - http://localhost:5173 (нужно пробросить порт, как описано выше).

Серверная архитектура

Основные слои:

  • HTTP API (Controllers) — входная точка для запросов (аутентификация, rate limit, ставки, аукционы, инвентарь).
  • Services (бизнес‑логика) — сценарии аукциона: ставки, ранги, возвраты, переносы, анти‑снайпинг.
  • WS Gateway — realtime‑уведомления для клиентов (изменения топа, новые раунды, возвраты и т.д.).
  • Persistency — MongoDB для ставок/пользователей/аукционов и Redis для ранжирования и быстрых операций по топам.

Технологии:

  • Typescript, NestJS + Mongoose — основная логика и хранение данных.
  • Redis — быстрые рейтинги и операции по ранжированию (ZSet) и BullMQ.
  • Socket.IO — realtime коммуникация.

Очередь (Bull)

Для фоновых задач и регулярного тика аукционов используется очередь Bull поверх Redis. Ключевой повторяющийся job — AUCTION_TICK, который запускается каждую секунду и дергает оркестратор аукциона.

Важно:

  • без Redis очередь не запустится, и аукционы не будут тикать/переходить по раундам;
  • частота тика сейчас фиксирована на 1s, меняется в AuctionModule при добавлении repeat-job.

Rate limiting

В API включено ограничение запросов на уровне пользователя (по userID из запроса). Используется скользящее окно на Redis Sorted Set:

  • Для каждого пользователя и маршрута хранится набор временных меток запросов.

  • Перед добавлением нового запроса все записи старше окна удаляются.

  • GET /auctions — 10 запросов за 5 секунд.

  • POST /auctions — 5 запросов за 60 секунд.

  • GET /auctions/:id/info — 30 запросов за 10 секунд.

  • POST /bids — 5 запросов за 2 секунды.

  • POST /users/authenticate — 1 запрос за 5 секунд.

  • GET /users/me — 15 запросов за 5 секунд.

  • GET /users/inventory — 10 запросов за 5 секунд.

  • POST /users/balance — 10 запросов за 5 секунд.

Эндпоинты без ограничений:

  • GET /gifts
  • POST /users/bots/init

События Socket.IO

  • auction_created — создан новый аукцион.
  • auctions_started — аукционы начались (переход к раунду 1).
  • new_round — начался новый раунд.
  • auction_finished — аукцион завершен.
  • top_changed — изменился рейтинг топ‑победителей.
  • round_time_extended — раунд продлен антиснайпингом.
  • out_of_winners — пользователь вышел из победителей раунда.
  • bid_refunded — ставка возвращена пользователю.
  • min_bid_updated — изменена минимальная ставка.

Основные модули:

  • auction — создание/запуск/завершение аукционов, инфо по аукциону.
  • bid — размещение ставок, ранжирование, возвраты, переносы.
  • round — управление раундами, выбор победителей.
  • gateway — WebSocket‑события и комнаты.

Нагрузочное тестирование (k6)

Скрипт: scripts/k6-auctions.js

Тестируются:

  • аукционы (GET /auctions, GET /auctions/:id/info)
  • ставки (POST /bids)
  • пользователи (GET /users/me)

Не тестируются: health, gifts.

Запуск:

  • Запустить через Docker (profile loadtest):
    • pnpm run loadtest
    • или напрямую: docker compose --profile loadtest run --rm k6

Смену аккаунта на клиенте как таковую не делал. Для смены нужно удалить auth_token из localStorage и удалить сессию в Telegram, после перезагрузить страницу

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Contributors