# 🗄 Supabase Setup для «Шашки Рояль»

## 1. Создание проекта

1. Зайди на [supabase.com](https://supabase.com/) → New project
2. Project name: `shashki-royale` (или любое)
3. Region: ближайший к твоей аудитории (EU Central / South Asia)
4. Запиши **Project URL** и **anon public key** (Settings → API)

## 2. Получение ключей

В Supabase Dashboard → **Settings** → **API**:

| Ключ | Куда |
|---|---|
| **Project URL** (`https://xxx.supabase.co`) | `VITE_SUPABASE_URL` |
| **anon public** (`sb_publishable_...` или legacy `eyJ...`) | `VITE_SUPABASE_ANON_KEY` |
| **service_role secret** | ❌ **НИКОГДА** в клиентский код / .env с префиксом VITE_ |

## 3. Применение SQL миграций

⚠️ **Применять в указанном порядке.** Каждая миграция обратно-совместима (`IF NOT EXISTS`), но не запускай поздние раньше ранних.

Открой Dashboard → **SQL Editor** → **New query** → вставь содержимое файла → **Run**:

| # | Файл | Что делает | Обязательность |
|---|---|---|---|
| 1 | `01_stakes_core.sql` | Базовая схема + stake-система | Обязательно |
| 2 | `02_audit_and_crypto_prep.sql` | audit_logs, payment intents | Обязательно |
| 3 | `05_coin_economy_upgrade.sql` | Монетная экономика | Обязательно |
| 4 | `20260429_add_mode_to_games.sql` | play_mode (quickplay/friend) | Обязательно |
| 5 | `20260429_economy_phases_2_to_6.sql` | Дополнения экономики | Обязательно |
| 6 | `20260522_production_hardening.sql` | RLS + CAS + heartbeat + cleanup | **Production** |
| 7 | `20260522_p0_server_move_validation.sql` | **P0: server-side анти-чит** | **Production** |

После каждой миграции должно появиться `OK` или `Success`.

## 4. Включение Realtime

Dashboard → **Database** → **Publications** → `supabase_realtime`:

Включить (галочки) для таблиц:
- ✅ `games`
- ✅ `moves`
- ✅ `wallets` (опционально, для live balance)
- ✅ `game_stakes` (опционально)

Или через SQL:
```sql
ALTER PUBLICATION supabase_realtime ADD TABLE games;
ALTER PUBLICATION supabase_realtime ADD TABLE moves;
```

## 5. Cron Job для cleanup (рекомендуется)

Dashboard → **Database** → **Cron Jobs** → **Create**:

- **Name:** `cleanup_stale_rooms`
- **Schedule:** `*/5 * * * *` (каждые 5 минут)
- **SQL:**
  ```sql
  SELECT cleanup_stale_rooms();
  ```

Это автоматически закрывает waiting-комнаты старше 10 минут и удаляет moves старше 7 дней для финишированных игр.

## 6. Проверка применения

В SQL Editor:

```sql
-- 1. RLS включён?
SELECT tablename, rowsecurity
FROM pg_tables
WHERE schemaname='public' AND tablename IN ('games', 'moves');
-- Должно быть rowsecurity=true

-- 2. RPC задеплоены?
SELECT proname
FROM pg_proc
WHERE proname IN (
  'apply_move_safe', 'apply_move_v2', 'heartbeat_game',
  'cleanup_stale_rooms', '_apply_step_to_board', '_count_pieces',
  'set_player_context'
)
ORDER BY proname;
-- Должно быть 7 строк

-- 3. Realtime publication?
SELECT tablename FROM pg_publication_tables WHERE pubname='supabase_realtime';
-- games и moves должны быть в списке

-- 4. Триггеры?
SELECT tgname FROM pg_trigger WHERE tgname = 'games_touch_timestamps_trg';
```

## 7. Мониторинг подозрительной активности

После релиза периодически запускай:

```sql
SELECT event_type, COUNT(*) AS attempts, MAX(created_at) AS last_attempt
FROM audit_logs
WHERE event_type LIKE 'move.%'
  AND created_at > NOW() - INTERVAL '24 hours'
GROUP BY event_type
ORDER BY attempts DESC;
```

**Возможные event_type:**
- `move.unauthorized` — не-участник попытался ходить
- `move.wrong_turn` — попытка походить не в свой ход
- `move.invalid_step` — невалидный step (вне границ, не диагональ)
- `move.board_mismatch` — **возможный читер** — клиент прислал board, не совпадающий с серверным

Если в `move.board_mismatch` появятся записи — посмотри:
```sql
SELECT event_data->>'player_id' AS player, created_at,
       event_data->'server_board' AS server, event_data->'client_board' AS client
FROM audit_logs
WHERE event_type = 'move.board_mismatch'
ORDER BY created_at DESC
LIMIT 20;
```

Можно забанить `player_id` через таблицу `profiles` (поле `is_banned`).

## 8. Откат (rollback)

Если что-то пошло не так, отдельные миграции можно откатить:

```sql
-- Снять строгие RLS (вернуть к открытым)
DROP POLICY IF EXISTS "games_insert_self" ON games;
DROP POLICY IF EXISTS "games_update_participant" ON games;
DROP POLICY IF EXISTS "games_no_delete" ON games;
DROP POLICY IF EXISTS "moves_insert_participant" ON moves;

-- Удалить P0 RPC
DROP FUNCTION IF EXISTS apply_move_v2(TEXT, UUID, INT, JSONB, JSONB, TEXT, INT);
DROP FUNCTION IF EXISTS _apply_step_to_board(JSONB, INT, INT, INT, INT, INT, INT, TEXT);
DROP FUNCTION IF EXISTS _count_pieces(JSONB);
```

Клиент при отсутствии новых RPC автоматически fallback'ит на старый `UPDATE`.

## 9. Резервное копирование

Dashboard → **Database** → **Backups** — Supabase Pro делает ежедневные бэкапы автоматически.

Для ручного экспорта:
```bash
pg_dump "postgresql://postgres:[PASSWORD]@db.YOUR-PROJECT.supabase.co:5432/postgres" > backup.sql
```

## 10. Лимиты Supabase

| План | DB Size | Realtime connections | Cost |
|---|---|---|---|
| Free | 500 MB | 200 | $0 |
| Pro | 8 GB | 500 | $25/mo |
| Team | 8+ GB | 2500 | $599/mo |

Для 100+ одновременных игр рекомендуется **Pro**.

---

**Готово!** Если нужна помощь — открой issue или смотри [главный README](../README.md).
