97 lines
8.1 KiB
Markdown
97 lines
8.1 KiB
Markdown
# Требования к серверу App Store для совместимости с F7cloud
|
||
|
||
Чтобы клиент F7cloud корректно работал с вашим appstore, сервер должен соблюдать следующие условия.
|
||
|
||
**Изменения в клиенте** (для совместимости с кастомным appstore) зарегистрированы в [CHANGELOG-APPSTORE.md](CHANGELOG-APPSTORE.md).
|
||
|
||
---
|
||
|
||
## 1. Формат тела ответа `GET /api/v1/apps.json`
|
||
|
||
**Клиент делает:**
|
||
`responseJson['data'] = json_decode($response->getBody(), true)`
|
||
То есть в `data` попадает **весь** распарсенный JSON-ответ.
|
||
|
||
**Сейчас у вас:**
|
||
Тело ответа: `{"data": [{"id": "spreed", "releases": [...], ...}]}`
|
||
После парсинга в `data` попадает объект `{ data: [ ... ] }`. В коде клиента по этому объекту идёт цикл `foreach ($response['data'] as $app)`, и в первой итерации `$app` оказывается **массивом приложений**, а не одним приложением. У массива нет ключа `releases` → ошибка.
|
||
|
||
**Что нужно на appstore:**
|
||
Тело ответа должно быть **массивом приложений в корне**, без обёртки `{"data": ...}`.
|
||
|
||
**Неправильно (как сейчас):**
|
||
```json
|
||
{"data":[{"id":"spreed","releases":[...],...}]}
|
||
```
|
||
|
||
**Правильно:**
|
||
```json
|
||
[{"id":"spreed","releases":[...],...}]
|
||
```
|
||
|
||
То есть ответ `GET /api/v1/apps.json` должен отдавать **JSON-массив** приложений, а не объект с полем `data`.
|
||
|
||
---
|
||
|
||
## 2. Ответ 200 и автоматическое заполнение кэша
|
||
|
||
Кэш на стороне F7cloud **заполняется сам**: при ответе **200 OK** с телом клиент сохраняет полученный JSON в свой кэш. Вручную заполнять кэш на клиенте не нужно — достаточно, чтобы appstore отдавал 200 с полным массивом приложений.
|
||
|
||
### Ответ 304 Not Modified
|
||
|
||
При ответе **304 Not Modified** клиент **не получает** новое тело, а подставляет в `data` своё сохранённое содержимое кэша. Если кэша ещё не было (первый запрос или кэш пустой), подставляется пустая строка → `json_decode('')` → `null` → пустой список приложений и ошибка «приложение не найдено».
|
||
|
||
**Что нужно на appstore:**
|
||
Для `GET /api/v1/apps.json` **не отдавать 304**, когда у клиента ещё нет валидного кэша. Проще всего: **по умолчанию всегда отдавать 200** с полным телом (полным массивом приложений). Тогда клиент при первом же запросе получит данные и сам заполнит свой кэш.
|
||
Если решите поддерживать 304, отдавайте его **только** когда запрос содержит заголовок `If-None-Match` с актуальным ETag и контент не менялся.
|
||
|
||
---
|
||
|
||
## 3. Структура каждого приложения в массиве
|
||
|
||
У **каждого** элемента массива приложений должно быть поле **`releases`** — массив (хотя бы пустой `[]`). Клиент делает `foreach ($app['releases'] as $release)` без проверки; при отсутствии или `null` возникает ошибка.
|
||
|
||
**Обязательно у каждого приложения:**
|
||
- `id` — идентификатор приложения
|
||
- `releases` — массив релизов (минимум `[]`)
|
||
- в каждом элементе `releases` — поля в формате F7cloud (version, download, signature, certificate и т.д.)
|
||
|
||
---
|
||
|
||
## 4. Заголовки ответа
|
||
|
||
- **Content-Type:** `application/json`
|
||
- При необходимости кэширования по ETag — заголовок **ETag** в ответе 200. Тогда клиент сможет при следующем запросе передать `If-None-Match` и при неизменном контенте получить 304 (с учётом п. 2 — только когда у клиента уже есть валидный кэш).
|
||
|
||
---
|
||
|
||
## 5. Сертификат и подпись: символы `\r\n` (перенос строки)
|
||
|
||
В ответе у вас в полях **`certificate`** и **`signature`** встречаются последовательности **`\r\n`** (CRLF — перевод строки в стиле Windows). В JSON они приходят как экранированные `\r\n` и после `json_decode()` превращаются в реальные символы CR и LF в строке.
|
||
|
||
**Как ведёт себя F7cloud (без изменений в коде):**
|
||
|
||
- **Сертификат** — передаётся как есть в `openssl_get_publickey()`, `openssl_x509_parse()` и в phpseclib `loadX509()`. Формат PEM допускает и `\n` (LF), и `\r\n` (CRLF) между строками. OpenSSL такие сертификаты обрабатывает нормально, **проблемой это не является**.
|
||
|
||
- **Подпись** — строка передаётся в `base64_decode($app['releases'][0]['signature'])`. В PHP в режиме по умолчанию `base64_decode()` **игнорирует пробельные символы** (в т.ч. `\r`, `\n`, пробелы) внутри base64-строки (по RFC 2045). То есть подпись с переносами строк **обрабатывается корректно**.
|
||
|
||
**Итог:** F7cloud такие значения обрабатывает, менять что-то на клиенте из-за `\r\n` не нужно.
|
||
|
||
**Рекомендация для appstore (по желанию):** для единообразия и совместимости с разными клиентами можно:
|
||
- в **сертификате** (PEM) использовать только **`\n`** (LF), без `\r`;
|
||
- в **подписи** (base64) хранить/отдавать одну строку **без переносов** (как одну длинную base64-строку).
|
||
|
||
Это не обязательно для F7cloud, но упрощает отладку и совместимость с другими системами.
|
||
|
||
---
|
||
|
||
## Краткий чек-лист для сервера appstore
|
||
|
||
1. **Тело `GET /api/v1/apps.json`** — JSON-массив приложений в корне: `[{...}, {...}]`, а не `{"data": [...]}`.
|
||
2. **По умолчанию отдавать 200** с полным массивом приложений — кэш на стороне F7cloud заполнится сам. 304 отдавать только при наличии у клиента валидного кэша (If-None-Match + неизменный контент).
|
||
3. У **каждого** приложения в массиве есть поле **`releases`** (массив, не null и не отсутствует).
|
||
4. Ответ с **Content-Type: application/json**.
|
||
5. **`\r\n` в сертификате и подписи** — F7cloud обрабатывает (OpenSSL принимает CRLF в PEM, base64_decode игнорирует пробелы). Менять не обязательно; при желании можно отдавать сертификат с `\n` и подпись без переносов.
|
||
|
||
После этих изменений на стороне appstore клиент F7cloud будет корректно получать и обрабатывать список приложений без изменений в коде клиента.
|