Condo API предоставляет безопасный способ работы с файлами через REST эндпоинты
/api/files, основанный на подписи (signature)GraphQL Upload помечен как deprecated и будет удален в будущем. Пожалуйста, мигрируйте на новый flow с использованием подписи.
API работы с файлами
API загрузки файлов использует специальные REST эндпоинты (
/api/files) и требует client_id, который ранее был зарегистрирован на файловом сервере.
Этот подход обеспечивает лучшую безопасность, ограничение частоты запросов, а также возможности для переиспользования ранее загруженных файлов.Начало работы
Для использования данного API вам необходим
fileClientId. fileClientId — это уникальный идентификатор вашего приложения, предоставляемый вашим партнером.На клиентской стороне вам нужен только
fileClientId (обычно доступен из next/config или переменных окружения). Вам не нужно знать о секрете или валидации подписи — это обрабатывается Condo API.Если ваше приложение имеет собственный бэкенд и вам необходимо верифицировать подписи, полученные от Condo API, вы должны разместить переменную окружения
FILE_SECRET на вашем бэкенде. FILE_SECRET используется для шифрования и дешифрования подписей метаданных файла.Загрузка файлов
Используйте пакет
@open-condo/files для загрузки файлов:Пример TypeScript
Пример JavaScript
Пример cURL
Ответ
typescriptimport { upload, buildMeta } from '@open-condo/files' import { getClientSideSenderInfo } from '@open-condo/miniapp-utils/helpers/sender' import getConfig from 'next/config' const { publicRuntimeConfig: { fileClientId } } = getConfig() async function uploadFile(file: File, userId: string, organizationId?: string) { const senderInfo = getClientSideSenderInfo() const uploadResult = await upload({ files: [file], meta: buildMeta({ userId, // ID пользователя, который будет привязывать файл к модели fileClientId, // client_id, который станет владельцем данного файла modelNames: ['Document'], // Модели, к которым можно прикрепить этот файл fingerprint: senderInfo.fingerprint, organizationId, // Организация пользователя, к которой будет привязан загруженный файл }), }) // uploadResult.files[0].signature содержит JWT подпись // Используйте эту подпись в вашей GraphQL мутации return uploadResult.files[0].signature }
Структура upload meta
Каждый запрос загрузки отправляет JSON-поле
meta (его формирует buildMeta), в котором описаны контекст и ограничения для файла:json{ "dv": 1, "sender": { "dv": 1, "fingerprint": "<client-fingerprint>" }, "user": { "id": "<current-user-id>" }, "fileClientId": "<your-client-id>", "modelNames": ["Document", "..."], "organization": { "id": "<organization-id>" } }
dv— версия структуры данных (сейчас всегда1)sender— идентификатор клиента, инициировавшего запросuser— пользователь, которому принадлежит файлfileClientId— выданный вам идентификатор приложенияmodelNames— список GraphQL моделей, которым разрешено использовать файлorganization— идентификатор организации, в рамках которой загружается файл
Если вы передаёте необязательное поле
attach, оно должно выглядеть так:json{ "dv": 1, "sender": { "dv": 1, "fingerprint": "<client-fingerprint>" }, "modelName": "Document", "itemId": "<model-record-id>" }
Таким образом API может сразу прикрепить загруженный файл к указанной записи и вернуть
publicSignature — зашифрованные метаданные, которые можно повторно использовать для обращения к файлу.Как работает подпись
Каждому загруженному файлу соответствуют JWT-токены (
signature и publicSignature), подписанные вашим FILE_SECRET алгоритмом HS256. Время жизни каждого токена — 5 минут (exp = iat + 300 секунд).Пример полезной нагрузки для
signature:json{ "id": "<storage-id>", "recordId": "<FileRecord.id>", "filename": "<generated-name>", "originalFilename": "<original name>", "mimetype": "<mime>", "encoding": "<encoding>", "meta": { "dv": 1, "sender": { "dv": 1, "fingerprint": "<string>" }, "user": { "id": "<uuid>" }, "fileClientId": "<client_id>", "modelNames": ["Document"], "sourceFileClientId": "<string|null>" }, "iat": 1700000000, "exp": 1700000300 }
publicSignature содержит тот же публичный набор метаданных, что и ответ эндпоинта /api/files/attach, поэтому его можно безопасно хранить в вашей базе данных.Если вам нужно валидировать подпись на своём бэкенде:
- Сохраните
FILE_SECRETв переменных окружения сервера - Выполните
jwt.verify(signature, process.env.FILE_SECRET, { algorithms: ['HS256'] }) - Проверьте расшифрованные данные (например,
modelNames,user.idилиfileClientId)
Никогда не передавайте
FILE_SECRET в браузер: фронтенд использует только fileClientId и токены, полученные от Condo API.Прикрепление файлов к моделям
После загрузки используйте
signature в вашей GraphQL мутации:GraphQL мутация
Ответ
graphqlmutation CreateDocument($data: DocumentCreateInput!) { obj: createDocument(data: $data) { id file { filename mimetype publicUrl } } }
Входные данные мутации должны включать:
json{ "data": { "file": { "signature": "<jwt-signature-from-upload>", "originalFilename": "document.pdf" }, "name": "Мой документ", "organization": { "connect": { "id": "..." } } } }
Встроенное прикрепление
Вы можете прикрепить файл к модели во время загрузки в одном запросе:
Пример TypeScript
typescriptimport { upload, buildMeta } from '@open-condo/files' const uploadResult = await upload({ files: [file], meta: buildMeta({ userId, fileClientId, modelNames: ['Document'], fingerprint: senderInfo.fingerprint, organizationId, }), attach: { dv: 1, sender: { dv: 1, fingerprint: senderInfo.fingerprint }, modelName: 'Document', itemId: 'existing-document-id', }, }) // uploadResult.files[0].publicSignature можно использовать напрямую // Нет необходимости вызывать /api/files/attach отдельно
Совместное использование файлов
Поделитесь загруженным файлом с другим пользователем или приложением:
Пример TypeScript
Пример cURL
typescriptimport { share } from '@open-condo/files' const shareResult = await share({ payload: { dv: 1, sender: { dv: 1, fingerprint: senderInfo.fingerprint }, id: 'existing-file-id', user: { id: 'target-user-id' }, fileClientId: 'target-app-id', modelNames: ['Document'], }, }) // shareResult.file.signature содержит новую подпись
Прикрепление существующих файлов
Прикрепите ранее загруженный файл к модели:
Пример TypeScript
Пример cURL
typescriptimport { attach } from '@open-condo/files' const attachResult = await attach({ payload: { dv: 1, sender: { dv: 1, fingerprint: senderInfo.fingerprint }, modelName: 'Document', itemId: 'document-id', fileClientId, signature: 'upload-or-share-signature', }, }) // attachResult.file.signature содержит публичную подпись
Скачивание файлов
Файлы можно скачать двумя способами:
- Используя publicUrl из GraphQL ответа — при запросе модели с полем файла вы получаете
publicUrl, который можно использовать напрямую:
GraphQL запрос
Использование
graphqlquery GetDocument($id: ID!) { obj: document(where: { id: $id }) { id file { filename mimetype publicUrl } } }
- Используя API эндпоинт файлов — скачайте файл напрямую, используя его ID и подпись:
GET /api/files/<file_id>?sign=<file_signature>
Подпись можно получить из ответа загрузки или из метаданных файла.
API эндпоинты
Новый flow использует следующие REST эндпоинты:
POST /api/files/upload— загрузка одного или нескольких файловPOST /api/files/share— совместное использование существующего файлаPOST /api/files/attach— прикрепление файла к моделиGET /api/files/<file_id>?sign=<signature>— скачивание файла
Все эндпоинты требуют аутентификации через cookie сессии / Authorization заголовок (так же, как GraphQL API).