first commit

This commit is contained in:
2026-08-07 13:41:43 +03:00
commit dd7f61a958
28 changed files with 2888 additions and 0 deletions

367
docs/API.md Normal file
View File

@@ -0,0 +1,367 @@
# ShbUtils Core API
Локальный справочник по API сервиса `https://api.shlakoblock.com`.
- Источник: `https://api.shlakoblock.com/api.json`
- OpenAPI: `3.1.0`
- Версия сервиса: `2.1.3-SNAPSHOT`
- Дата снимка: `2026-07-22`
- Формат запросов и ответов, если не указано иное: `application/json`
Документ является снимком контракта. В OpenAPI не описаны rate limits, гарантии идемпотентности и формат JSON-тел для большинства ошибок.
## Авторизация
### Internal endpoints
Все пути `/internal/*` требуют заголовок:
```http
X-Internal-Secret: <shared-secret>
```
При отсутствующем или неверном секрете возвращается `401 invalid_internal_secret`.
### Player endpoints
Пути `/api/*` требуют bearer-токен, выданный `POST /internal/auth/start`:
```http
Authorization: Bearer <token>
```
Хотя схема `BearerAuth` присутствует в OpenAPI, требования `security` не проставлены у самих операций. Необходимость токена следует из ответов `401 missing/invalid token`; для этих endpoint также возможен `403 player_offline`.
## Basic
### `GET /`
Маркер сервиса. Возвращает `200` и название сервиса простым текстом.
### `GET /health`
Проверяет соединения с БД и Redis.
| Код | Значение |
| --- | --- |
| `200` | `status: ok` |
| `503` | `status: degraded` |
## Internal
### `POST /internal/join`
Регистрирует вход игрока.
```json
{
"uuid": "11111111-1111-1111-1111-111111111111",
"playerName": "Player"
}
```
`playerName` необязателен и может быть `null`. Успех: `200 ApiMessage`. Ошибки: `400 invalid_body / invalid_uuid`, `401 invalid_internal_secret`.
### `POST /internal/quit`
Регистрирует выход игрока. Тело: `UuidRequest`. Успех: `200 ApiMessage`. Ошибки: `400 invalid_body / invalid_uuid`, `401`.
### `POST /internal/auth/start`
Выдаёт bearer-токен для игрока. Игрок должен быть онлайн.
- Тело: `UuidRequest`.
- Успех: `200 TokenResponse`.
- Ошибки: `400 invalid_body / invalid_uuid`, `401`, `403 player_offline`.
### `POST /internal/verification/code`
Создаёт verification-код для Minecraft UUID. Это направление «Minecraft → внешний клиент», поэтому ShbDiscordBot не использует endpoint для своего `/auth`.
- Тело: `VerificationCodeRequest`.
- Успех: `200 VerificationCodeResponse`.
- Ошибки: `400 invalid_body / invalid_uuid`, `401`.
### `POST /internal/verification/verify`
Проверяет ранее созданный код и привязывает Telegram и/или Discord.
```json
{
"code": "123456",
"telegramId": 123456789,
"telegramTag": "example",
"discordId": "123456789012345678"
}
```
Кроме `code`, поля необязательны и nullable. Успех: `200 VerificationVerifyResponse`. Ошибки: `400 invalid_body`, `401`, `404 invalid_or_expired_code`.
### `POST /internal/auth/password`
Проверяет пароль игрока без передачи хэша наружу.
```json
{
"uuid": "11111111-1111-1111-1111-111111111111",
"password": "plain-text-password"
}
```
Успех: `200 PasswordVerifyResponse`. Ошибки: `400 invalid_body / invalid_uuid`, `401`.
### `POST /internal/auth/password/change`
Заменяет пароль игрока.
```json
{
"uuid": "11111111-1111-1111-1111-111111111111",
"newPassword": "new-plain-text-password"
}
```
Успех: `200 ApiMessage`. Ошибки: `400 invalid_body / invalid_uuid / invalid_password`, `401`, `404 player_not_found`.
### `POST /internal/players/password/reset`
Удаляет сохранённый пароль игрока. Тело: `PasswordResetRequest`. Успех: `200 ApiMessage`. Ошибки: `400 invalid_body / invalid_uuid`, `401`, `404 player_not_found`.
### `POST /internal/players/unlink`
Отвязывает Telegram и/или Discord. Оба флага по умолчанию равны `true`.
```json
{
"uuid": "11111111-1111-1111-1111-111111111111",
"telegram": true,
"discord": true
}
```
Успех: `200 ApiMessage`. Ошибки: `400 invalid_body / invalid_uuid`, `401`, `404 player_not_found`.
### `POST /internal/players/status`
Ищет игрока и возвращает состояние регистрации/привязок. В теле должно присутствовать ровно одно из полей `uuid`, `name`, `telegramId`, `discordId`.
```json
{
"discordId": "123456789012345678"
}
```
Успех: `200 PlayerStatusResponse`; если совпадение отсутствует, возвращается объект с `exists: false`, а не `404`. Ошибки: `400 invalid_body / invalid_uuid / exactly_one_identifier_required`, `401`.
### `GET /internal/players/{uuid}`
Возвращает полный `PlayerProfile` по UUID. Ошибки: `400 invalid_uuid`, `401`, `404 player_not_found`.
### `GET /internal/players/by-name/{name}`
Ищет полный `PlayerProfile` по Minecraft-нику без учёта регистра. Ошибки: `401`, `404 player_not_found`.
### `GET /internal/players/by-telegram/{telegramId}`
Ищет полный `PlayerProfile` по Telegram ID. Ошибки: `400 invalid_telegram_id`, `401`, `404 player_not_found`.
### `POST /internal/players/{uuid}/telegram`
Устанавливает либо очищает Telegram-привязку.
```json
{
"telegramId": 123456789,
"telegramTag": "example"
}
```
Оба поля nullable; `null` используется для отвязки. Успех: `200 PlayerProfile`. Ошибки: `400 invalid_body / invalid_uuid`, `401`, `404 player_not_found`.
### `POST /internal/players/{uuid}/discord`
Устанавливает либо очищает Discord-привязку. Это endpoint, используемый ShbDiscordBot после погашения локального кода.
```json
{
"discordId": "123456789012345678"
}
```
`discordId: null` означает отвязку. Успех: `200 PlayerProfile`. Ошибки: `400 invalid_body / invalid_uuid`, `401`, `404 player_not_found`.
## Player API
### `GET /api/players/online`
Возвращает массив `Player` со всеми онлайн-игроками. Ошибки: `401 missing/invalid token`, `403 player_offline`.
### `GET /api/players/registered`
Возвращает массив `Player` со всеми зарегистрированными игроками. Ошибки: `401 missing/invalid token`, `403 player_offline`.
### `GET /api/players/me/state`
Возвращает `MyPlayerStateResponse` для владельца bearer-токена. Ошибки: `401 missing/invalid token`, `403 player_offline`.
### `GET /api/players/{uuid}`
Возвращает зарегистрированного игрока как `Player`.
Ошибки: `400 invalid_uuid`, `401 missing/invalid token`, `403 player_offline`, `404 player_not_found`.
## Схемы данных
`required` ниже означает обязательность по OpenAPI. Nullable-поле может явно содержать `null`.
### `ApiMessage`
| Поле | Тип | required |
| --- | --- | --- |
| `message` | string | да |
### `JoinRequest`
| Поле | Тип | required |
| --- | --- | --- |
| `uuid` | string | да |
| `playerName` | string или null | нет |
### `UuidRequest`, `VerificationCodeRequest`, `PasswordResetRequest`
Каждая схема содержит одно обязательное поле `uuid: string`.
### `TokenResponse`
| Поле | Тип | required |
| --- | --- | --- |
| `token` | string | да |
### `VerificationCodeResponse`
| Поле | Тип | required |
| --- | --- | --- |
| `code` | string | да |
| `expiresAt` | int64 | да |
OpenAPI не уточняет единицу `expiresAt`; клиенту следует согласовать её с реализацией сервиса.
### `VerificationVerifyRequest`
| Поле | Тип | required |
| --- | --- | --- |
| `code` | string | да |
| `telegramId` | int64 или null | нет |
| `telegramTag` | string или null | нет |
| `discordId` | string или null | нет |
### `VerificationVerifyResponse`
| Поле | Тип | required |
| --- | --- | --- |
| `uuid` | string | да |
| `name` | string | да |
### `PasswordVerifyRequest`
Обязательные поля: `uuid: string`, `password: string`.
### `PasswordVerifyResponse`
Обязательное поле: `valid: boolean`.
### `PasswordChangeRequest`
Обязательные поля: `uuid: string`, `newPassword: string`.
### `UnlinkAccountsRequest`
| Поле | Тип | required |
| --- | --- | --- |
| `uuid` | string | да |
| `telegram` | boolean | нет |
| `discord` | boolean | нет |
### `PlayerStatusRequest`
Все поля необязательны/nullable, но сервер требует ровно один идентификатор:
| Поле | Тип |
| --- | --- |
| `uuid` | string или null |
| `name` | string или null |
| `telegramId` | int64 или null |
| `discordId` | string или null |
### `PlayerStatusResponse`
Только `exists` обязательно по схеме. Остальные поля могут отсутствовать при `exists: false`.
| Поле | Тип |
| --- | --- |
| `exists` | boolean |
| `uuid` | string или null |
| `name` | string или null |
| `registered` | boolean |
| `telegramLinked` | boolean |
| `telegramId` | int64 или null |
| `telegramTag` | string или null |
| `discordLinked` | boolean |
| `discordId` | string или null |
### `PlayerProfile`
Все поля обязательны; идентификаторы внешних аккаунтов nullable.
| Поле | Тип |
| --- | --- |
| `uuid` | string |
| `name` | string |
| `registered` | boolean |
| `online` | boolean |
| `telegramId` | int64 или null |
| `telegramTag` | string или null |
| `discordId` | string или null |
### `TelegramLinkRequest`
Необязательные nullable-поля: `telegramId: int64`, `telegramTag: string`.
### `DiscordLinkRequest`
Необязательное nullable-поле: `discordId: string`.
### `Player`
| Поле | Тип | required |
| --- | --- | --- |
| `uuid` | string | да |
| `name` | string или null | да |
### `MyPlayerStateResponse`
Обязательные поля: `uuid: string`, `name: string`.
## Пример привязки Discord напрямую
Проверка текущей связи:
```http
POST /internal/players/status HTTP/1.1
Host: api.shlakoblock.com
X-Internal-Secret: <secret>
Content-Type: application/json
```
Запись Discord ID игроку:
```http
POST /internal/players/11111111-1111-1111-1111-111111111111/discord HTTP/1.1
Host: api.shlakoblock.com
X-Internal-Secret: <secret>
Content-Type: application/json
```
{"discordId":"123456789012345678"}
```