SHA256
НЕ ПРОВЕРЕНО: DM-вложения, upload файлов и ревизии личных сообщений
This commit is contained in:
@@ -61,5 +61,6 @@
|
||||
## Важные замечания
|
||||
|
||||
- `ReceiveOutcomingMessage` сейчас зарегистрирован как алиас того же handler/request-класса, что и `SendMessagePair`.
|
||||
- HTTP endpoints для DM-файлов (`HEAD/GET /f/<hashB64url>` и `POST /upload`) не являются WebSocket `op`, поэтому в таблицу выше не входят; они описаны в `12_Direct_Messages_Push_Calls_API.md`.
|
||||
- Классы `Net_MarkChannelMessagesSeen_*` существуют в коде, но операция `MarkChannelMessagesSeen` не зарегистрирована в `JsonHandlerRegistry`, поэтому в публичный список API не входит.
|
||||
- HTTP debug endpoints из `src/main/java/server/debug/` не входят в этот индекс WebSocket `op`; они описаны отдельно в `13_HTTP_Debug_API.md`.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# API для разработчиков: DM, push и сигналы звонков
|
||||
# API для разработчиков: DM, файлы, push и сигналы звонков
|
||||
|
||||
Документ описывает WebSocket-операции для подписанных личных сообщений, WebPush и realtime-сигналов звонков.
|
||||
Документ описывает публичные операции и endpoints, связанные с личными сообщениями, файлами для DM, WebPush и сигналами звонков.
|
||||
|
||||
Логика личных сообщений дополнительно описана в `Dev_Docs/Personal_Messages/README.md`; этот файл фиксирует именно публичные `op`, поля запросов и поля ответов.
|
||||
Подробная логика DM и бинарного формата: `Dev_Docs/Personal_Messages/README.md`.
|
||||
|
||||
## 1. `UpsertPushToken`
|
||||
|
||||
@@ -40,11 +40,9 @@
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. `SendTestWebPush`
|
||||
|
||||
Требует авторизации. Если `login` передан, он должен совпадать с логином текущей сессии.
|
||||
Требует авторизации.
|
||||
|
||||
### Запрос
|
||||
|
||||
@@ -61,65 +59,18 @@
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
## 3. `SendMessagePair` и `ReceiveOutcomingMessage`
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "SendTestWebPush",
|
||||
"requestId": "push-test-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"targetLogin": "alice",
|
||||
"attemptedSessions": 1,
|
||||
"sessionsWithPushConfig": 1,
|
||||
"delivered": 1,
|
||||
"failed": 0,
|
||||
"sentAtMs": 1774700000123
|
||||
}
|
||||
}
|
||||
```
|
||||
`ReceiveOutcomingMessage` — алиас `SendMessagePair`.
|
||||
|
||||
---
|
||||
### Назначение
|
||||
|
||||
## 3. `SendDirectMessage`
|
||||
Передаёт пару signed DM-блоков:
|
||||
|
||||
Отправляет один подписанный DM-пакет.
|
||||
- `incomingBlobB64` — блок `type=1` или `type=3`
|
||||
- `outgoingBlobB64` — блок `type=2` или `type=4`
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "SendDirectMessage",
|
||||
"requestId": "dm-001",
|
||||
"payload": {
|
||||
"blobB64": "BASE64_SIGNED_DM_PACKET"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "SendDirectMessage",
|
||||
"requestId": "dm-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"messageId": "dm-1",
|
||||
"deliveredWsSessions": 1,
|
||||
"deliveredWebPushSessions": 0,
|
||||
"sessionNotFound": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. `SendMessagePair` и `ReceiveOutcomingMessage`
|
||||
|
||||
`ReceiveOutcomingMessage` сейчас является алиасом `SendMessagePair` и использует тот же request/handler.
|
||||
Для контентных сообщений `type=1/2` внутри base64 лежит новый бинарный формат `SHiNE_DM`.
|
||||
|
||||
### Запрос
|
||||
|
||||
@@ -143,55 +94,28 @@
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"baseKey": "base-key",
|
||||
"incomingKey": "incoming-key",
|
||||
"outgoingKey": "outgoing-key",
|
||||
"baseKey": "from|to|time|nonce",
|
||||
"incomingKey": "from|to|time|nonce|1",
|
||||
"outgoingKey": "from|to|time|nonce|2",
|
||||
"deliveredWsSessions": 1,
|
||||
"deliveredWebPushSessions": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
### Ошибки
|
||||
|
||||
## 5. `ReceiveIncomingMessage`
|
||||
- `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` — подпись блока не прошла проверку
|
||||
|
||||
Принимает входящий подписанный DM-блок.
|
||||
## 4. `AckSessionDelivery`
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "ReceiveIncomingMessage",
|
||||
"requestId": "dm-in-001",
|
||||
"payload": {
|
||||
"incomingBlobB64": "BASE64_INCOMING_SIGNED_BLOCK"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "ReceiveIncomingMessage",
|
||||
"requestId": "dm-in-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"messageKey": "incoming-key",
|
||||
"baseKey": "base-key",
|
||||
"deliveredWsSessions": 1,
|
||||
"deliveredWebPushSessions": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. `AckSessionDelivery`
|
||||
|
||||
Требует авторизации. Подтверждает доставку сообщения в текущую сессию.
|
||||
Требует авторизации. Подтверждает доставку в текущую сессию.
|
||||
|
||||
### Запрос
|
||||
|
||||
@@ -200,107 +124,99 @@
|
||||
"op": "AckSessionDelivery",
|
||||
"requestId": "ack-001",
|
||||
"payload": {
|
||||
"messageKey": "incoming-key"
|
||||
"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/<hashB64url>`
|
||||
|
||||
Проверка, есть ли ciphertext-файл на сервере.
|
||||
|
||||
### Ответы
|
||||
|
||||
- `200` — файл существует
|
||||
- `404` — файла нет
|
||||
|
||||
## 7. HTTP `GET /f/<hashB64url>`
|
||||
|
||||
Отдаёт ciphertext-файл.
|
||||
|
||||
### Особенности
|
||||
|
||||
- `Content-Type: application/octet-stream`
|
||||
- файл сейчас доступен публично
|
||||
- имя файла на диске и в URL — `base64url(SHA-256(ciphertext))`
|
||||
|
||||
## 8. HTTP `POST /upload?hash=<hashB64url>&size=<bytes>`
|
||||
|
||||
Загружает ciphertext-файл для будущего DM.
|
||||
|
||||
### Тело запроса
|
||||
|
||||
Raw bytes ciphertext-файла.
|
||||
|
||||
### Поведение сервера
|
||||
|
||||
- пересчитывает `SHA-256`
|
||||
- сверяет размер
|
||||
- сохраняет blob в папку `f/`, если его ещё не было
|
||||
- если blob уже есть, не перезаписывает его
|
||||
- создаёт или обновляет запись в `dm_files`
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "AckSessionDelivery",
|
||||
"requestId": "ack-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"messageKey": "incoming-key"
|
||||
}
|
||||
"hash": "base64url_sha256",
|
||||
"size": 245120,
|
||||
"alreadyExists": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
### Ошибки
|
||||
|
||||
## 7. `CallInviteBroadcast`
|
||||
- `400 / bad hash`
|
||||
- `400 / bad size`
|
||||
- `400 / SIZE_MISMATCH`
|
||||
- `400 / HASH_MISMATCH`
|
||||
- `400 / UPLOAD_TOO_LARGE`
|
||||
- `500 / upload_failed`
|
||||
|
||||
Требует авторизации. Отправляет приглашение к звонку на активные сессии пользователя `toLogin`.
|
||||
## 9. `CallInviteBroadcast`
|
||||
|
||||
### Запрос
|
||||
Требует авторизации. Шлёт приглашение к звонку в активные сессии `toLogin`.
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "CallInviteBroadcast",
|
||||
"requestId": "call-invite-001",
|
||||
"payload": {
|
||||
"toLogin": "bob",
|
||||
"callId": "call-1",
|
||||
"type": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
## 10. `CallSignalToSession`
|
||||
|
||||
### Успешный ответ
|
||||
Требует авторизации. Шлёт сигнал звонка в конкретную сессию.
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "CallInviteBroadcast",
|
||||
"requestId": "call-invite-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"callId": "call-1",
|
||||
"deliveredWsSessions": 1,
|
||||
"deliveredFcmSessions": 0,
|
||||
"deliveredWebPushSessions": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
## 11. Замечания
|
||||
|
||||
---
|
||||
|
||||
## 8. `CallSignalToSession`
|
||||
|
||||
Требует авторизации. Отправляет сигнал звонка в конкретную сессию получателя.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "CallSignalToSession",
|
||||
"requestId": "call-signal-001",
|
||||
"payload": {
|
||||
"toLogin": "bob",
|
||||
"targetSessionId": "SESSION_ID",
|
||||
"callId": "call-1",
|
||||
"type": 101,
|
||||
"data": "{\"sdp\":\"...\"}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "CallSignalToSession",
|
||||
"requestId": "call-signal-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"delivered": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если целевая сессия не найдена или доставка не удалась, сервер может вернуть `404`.
|
||||
|
||||
## Типовые ошибки
|
||||
|
||||
- `422 / NOT_AUTHENTICATED` — требуется авторизация.
|
||||
- `400 / BAD_FIELDS` — не заполнены обязательные поля.
|
||||
- `404 / USER_NOT_FOUND` — пользователь не найден.
|
||||
- `404 / SESSION_NOT_FOUND` — сессия не найдена.
|
||||
- `422 / BAD_SIGNATURE` — подпись DM не прошла проверку.
|
||||
- `422 / BAD_DEVICE_KEY` — некорректный device key отправителя.
|
||||
- `422 / BAD_TIME_WINDOW` — время подписанного сообщения вне допустимого окна.
|
||||
- `422 / REPLAY` — повторное сообщение заблокировано.
|
||||
- Для нового DM-файла сценарий такой: `HEAD /f/<hash>` → при `404` `POST /upload` → затем `SendMessagePair`.
|
||||
- Сервер хранит только последнюю версию контентного сообщения по `messageKey`.
|
||||
- Удаление сообщения реализуется новой ревизией с пустым телом и нулём вложений.
|
||||
|
||||
Reference in New Issue
Block a user