# Требования к серверу 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 будет корректно получать и обрабатывать список приложений без изменений в коде клиента.