# API для разработчиков: DM, файлы, push и сигналы звонков Документ описывает публичные операции и endpoints, связанные с личными сообщениями, файлами для DM, WebPush и сигналами звонков. Подробная логика DM и бинарного формата: `Dev_Docs/Personal_Messages/README.md`. ## 1. `UpsertPushToken` Требует авторизации. ### Запрос ```json { "op": "UpsertPushToken", "requestId": "push-upsert-001", "payload": { "sessionId": "SESSION_ID", "endpoint": "https://push.example/...", "p256dhKey": "BASE64", "authKey": "BASE64", "platform": "web", "userAgent": "Mozilla/5.0 ..." } } ``` ### Успешный ответ ```json { "op": "UpsertPushToken", "requestId": "push-upsert-001", "status": 200, "ok": true, "payload": { "tokenId": "token-1", "updatedAtMs": 1774700000123 } } ``` ## 2. `SendTestWebPush` Требует авторизации. ### Запрос ```json { "op": "SendTestWebPush", "requestId": "push-test-001", "payload": { "login": "alice", "sessionId": "SESSION_ID", "title": "Test", "text": "Push body" } } ``` ## 3. `SendMessagePair` и `ReceiveOutcomingMessage` `ReceiveOutcomingMessage` — алиас `SendMessagePair`. ### Назначение Передаёт пару signed DM-блоков: - `incomingBlobB64` — блок `type=1` или `type=3` - `outgoingBlobB64` — блок `type=2` или `type=4` Для контентных сообщений `type=1/2` внутри base64 лежит новый бинарный формат `SHiNE_DM`. ### Запрос ```json { "op": "SendMessagePair", "requestId": "dm-pair-001", "payload": { "incomingBlobB64": "BASE64_INCOMING_SIGNED_BLOCK", "outgoingBlobB64": "BASE64_OUTGOING_SIGNED_BLOCK" } } ``` ### Успешный ответ ```json { "op": "SendMessagePair", "requestId": "dm-pair-001", "status": 200, "ok": true, "payload": { "baseKey": "from|to|time|nonce", "incomingKey": "from|to|time|nonce|1", "outgoingKey": "from|to|time|nonce|2", "deliveredWsSessions": 1, "deliveredWebPushSessions": 0 } } ``` ### Ошибки - `400 / BAD_FIELDS` — пустой `incomingBlobB64` или `outgoingBlobB64` - `400 / BAD_BLOCK_FORMAT` — base64 или бинарный контейнер повреждён - `400 / BAD_CONTENT_FORMAT` — для контентного сообщения пришёл не `SHiNE_DM` - `400 / TOO_MANY_ATTACHMENTS` — больше 12 вложений - `400 / ATTACHMENT_NOT_FOUND` — сообщение ссылается на blob, которого нет на сервере - `404 / USER_NOT_FOUND` — один из логинов не найден - `460 / BAD_SIGNATURE` — подпись блока не прошла проверку ## 4. `AckSessionDelivery` Требует авторизации. Подтверждает доставку в текущую сессию. ### Запрос ```json { "op": "AckSessionDelivery", "requestId": "ack-001", "payload": { "messageKey": "from|to|time|nonce|1" } } ``` ## 5. Событие `SignedMessageArrived` Сервер присылает его по WebSocket в активные сессии адресата. ### Payload события ```json { "messageKey": "from|to|time|nonce|1", "baseKey": "from|to|time|nonce", "fromLogin": "alice", "toLogin": "bob", "targetLogin": "bob", "messageType": 1, "timeMs": 1774700000123, "nonce": 123456789, "blobB64": "BASE64_SIGNED_BLOCK", "backlog": false } ``` Если это новая ревизия того же письма, `messageKey` остаётся тем же, а `revisionTimeMs` меняется внутри бинарного блока. ## 6. HTTP `HEAD /f/` Проверка, есть ли ciphertext-файл на сервере. ### Ответы - `200` — файл существует - `404` — файла нет ## 7. HTTP `GET /f/` Отдаёт ciphertext-файл. ### Особенности - `Content-Type: application/octet-stream` - файл сейчас доступен публично - имя файла на диске и в URL — `base64url(SHA-256(ciphertext))` ## 8. HTTP `POST /upload?hash=&size=` Загружает ciphertext-файл для будущего DM. ### Тело запроса Raw bytes ciphertext-файла. ### Поведение сервера - пересчитывает `SHA-256` - сверяет размер - сохраняет blob в папку `f/`, если его ещё не было - если blob уже есть, не перезаписывает его - создаёт или обновляет запись в `dm_files` ### Успешный ответ ```json { "ok": true, "hash": "base64url_sha256", "size": 245120, "alreadyExists": false } ``` ### Ошибки - `400 / bad hash` - `400 / bad size` - `400 / SIZE_MISMATCH` - `400 / HASH_MISMATCH` - `400 / UPLOAD_TOO_LARGE` - `500 / upload_failed` ## 9. `CallInviteBroadcast` Требует авторизации. Шлёт приглашение к звонку в активные сессии `toLogin`. ## 10. `CallSignalToSession` Требует авторизации. Шлёт сигнал звонка в конкретную сессию. ## 11. Замечания - Для нового DM-файла сценарий такой: `HEAD /f/` → при `404` `POST /upload` → затем `SendMessagePair`. - Сервер хранит только последнюю версию контентного сообщения по `messageKey`. - Удаление сообщения реализуется новой ревизией с пустым телом и нулём вложений.