1
0
forked from BRT/arc
arc/docs/api/network.md

156 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NetworkService — полная карта API-вызовов
[← Главная](../index.md)
Все HTTP-вызовы, выполняемые приложением. Класс `NetworkService` (`services/net/NetworkService.h/cpp`).
---
## Аутентификация
| Метод | Путь | Слот | Описание |
|-------|------|------|----------|
| POST | `/api/v1/auth/login` | `loginAndSetup()` | Вход по `api_key`, получение токенов |
| POST | `/api/v1/auth/refresh_token` | `refreshToken()` (private) | Обновление access_token |
Токены сохраняются в `config.ini`. Авто-рефреш за 30 минут до истечения — `scheduleNextRefresh()`.
---
## Desktop
| Метод | Путь | Слот | Описание |
|-------|------|------|----------|
| POST | `/api/v1/desktop/` | `loginAndSetup()` | Создание desktop при первом запуске (если `desktop_id` не сохранён) |
Ответ: сохраняет `desktop_id` и `profile_id` в `config.ini`.
---
## Device
| Метод | Путь | Слот | Описание |
|-------|------|------|----------|
| POST | `/api/v1/device/` | `createDevice()` | Регистрация нового Android устройства |
| PATCH | `/api/v1/device/{api_id}` | `updateDevice()` | Обновление статуса/батареи устройства |
| POST | `/api/v1/device/add_screenshot/{api_id}` | `postDeviceScreenshot()` | Загрузка скриншота (`multipart/form-data`, `image/jpeg`) |
`api_id` — UUID, получаемый при создании и сохраняемый в таблице `devices`.
---
## Profile / Material
| Метод | Путь | Слот | Описание |
|-------|------|------|----------|
| POST | `/api/v1/profile/bank_profile` | `postBankProfile()` | Создать банк-профиль |
| PATCH | `/api/v1/profile/bank_profile/{id}` | `patchBankProfile()` | Обновить банк-профиль |
| POST | `/api/v1/profile/material` | `postMaterial()` | Создать материал (карту) |
`postBankProfile` сохраняет `bank_profile_id` в таблице `applications`.
`postMaterial` сохраняет `material_id` (UUID) в таблице `accounts`.
---
## Tasks
| Метод | Путь | Слот | Auth | Описание |
|-------|------|------|------|----------|
| GET | `/api/v1/task/get_tasks` | `getTasks()` | Bearer | Получить очередь задач (поллинг каждые 5 сек) |
| POST | `/api/v1/task/task_result` | `postTaskResult()` | Bearer | Отправить результат задачи |
`getTasks()` — асинхронный (без QEventLoop), использует сигнал `tasksReceived(QJsonArray)`.
`postTaskResult()` — вызывается из `sendTaskResult()` в `EventHandler` в отдельном QThread.
---
## Устаревшие эндпоинты (legacy)
Используют старый `multipart/form-data` API с токеном в теле. Пути задаются в `config.ini` напрямую как полные URL.
| Слот | Ключ в config.ini | Статус |
|------|-------------------|--------|
| `postEventStatus()` | `net/eventStatusUpdate` | ⚠️ устарел, используется в `updateEventThread` |
| `postAppStatus()` | `net/appStatusUpdate` | используется при смене статуса приложения |
| `insertTransactionData()` | `net/transactionInsert` | создание транзакции |
| `updateTransactionData()` | `net/transactionUpdate` | обновление транзакции |
| `getPayments()` | `net/paymentsGet` | ⚠️ старый поллинг задач (заменён на `getTasks`) |
---
## Конфигурация путей (`config.ini`)
```ini
[net]
apiBase = http://localhost:9999
pathAuthLogin = /api/v1/auth/login
pathAuthRefresh = /api/v1/auth/refresh_token
pathDesktopCreate = /api/v1/desktop/
pathDevice = /api/v1/device/
pathDeviceScreenshot = /api/v1/device/add_screenshot/
pathBankProfile = /api/v1/profile/bank_profile
pathMaterial = /api/v1/profile/material
pathGetTasks = /api/v1/task/get_tasks
pathTaskResult = /api/v1/task/task_result
```
Если ключ отсутствует — используется значение по умолчанию (захардкожено в конструкторе `NetworkService`).
---
## Авторизация в запросах
- **Bearer** — `Authorization: Bearer <access_token>` из `config.ini [common/access_token]`
- **Legacy** — поле `token` в multipart-теле (из `config.ini [common/token]`)
---
## Таймауты и ретраи
Каждый синхронный запрос крутится в локальном `QEventLoop` под охраной `QTimer`. Если таймер сработал раньше, чем `reply->finished` — это **client timeout**: запрос аборитится и повторяется заново.
### Параметры
| Параметр | Значение | Где |
|----------|----------|-----|
| Лимит ожидания | **30 секунд** | `kDefaultTimeoutMs` — все запросы |
| Лимит ожидания (dump) | **120 секунд** | `kDumpTimeoutMs` — только `postMonitoringDump` (большой zip-архив) |
| Макс. попыток | **3** | `kMaxRequestAttempts` — 1 основная + 2 повтора |
| Пауза между попытками | **0** | Ретрай идёт сразу после abort |
Константы и helper `awaitReply()` — в анонимном namespace в `services/net/NetworkService.cpp`.
### Когда ретраим
Ретрай срабатывает **только** при client timeout (наш `QTimer` сработал, reply не успел завершиться). На любых других исходах ретрая нет:
| Исход | Ретрай? | Почему |
|-------|---------|--------|
| Client timeout (наш QTimer) | ✅ да | сеть моргнула — стоит попробовать |
| HTTP 4xx (401/404/409/…) | ❌ нет | детерминированная ошибка, повтор не поможет |
| HTTP 5xx | ❌ нет | сервер ответил — это бизнес-сбой, не транспортный |
| `ConnectionRefused` / DNS / SSL | ❌ нет | повтор не починит инфраструктурную проблему |
| `success: false` в JSON | ❌ нет | прикладная ошибка |
После каждой неудачной попытки в лог пишется `Network timeout (POST) attempt N/3: <details>`; если все 3 попытки таймаутнулись — финальная строка `Timeout (POST) after 3 attempts: <details>` плюс запись в `GeneralLogDAO` (для `postJson`/`patchJson`).
### Покрытие методов
| Метод | Ретрай | Примечание |
|-------|--------|------------|
| `postJson` | ✅ | |
| `patchJson` | ✅ | |
| `getJson` | ✅ | |
| `deleteJson` | ✅ | |
| `get(path, params)` | ✅ | используется `getPayments()` (legacy) |
| `syncBanksIfEmpty()` инлайн-GET | ✅ | |
| `postDeviceScreenshot()` | ✅ | multipart пересобирается на каждой попытке |
| `postMonitoringLog()` | ✅ | multipart пересобирается на каждой попытке |
| `postMonitoringDump()` | ✅ | timeout 120s; multipart пересобирается |
| `post(QHttpMultiPart*, path)` | ❌ | multipart передаётся снаружи и удаляется вместе с `reply`; колл-сайты (`postEventStatus`/`insertTransactionData`/…) — legacy |
| `getTasks()` | ❌ | асинхронный (без `QEventLoop`); таймаут просто аборитит reply |
### Идемпотентность
⚠️ POST-запросы, создающие ресурсы (`/desktop/`, `/device/`, `/profile/bank_profile`, `/profile/material`), при ретрае могут создать дубль, если первая попытка дошла до сервера, но ответ потерялся. Сейчас защиты от этого нет — полагаемся на то, что 3 timeout-а подряд это редкая ситуация. Если станет проблемой — добавлять идемпотентные ключи на стороне бэкенда.