К содержимому
Ricklayer
RUEN
← Все кейсы

01 · Экосистема EloStore

EloStore

Платформа услуг для CS2

Коммерческий сервис: буст рейтинга, AI-разбор демок, турниры, скины, партнерка. Переписан с монолита на новый стек без остановки продаж: 109 522 строки TypeScript, 82 таблицы PostgreSQL и ответ API за 30-90 мс на проде.

Команда на витрине

У каждого бустера на главной своя карточка: рендер агента, 10 уровень FACEIT и ELO.

Рендеры с elostore.ru: сделаны нашим промпт-инжинирингом на наших 3D-наработках и нашей собственной модели

Оплата: три шлюза

Рубли на elostore.ru, доллары и евро на elostore.pro: российская платежка, зарубежный кошелек и криптовалюта. Каждая оплата проходит одну и ту же проверку, прежде чем стать заказом, а данные карт к нам не попадают вообще.

Журнал повторяет обработку оплаты в коде сайта, суммы и номера заказов вымышленные

Журнал оплатыLava

    Заказ сам находит исполнителя

    Оплаченный заказ за секунду уходит в Telegram: клиенту подтверждение, админам карточка, бустерам заявка с кнопкой. Сайт при этом самодостаточен: все то же приходит в кабинет и на почту, а запасной путь для бустеров это канал заказов. Кто первый нажал, того и заказ, а доля бустера начисляется сама.

    Тексты сообщений из кода ботов EloStore, заказ, имена и суммы вымышленные

    01 / 05Заказ в TelegramКлиенту подтверждение, админам карточка, бустерам заявка с кнопкой.

    EloStoreTelegram · клиенту

    ✅ Оплата получена#A7K2FACEIT · 6 → 9 lvlСумма: 3 490 ₽Срок: 2-3 дняПодбираем бустера. Напишем, как только он возьмёт заказ.
    Мой заказ

    EloStoreTelegram · админам

    💸 Новый оплаченный заказ#A7K2FACEIT · 6 → 9 lvlСумма: 3 490 ₽Клиент: АртемСрок: 2-3 дня
    Открыть заказ

    EloStoreTelegram · бустеру Stormrush

    🆕 Новая заявкаFACEIT · 6 → 9 lvlСрок: 2-3 дняСвободна, кто первый - того и заказ.
    ✅ Взять заказ➖ Пропустить

    AI-разбор демки

    Игрок загружает запись матча и получает разбор по раундам. Парсер, больше 70 метрик, разметку зон карт и эталоны мы сделали с полного нуля, а LLM настроили и обучили на примерах как тренера: роль, словарь сленга, правила, хороший и плохой разбор.

    Цифры игрока в примере вымышленные, метрики и формат разбора настоящие

    01 / 06Перетаскивает файлЗапись матча с FACEIT, Premier или ММ падает в кабинет.

    Перетащи .dem или нажми, чтобы выбрать

    FACEITPremierММWingman

    match_mirage.dem

    Турниры

    Турнир проходит целиком на сайте: заявка командой, сетка, бан карт капитанами и запуск матча на нашем CS2-сервере. Адрес сервера после вето видят только игроки, админ и кастер, а трансляцию дополняют повторы ярких моментов.

    Турнир · 5v5 · Bo1Карта: ·
    KKraken

    0 vs 0

    Ход Kraken: бан карты
    RRedline
    AncientAncient
    AnubisAnubis
    Dust IIDust II
    InfernoInferno
    MirageMirage
    NukeNuke
    OverpassOverpass

    Double Elimination · 8 команд

    Верхняя · 1/4

    Kraken13

    Nomads9

    Stormline13

    Havoc11

    Frostbite8

    Zenith13

    Redline13

    Aftershock6

    Верхняя · 1/2

    Kraken13

    Stormline10

    Zenith11

    Redline13

    Финал верхней

    Kraken·

    Redline·

    CS2-серверы, на которых играются матчи

    Команда собирается по ссылке

    Ссылка для тиммейтовelostore.ru/t/cup/join/•••••·
    • kkiraкэп
    • свободный слот
    • свободный слот
    • свободный слот
    • свободный слот

    Задача

    EloStore продавал на старом монолите: один server.js, данные без строгой схемы, страшно трогать платежи.

    Задача: переписать продукт целиком так, чтобы продажи не останавливались ни на день, и заложить основу для роста: SEO, новые продукты, вторая витрина на западный рынок.

    Срез по слоям

    Интерфейспиксель
    Витрина с калькулятором цены, кабинеты клиента, бустера и партнера, админка на 38 разделов, оверлей для трансляций
    Логикаденьги и правила
    Цена чистыми функциями с тестами, заказ создает только вебхук, начисления без дублей, одно ядро на сайт, бота и админку
    Данныесхема и миграции
    82 таблицы в отдельной схеме, миграции руками и идемпотентно, баланс исполнителя в трех карманах под FOR UPDATE
    Сервервыкладка и эксплуатация
    Standalone-сборка одним архивом, systemd и nginx, снимок и health-check в деплое, разбор демок отдельной службой
    Безопасностьядро
    Guard в каждой странице админки и тест на все page.tsx, вход gate → пароль → 2FA, CSP без unsafe-inline

    Переезд без остановки продаж

    Старый монолит на Express заменен на Next.js 15, React 19, TypeScript strict и Drizzle поверх той же PostgreSQL.

    1. 01Сначала разбор старой системы и живой схемы базы, потом схема Drizzle поверх существующих данных: текстовые даты переведены в timestamptz, старые идентификаторы сохранены
    2. 02Фазы: схема → дизайн-система → кабинеты → калькулятор и оплата → чаты и админка → переключение трафика в nginx
    3. 03Платежный модуль перенесен как черный ящик: формулы один в один с прежним продом
    4. 04Server Actions для мутаций, API-роуты только там, где нужен внешний вызов: вебхуки, Telegram, игровой сервер

    Итог

    Трафик на новой версии, старое приложение выведено из обращения и заархивировано.

    Деньги: оплата и выплаты

    Прием оплаты через три провайдера, включая криптовалюту, и автоматические начисления исполнителям.

    1. 01Заказ создает только вебхук провайдера, клиент лишь инициирует счет
    2. 02Атомарный клейм: UPDATE ... WHERE status <> 'succeeded' RETURNING. Заказ создает та доставка, что выиграла гонку
    3. 03Подпись говорит «от кого», но не «сколько»: сумма сверяется, цена пересчитывается на сервере из снимка
    4. 04Провайдер без вебхуков страхуется опросом неоплаченных счетов раз в 3 минуты
    5. 05Выплата идемпотентна по паре заказ-исполнитель, сумма дельт баланса всегда ноль, это держит тест

    Итог

    Повторный или опоздавший вебхук не создает второй заказ и не откатывает статус.

    Четыре роли на одной базе

    Клиент, бустер, партнер и админ работают в своих интерфейсах поверх одних данных.

    1. 01Клиент: заказы, чат с исполнителем, доступы к аккаунту шифруются и стираются при закрытии, отзыв и чаевые
    2. 02Бустер: пул заявок, взятие заказа, прогресс, финансы и вывод
    3. 03Партнер на своем поддомене: промокод, переходы, конверсия, начисления
    4. 04Бизнес-логика живет в server/<домен>/core.ts и вызывается из сайта, бота и админки: после расхождения выплат между кабинетом и админкой дублей больше нет

    AI-разбор демок

    Платный продукт: игрок загружает запись матча и получает разбор ошибок по раундам.

    1. 01Python-парсер реплеев CS2 считает метрики прицела и движения
    2. 02LLM-анализ поверх метрик, воркер на FastAPI в Docker, non-root, токен сравнивается за константное время
    3. 03Запросы к LLM из РФ режутся провайдерами: исходящий трафик идет SOCKS-туннелем на зарубежный узел

    Итог

    Тяжелая обработка изолирована от витрины и не может ее положить.

    Партнерка со стримерами и антифрод

    Стримеры получают процент с заказов по промокоду, накрутка онлайна ловится автоматически.

    1. 01Считаем не «сколько пишет чат», а «сколько пишет живых»: ботоферма умеет писать, но ее аккаунты палятся
    2. 02Сигналы: доля онлайна, проявившаяся в чате живыми аккаунтами, онлайн к фолловерам, плоский график онлайна, нулевые фолловеры и кучная регистрация у писавших
    3. 03Только публичные источники: анонимный Twitch IRC и открытые API, трекер копит данные каждые 5 минут
    4. 04Пока данных мало, вердикт честный: «мало данных», а не догадка
    01 / 03Снаружи все отличноНа счетчике 1 200 зрителей, и онлайн весь эфир держится ровно.
    Nnorth_wind_csв эфире1 200зрителей

    точка это 10 зрителей

    Онлайн за час

    обычный канал

    этот каналровная линия

    Истории с прода

    Инцидент 01 · открыт

    Layout не защищает страницу

    В Next.js layout и page рендерятся параллельно. Проверка в layout не мешала странице сходить в базу и отдать данные в RSC-пейлоаде: заказы и почты клиентов уходили неавторизованному curl.

    Инцидент 02 · открыт

    Настройки админки не доезжали до цены

    Функция надбавки принимала ставки аргументом, а вызывалась без него. Цена считалась по зашитым значениям, что бы ни выставил админ.

    Инцидент 03 · открыт

    Карта сайта закешировалась пустой

    Страницы с ISR пререндерились во время сборки без доступа к базе, и sitemap однажды ушел в кеш пустым.

    Стек

    Next.js 15 · React 19 · TypeScript strict · Tailwind v4 · Drizzle ORM · PostgreSQL · Zod · Pino · Vitest · Python · FastAPI · Docker · nginx · systemd