This commit is contained in:
Kirill
2026-07-02 23:13:13 +07:00
parent e997a20fe6
commit d4a57c0b6a
34 changed files with 524 additions and 67 deletions
+24
View File
@@ -0,0 +1,24 @@
# `modules/users` — профиль и Cyberhack
## Профиль
- `GET /users/me` — возвращает `ProfileData`, собранный одним агрегирующим SQL-запросом
(`repository::get_aggregated_profile`): юзер + текущая подписка (если есть, кроме
`expired`) + список рефералов одним JSON-объектом. Это осознанно сделано на стороне
БД (`jsonb_build_object`/`jsonb_agg`), а не в Rust несколькими запросами.
## Cyberhack (мини-игра)
- `POST /users/hack/result` (`{ score }`) — фронтенд (`frontend/src/HackGame.tsx`,
используется npm-пакет `cyberhack`) присылает результат раунда игры.
- `score` клампится на сервере в `0..=500` — это **единственная** серверная проверка
честности результата; клиент фактически сам решает, сколько очков получить.
Неэксплуатируемо, пока баланс E$ ни на что не тратится, но нужно закрыть до того, как
появится обмен баланса на что-то ценное — см. `// TODO` в `controller.rs`.
- `add_hack_reward` -> `repo.consume_ticket_and_reward` списывает один игровой билет и
начисляет баланс **в одной транзакции с `SELECT ... FOR UPDATE`**, чтобы параллельные
запросы одного юзера не списали билет дважды. Дополнительно требует минимум 45 секунд
между попытками (`last_hack_at`) — защита от скриптового фарма очков.
- Билеты выдаются только при покупке подписки (`billing::buy_subscription`, +1 билет,
кэп 10) — то есть поиграть в Cyberhack без действующей/когда-либо купленной подписки
нельзя.