# Аккаунты, токены и SPA Поверх тайлового пайплайна: Яндекс OAuth, персональные стили/URL, учёт запросов, Vite-кабинет на том же origin. ## Данные - EF Core SQLite, файл `/data/app.db` (`IDataPaths.AppDatabase`). - Таблицы: `Users`, `ApiTokens` (только SHA-256 hash + prefix `ts_...`), `UserStyles`, `UsageDaily`. - Миграция `InitialAccounts`, `Database.Migrate()` на старте. ## Auth - `GET /api/v1/auth/yandex/start` → 302 на oauth.yandex.ru. - SPA ловит `?code=&state=` на `/`, `POST /api/v1/auth/yandex`. - Cookie `ts_session` HttpOnly, SameSite=Lax, Secure=SameAsRequest. - Кабинет: cookie. Выдача тайлов/стилей: `?token=` или `Authorization: Bearer`. - Секрет Яндекса только в env: `TileServer__Yandex__ClientId|ClientSecret|RedirectUri`. Не коммитить. ## Выдача и usage - `/u/{slug}/tiles/{source}/{z}/{x}/{y}.pbf?token=` - `/u/{slug}/tiles/{source}.json?token=` - `/u/{slug}/styles/{name}?source=&token=` - Счётчик в `ConcurrentDictionary`, flush каждые 5 с в `UsageDaily`. - Публичные `/api/v1/tiles` и `/api/v1/styles` без счётчика. ## Стили - Клон пресета: `POST /api/v1/me/styles`. - Bind персонального стиля: `tiles[0]` = `/u/{slug}/tiles/{source}/...pbf?token=`. - Свои имена перекрывают пресеты. ## Фронт и деплой - `web/`: Vite + React + styled-components, тема HUD (`#e8923d`). - Docker: stage `node:22` → `wwwroot/app`. `/` — SPA, `/demo` — старое Razor-демо. - Compose: `YANDEX_CLIENT_ID` / `YANDEX_CLIENT_SECRET` с хоста. ## Тесты 26 unit-тестов: hash/verify/revoked, slug, bind `/u/{slug}/` + token, батч usage.