Skip to content
Ricklayer
RUEN
← All work

01 · EloStore ecosystem

EloStore

A services platform for CS2

A commercial service: rank boosting, AI demo analysis, tournaments, skins, a partner program. Rewritten from a monolith to a new stack without stopping sales: 109,522 lines of TypeScript, 82 PostgreSQL tables and an API that answers in 30-90 ms in production.

The team on the storefront

Every booster gets a card on the home page: an agent render, FACEIT level 10 and ELO.

Renders from elostore.ru: made with our prompt engineering on our own 3D work and our own model

Payments: three gateways

Rubles on elostore.ru, dollars and euros on elostore.pro: a Russian payment gateway, a foreign wallet and crypto. Every payment goes through the same check before it becomes an order, and card data never reaches us at all.

The log follows the payment handling in the site's code, amounts and order numbers are made up

Payment logLava

    An order finds its booster

    A paid order reaches Telegram within a second: a confirmation to the client, a card to admins, an offer with a button to boosters. The site is self-sufficient: the same lands in the dashboard and by email, and the orders channel is a fallback for boosters. First to tap takes it, and the booster's share is credited automatically.

    Message texts from the EloStore bots' code, the order, names and amounts are made up

    01 / 05Order in TelegramThe client gets a confirmation, admins a card, boosters an offer with a button.

    EloStoreTelegram · to the client

    ✅ Payment received#A7K2FACEIT · 6 → 9 lvlTotal: 3,490 ₽ETA: 2-3 daysWe are matching you with a booster and will message you as soon as one picks up the order.
    My order

    EloStoreTelegram · to admins

    💸 New paid order#A7K2FACEIT · 6 → 9 lvlTotal: 3,490 ₽Client: ArtemETA: 2-3 days
    Open order

    EloStoreTelegram · to booster Stormrush

    🆕 New order availableFACEIT · 6 → 9 lvlETA: 2-3 daysFirst to accept takes it.
    ✅ Accept➖ Skip

    AI demo review

    A player uploads a match recording and gets a round-by-round review. The parser, 70+ metrics, map zone markup and benchmarks are built from scratch, and the LLM is set up and taught by example as a coach: role, slang vocabulary, rules, good and bad reviews.

    The player's numbers are made up, the metrics and review format are real

    01 / 06Drops the fileA match recording from FACEIT, Premier or MM lands in the dashboard.

    Drop a .dem or click to choose

    FACEITPremierMMWingman

    match_mirage.dem

    Tournaments

    A tournament runs entirely on the site: team sign-up, the bracket, captains banning maps and the match launching on our CS2 server. After the veto only the players, the admin and the caster see the server address, and the broadcast gets replays of the best moments.

    Tournament · 5v5 · Bo1Map: ·
    KKraken

    0 vs 0

    Kraken to ban a map
    RRedline
    AncientAncient
    AnubisAnubis
    Dust IIDust II
    InfernoInferno
    MirageMirage
    NukeNuke
    OverpassOverpass

    Double Elimination · 8 teams

    Upper · QF

    Kraken13

    Nomads9

    Stormline13

    Havoc11

    Frostbite8

    Zenith13

    Redline13

    Aftershock6

    Upper · SF

    Kraken13

    Stormline10

    Zenith11

    Redline13

    Upper final

    Kraken·

    Redline·

    CS2 servers where the matches are played

    A team assembles via a link

    Link for teammateselostore.ru/t/cup/join/•••••·
    • kkiracapt
    • open slot
    • open slot
    • open slot
    • open slot

    The task

    EloStore was selling on an old monolith: a single server.js, data without a strict schema, payments nobody dared to touch.

    The task: rewrite the whole product without stopping sales for a single day, and lay the ground for growth: SEO, new products, a second storefront for the western market.

    Slice by layer

    Interfacethe pixel
    Storefront with a price calculator, client, booster and partner dashboards, a 38-section admin, a broadcast overlay
    Logicmoney and rules
    Pricing as pure tested functions, only the webhook creates orders, duplicate-proof payouts, one core for site, bot and admin
    Dataschema and migrations
    82 tables in a dedicated schema, hand-written idempotent migrations, booster balance in three pockets under FOR UPDATE
    Serverdeploy and operations
    Standalone build as one archive, systemd and nginx, snapshot and health check on deploy, demo parsing as a separate service
    Securitythe kernel
    A guard in every admin page plus a test over every page.tsx, sign-in gate → password → 2FA, CSP without unsafe-inline

    Moving without stopping sales

    The old Express monolith was replaced with Next.js 15, React 19, strict TypeScript and Drizzle on top of the same PostgreSQL.

    1. 01First a study of the old system and the live database schema, then a Drizzle schema over the existing data: text dates converted to timestamptz, legacy ids kept
    2. 02Phases: schema → design system → dashboards → calculator and checkout → chats and admin → traffic switch in nginx
    3. 03The payment module was moved as a black box: formulas identical to the old production
    4. 04Server Actions for mutations, API routes only where an external call is needed: webhooks, Telegram, the game server

    Result

    Traffic runs on the new version, the old app is retired and archived.

    Money: payments and payouts

    Payments through three providers, crypto included, and automatic payouts to boosters.

    1. 01Only the provider's webhook creates an order, the client merely starts an invoice
    2. 02Atomic claim: UPDATE ... WHERE status <> 'succeeded' RETURNING. The delivery that wins the race creates the order
    3. 03A signature says who, not how much: the amount is checked and the price is recomputed on the server from a snapshot
    4. 04A provider without webhooks is covered by polling unpaid invoices every 3 minutes
    5. 05Payouts are idempotent per order-booster pair, balance deltas always sum to zero, and a test holds that

    Result

    A repeated or late webhook never creates a second order or rolls a status back.

    Four roles on one database

    Client, booster, partner and admin each work in their own interface over the same data.

    1. 01Client: orders, chat with the booster, account credentials encrypted and wiped on close, review and tips
    2. 02Booster: open requests, taking orders, progress, finances and withdrawals
    3. 03Partner on a separate subdomain: promo code, clicks, conversion, earnings
    4. 04Business logic lives in server/<domain>/core.ts and is called by the site, the bot and the admin: after payouts once diverged between dashboard and admin, duplicates are gone

    AI demo analysis

    A paid product: a player uploads a match recording and gets a round-by-round breakdown of mistakes.

    1. 01A Python CS2 replay parser computes aim and movement metrics
    2. 02LLM analysis on top of the metrics, a FastAPI worker in Docker, non-root, token compared in constant time
    3. 03LLM providers block Russian traffic: outgoing requests go through a SOCKS tunnel to a node abroad

    Result

    Heavy processing is isolated from the storefront and cannot take it down.

    Streamer partner program and anti-fraud

    Streamers earn a share of orders made with their promo code, viewer-count inflation is caught automatically.

    1. 01We count not how much the chat writes but how many live people write: a bot farm can write, but its accounts give it away
    2. 02Signals: share of viewers showing up in chat as real accounts, viewers to followers, a flat viewer curve, zero-follower and batch-registered chatters
    3. 03Public sources only: anonymous Twitch IRC and open APIs, a tracker samples every 5 minutes
    4. 04With too little data the verdict is honest: not enough data, not a guess
    01 / 03Looks great from outsideThe counter shows 1,200 viewers, and the audience stays perfectly level all stream.
    Nnorth_wind_cslive1,200viewers

    one dot is 10 viewers

    Viewers over the hour

    a normal channel

    this channela flat line

    Stories from production

    Incident 01 · open

    A layout does not protect a page

    In Next.js, layout and page render in parallel. A check in the layout did not stop the page from querying the database and shipping data in the RSC payload: orders and client emails leaked to an unauthenticated curl.

    Incident 02 · open

    Admin settings never reached the price

    The surcharge function took rates as an argument but was called without it. Prices used hard-coded values whatever the admin set.

    Incident 03 · open

    The sitemap got cached empty

    ISR pages were prerendered at build time without database access, and the sitemap once got cached empty.

    Stack

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