17 KiB
API для разработчиков: Технические запросы
Этот файл описывает технические WebSocket-запросы, которые нужны для служебной работы клиента с сервером. Часть операций доступна без авторизации, часть требует успешной авторизованной сессии.
Сейчас здесь восемь методов:
Ping— keep-alive запрос для поддержания живого WebSocket-соединения;GetServerInfo— запрос базовой публичной информации о сервере для выбора узла в децентрализованной сети;ListBlockchainHeads— краткая сводка по всем локальным блокчейнам сервера для межсерверной синхронизации;GetSyncUserProfile— межсерверный профиль пользователя для создания локальной цепочки без Solana RPC;GetCallIceConfig— выдача STUN/TURN конфигурации для звонков;ClientErrorLog— отправка клиентской ошибки в серверный лог;ClientDebugLog— отправка клиентского debug-события в серверный буфер;CallDeliveryReport— диагностический отчёт клиента о доставке/установке звонка.
Логика раздела такая:
Pingнужен для регулярной проверки, что соединение всё ещё живо;GetServerInfoнужен до авторизации и до работы с данными, чтобы клиент понял, что сервер доступен, и показал пользователю краткую карточку этого узла.ListBlockchainHeadsнужен для сервер-сервер сверки: партнёр получает список heads по всем цепочкам, сравнивает его со своим состоянием и затем добирает недостающие блоки по диапазону.GetSyncUserProfileнужен для server-to-server режима, когда принимающий сервер хочет создать у себя локальныеsolana_users + blockchain_stateбез прямого обращения в Solana. Это используется как временный обход ограничений внешнего Solana RPC.
Ниже сначала описаны назначение методов, затем точные форматы запросов и ответов.
1. Ping
Назначение
Служебный keep-alive запрос.
Клиент может отправлять его периодически, чтобы:
- поддерживать активное WebSocket-соединение;
- понимать, что сервер отвечает;
- при необходимости получать текущее серверное время.
Запрос
{
"op": "Ping",
"requestId": "ping-001",
"payload": {
"ts": 1774700000123
}
}
Поле ts в запросе необязательно для логики сервера. Сервер его не валидирует и не использует для принятия решения.
Успешный ответ
{
"op": "Ping",
"requestId": "ping-001",
"status": 200,
"ok": true,
"payload": {
"ts": 1774700000456
}
}
Специфические коды ошибок Ping
- У
Pingнет специальных прикладных ошибок. - Если произойдёт непредвиденная проблема, сервер вернёт общую ошибку из раздела
00, обычно500 / INTERNAL_ERROR.
2. GetServerInfo
Назначение
Запрос публичной информации о сервере.
Он нужен клиенту для выбора сервера в децентрализованной сети. По этому запросу клиент может:
- проверить, что сервер вообще доступен;
- показать URL и версию сервера;
- показать физический регион или адрес размещения;
- показать описание сервера;
- показать поле
originкак комментарий о природе этого узла; - показать дополнительную текстовую информацию.
Этот запрос доступен без авторизации.
Источник данных
versionберётся из Gradle build и подставляется вapplication.properties;- остальные поля читаются из настроек сервера;
- если значение в конфиге не задано, сервер возвращает пустую строку.
Запрос
{
"op": "GetServerInfo",
"requestId": "srv-001",
"payload": {
}
}
Успешный ответ
{
"op": "GetServerInfo",
"requestId": "srv-001",
"status": 200,
"ok": true,
"payload": {
"url": "wss://node.example.org/ws",
"version": "1.0",
"physicalRegion": "Грузия, Тбилиси",
"description": "Public community SHiNE node",
"origin": "Community-operated node",
"extraInfo": "IPv4 + IPv6; test federation enabled"
}
}
Поля ответа
url— публичный URL сервера.version— версия сервера из Gradle build.physicalRegion— физический регион или адрес размещения сервера.description— человекочитаемое описание сервера.origin— комментарий о том, какой это сервер.extraInfo— любая дополнительная информация о сервере.
Специфические коды ошибок GetServerInfo
- У
GetServerInfoнет специальных прикладных ошибок при штатной работе. - Если произойдёт непредвиденная проблема, сервер вернёт общую ошибку из раздела
00, обычно500 / INTERNAL_ERROR.
3. ListBlockchainHeads
Назначение
Запрос краткой сводки по всем локальным блокчейнам сервера.
Нужен для межсерверной синхронизации. Партнёр может:
- получить список всех блокчейнов;
- сравнить
lastBlockNumberиlastBlockHashсо своими значениями; - понять, какие цепочки нужно догонять;
- затем отдельно запросить недостающие блоки по диапазону.
Этот запрос доступен без авторизации.
Запрос
{
"op": "ListBlockchainHeads",
"requestId": "heads-001",
"payload": {}
}
Успешный ответ
{
"op": "ListBlockchainHeads",
"requestId": "heads-001",
"status": 200,
"ok": true,
"payload": {
"blockchains": [
{
"blockchainName": "alice_main",
"lastBlockNumber": 124,
"lastBlockHash": "aabbccdd00112233445566778899aabbccddeeff00112233445566778899aabb",
"fileSizeBytes": 58720
}
]
}
}
Поля ответа
blockchains— массив текущих heads всех цепочек сервера.blockchainName— имя блокчейна.lastBlockNumber— последний номер блока в этой цепочке.lastBlockHash— последний хэш блока в HEX-формате64символа.fileSizeBytes— текущий размер файла блокчейна в байтах.
Специфические коды ошибок ListBlockchainHeads
- У
ListBlockchainHeadsнет специальных прикладных ошибок при штатной работе. - Если произойдёт непредвиденная проблема, сервер вернёт общую ошибку из раздела
00, обычно500 / INTERNAL_ERROR.
4. GetSyncUserProfile
Назначение
Запрос минимального профиля пользователя для межсерверной синхронизации.
Нужен в сценарии, когда сервер во время periodic sync увидел чужой блокчейн, которого у него локально ещё нет. Вместо обращения в Solana PDA он может запросить у партнёра:
loginblockchainNamesolanaKeyblockchainKeyclientKeyblockchainSizeLimitBytes
После этого принимающий сервер может локально создать записи в solana_users и blockchain_state, а затем уже докачивать блоки через GetBlockchainBlock.
Этот запрос доступен без авторизации и предназначен именно для server-to-server sync.
Запрос
{
"op": "GetSyncUserProfile",
"requestId": "sync-user-001",
"payload": {
"login": "alice"
}
}
Успешный ответ: пользователь не найден
{
"op": "GetSyncUserProfile",
"requestId": "sync-user-001",
"status": 200,
"ok": true,
"payload": {
"exists": false
}
}
Успешный ответ: пользователь найден
{
"op": "GetSyncUserProfile",
"requestId": "sync-user-001",
"status": 200,
"ok": true,
"payload": {
"exists": true,
"login": "alice",
"blockchainName": "alice-001",
"solanaKey": "BASE64_32",
"blockchainKey": "BASE64_32",
"clientKey": "BASE64_32",
"blockchainSizeLimitBytes": 100000
}
}
Поля ответа
exists— найден ли пользователь на сервере-партнёре.login— канонический login из БД сервера-партнёра.blockchainName— имя основной цепочки пользователя.solanaKey— публичный ключ логина.blockchainKey— публичный ключ блокчейна.clientKey— публичный клиентский ключ, который в текущей модели используется при создании локальной записи.blockchainSizeLimitBytes— лимит размера файла блокчейна, который будет записан в локальныйblockchain_state.
Специфические коды ошибок GetSyncUserProfile
400 / BAD_FIELDS— пустой или некорректныйlogin.404 / BLOCKCHAIN_STATE_NOT_FOUND— пользователь найден, но на сервере-партнёре отсутствуетblockchain_stateдля его цепочки.- При непредвиденной ошибке сервер вернёт общую ошибку из раздела
00, обычно500 / INTERNAL_ERROR.
5. GetCallIceConfig
Доступно только после успешной авторизации.
Запрос
{
"op": "GetCallIceConfig",
"requestId": "ice-001",
"payload": {
}
}
Успешный ответ
{
"op": "GetCallIceConfig",
"requestId": "ice-001",
"status": 200,
"ok": true,
"payload": {
"stunUrls": ["stun:stun.example.org:3478"],
"turnUrls": ["turn:turn.example.org:3478?transport=udp"],
"turnUsername": "user",
"turnPassword": "password",
"turnServers": [
{
"id": "primary",
"urls": ["turn:turn.example.org:3478?transport=udp"],
"username": "user",
"password": "password"
}
],
"turnEnabled": true,
"generatedAtMs": 1774700000123,
"expiresAtMs": 1774700300123,
"ttlSec": 300
}
}
Специфические коды ошибок GetCallIceConfig
422 / NOT_AUTHENTICATED— требуется авторизация.
6. ClientErrorLog
Запрос
{
"op": "ClientErrorLog",
"requestId": "err-001",
"payload": {
"kind": "global_error",
"message": "TypeError: failed",
"stack": "...",
"sourceUrl": "https://shineup.me/app.js",
"lineNumber": 10,
"columnNumber": 20,
"route": "#/channel-view/own-0",
"href": "https://shineup.me/#/channel-view/own-0",
"userAgent": "...",
"clientTs": 1774700000123,
"requestOp": "GetChannelMessages",
"requestIdRef": "GetChannelMessages-123",
"contextJson": "{\"screen\":\"channels\"}"
}
}
Успешный ответ
{
"op": "ClientErrorLog",
"requestId": "err-001",
"status": 200,
"ok": true,
"payload": {
"serverTs": 1774700000456,
"accepted": true
}
}
Специфические коды ошибок ClientErrorLog
400 / BAD_FIELDS— обязательные поля ошибки не заполнены.
7. ClientDebugLog
Запрос
{
"op": "ClientDebugLog",
"requestId": "dbg-001",
"payload": {
"runId": "ui-run-1",
"level": "info",
"message": "opened channels tab",
"details": "{\"route\":\"#/channels\"}"
}
}
Успешный ответ
{
"op": "ClientDebugLog",
"requestId": "dbg-001",
"status": 200,
"ok": true,
"payload": {
"accepted": true,
"serverTs": 1774700000456
}
}
Специфические коды ошибок ClientDebugLog
400 / BAD_FIELDS— полеmessageне заполнено.
8. CallDeliveryReport
Запрос
{
"op": "CallDeliveryReport",
"requestId": "call-report-001",
"payload": {
"type": "outgoing_failed",
"value": "{\"reason\":\"ice_failed\",\"callId\":\"call-1\"}"
}
}
Успешный ответ
{
"op": "CallDeliveryReport",
"requestId": "call-report-001",
"status": 200,
"ok": true,
"payload": {
"serverTs": 1774700000456,
"accepted": true
}
}
Специфические коды ошибок CallDeliveryReport
400 / BAD_FIELDS— полеtypeне заполнено.
7. Короткое резюме
Pingнужен для keep-alive и проверки, что WebSocket-соединение живо.GetServerInfoнужен для выбора сервера в сети и показа публичной информации об узле.GetCallIceConfigнужен для WebRTC-звонков и требует авторизации.ClientErrorLog,ClientDebugLog,CallDeliveryReportиспользуются для диагностики клиента и звонков.
8. Прямое техническое сообщение в конкретную сессию
На текущий момент в публичном JSON API этого документа нет отдельного RPC для отправки произвольного технического сообщения в конкретную сессию пользователя (по sessionId).
Что уже есть в системе:
- сервер хранит
sessionIdактивной сессии; - есть
ListSessions, чтобы клиент получил список sessionId своего пользователя; - у сервера есть внутренний реестр активных WS-подключений по
sessionId.
Чего не хватает для полноценной фичи «direct tech message by sessionId»:
- отдельная API-операция (например,
SendSessionTechMessage); - правило авторизации (кто имеет право писать в чужую/свою сессию);
- унифицированный формат payload и события доставки;
- коды ошибок (
SESSION_OFFLINE,SESSION_NOT_FOUND,FORBIDDENи т.п.).
Итог: как инфраструктурная база это почти готово, но нужен отдельный RPC-слой и политика доступа.