API: Библиотеки документов и файлы
Требуется авторизация. Права наследуются от узла-родителя (nodeId).
Библиотеки — /api/v1/libraries
Заголовок раздела «Библиотеки — /api/v1/libraries»| Метод | Путь | Право на узел | Описание |
|---|---|---|---|
| GET | /libraries?nodeId={uuid} | view | Библиотеки узла |
| POST | /libraries | edit | Создать библиотеку |
| GET | /libraries/{id} | view | Одна библиотека |
| PATCH | /libraries/{id} | edit | Обновить |
| DELETE | /libraries/{id} | delete | Удалить в корзину узла (снимок библиотеки целиком) |
| POST | /libraries/{id}/recycle-content | delete | Переместить все корневые файлы/папки в корзину (библиотека остаётся) |
DELETE /libraries/{id} — мягкое удаление: одна запись корзины с представлениями и текущим содержимым. Файлы этой библиотеки, уже лежавшие в корзине, purge. Восстановление — POST /nodes/{nodeId}/recycle-bin/{entryId}/restore. См. Корзина.
Тело создания:
{ "nodeId": "uuid", "title": "Документы", "slug": "dokumenty", "description": ""}В ответе GET / после PATCH также: search_enabled (по умолчанию true) — участие библиотеки и файлов в глобальном поиске портала; office_open_mode (download | portal_office | client, по умолчанию download) — как открывать офисные файлы по клику; office_enabled — включён ли Portal Office на установке. GET /libraries/{id} отдаёт fields[] — схему настраиваемых полей (как у списков).
Поля библиотеки
Заголовок раздела «Поля библиотеки»Схема полей копирует контракт списков: те же типы (text, multiline_text, html_text, choice, number, date, datetime, boolean, lookup, person, link), те же форматы значений. Lookup указывает на список (settings.lookupListId) или на библиотеку (settings.lookupLibraryId) — ровно один источник. Системные колонки (name, item_type, mime_type, file_size, uploaded_by, version_number, item_number, created_at, updated_at, parent_id, …) нельзя создать как internal_name.
| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET | /libraries/{id}/fields | view | Схема полей |
| POST | /libraries/{id}/fields | portal admin + edit | Создать поле |
| PATCH | /libraries/{id}/fields/{fieldId} | portal admin + edit | Изменить поле (в т.ч. тип и isRequired) |
| DELETE | /libraries/{id}/fields/{fieldId} | portal admin + edit | Удалить поле и значения во всех файлах/папках |
| GET | /libraries/{id}/person-options | view | Поиск пользователей/групп для person-поля |
| GET | /libraries/{id}/lookup-options | view | Варианты lookup (targetListId или targetLibraryId) |
Значения: Поля библиотек. UI: вкладка Поля в настройках библиотеки.
Участие в глобальном поиске
Заголовок раздела «Участие в глобальном поиске»Настройки библиотеки → Общие → Участвует в поиске, либо:
PATCH /api/v1/libraries/{libraryId}Content-Type: application/json
{ "searchEnabled": false }Изменение searchEnabled — только администратор портала. При выключении библиотека и файлы сразу убираются из индекса. См. Поиск.
Как открывать документы Office
Заголовок раздела «Как открывать документы Office»Настройки библиотеки → Общие → Как открывать документы Office, либо:
PATCH /api/v1/libraries/{libraryId}Content-Type: application/json
{ "officeOpenMode": "client" }| Значение | Клик по имени | Portal Office нужен |
|---|---|---|
download | Скачать файл (по умолчанию) | Нет |
portal_office | Сессия WOPI в браузере | Да; иначе PATCH → 400 |
client | URI-схема установленного Office + WebDAV | Нет |
Клик по имени следует этой настройке; в меню строки остаются все доступные действия. Настройка на библиотеку, не на установку.
Пользовательская инструкция: Открытие документов Office.
Файлы и папки — /api/v1/libraries/{libraryId}/files
Заголовок раздела «Файлы и папки — /api/v1/libraries/{libraryId}/files»| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET | /files?parentId={uuid} | view | Содержимое папки (без parentId — корень) |
| GET | /files/live | view | SSE: версия и checkout файла после Save (без polling) |
| GET | /files/{fileRef} | view | Один файл/папка (номер или GUID), включая field_values |
| POST | /files/folders | add | Создать папку (опц. fieldValues) |
| POST | /files/upload | add | Загрузить файл (multipart, поле file, опц. parentId, опц. fieldValues) |
| PATCH | /files/{fileId} | edit | Переименовать / переместить / fieldValues (merge + required) |
| DELETE | /files/{fileId} | delete | Удалить файл или папку (рекурсивно) |
| GET | /files/{fileRef}/permission-settings | view | Настройки наследования прав (inheritsPermissions, parentLabel) |
| PATCH | /files/{fileRef}/permission-settings | admin | Включить/отключить наследование: { "inheritFromParent": true|false } |
| GET / HEAD | /files/{fileRef}/thumbnail | view | Миниатюра изображения для сетки и колонки «Предпросмотр» |
Для fileRef допускается числовой id или guid. Управление наследованием — только администратор портала. Явные права на файл/папку — через /api/v1/permissions с resourceType=file и libraryId.
Ответ GET /files включает meta.breadcrumb — цепочка папок до текущей. Каждый элемент содержит inherits_permissions и permissions (canEdit, canDelete, canManagePermissions).
Выборка файлов (GET)
Заголовок раздела «Выборка файлов (GET)»GET /api/v1/libraries/{libraryId}/files?parentId={uuid}&page=1&limit=50&viewId={uuid}&sort=...&orderBy=...&filter=...&q=...| Параметр | Описание |
|---|---|
parentId | UUID папки (без параметра — корень библиотеки) |
page | Номер страницы (по умолчанию 1) |
limit | Размер страницы (1–500; иначе из pageSize представления) |
viewId | UUID представления (иначе — default view) |
sort | Override сортировки: JSON-массив или column:asc,column2:desc |
orderBy | То же, что sort (приоритет у orderBy, если указаны оба) |
filter | Override фильтра (JSON); объединяется с фильтром представления через AND |
q | Текстовый поиск (OR по колонкам, без учёта регистра) |
Ответ meta: page, limit, total, totalPages, totalInView, groups, columns, view, breadcrumb, officeEnabled, officeOpenMode.
Фильтрация и сортировка по поддерживаемым условиям выполняются на уровне PostgreSQL (library_files). Сценарии с groupBy используют in-memory fallback.
Колонки фильтра
Заголовок раздела «Колонки фильтра»Системные: name, item_type, file_size, mime_type, updated_at, created_at, uploaded_by, version_number, item_number. Кастомные поля — по internal_name или UUID (как у списков). Поиск q также смотрит в field_values.
Формат фильтра — тот же JSON, что у списков:
{ "logic": "and", "conditions": [ { "column": "item_type", "operator": "eq", "value": "file" }, { "column": "name", "operator": "contains", "value": "договор" } ]}Пример — только PDF в корне библиотеки:
GET /api/v1/libraries/{libraryId}/files?filter={"logic":"and","conditions":[{"column":"item_type","operator":"eq","value":"file"},{"column":"mime_type","operator":"eq","value":"application/pdf"}]}&limit=50Сортировка и представления
Заголовок раздела «Сортировка и представления»По умолчанию папки всегда отображаются выше файлов; внутри каждой группы — сортировка по алфавиту (name). Дополнительные правила сортировки из config.sort применяются после группировки по типу.
Дефолтный config представления библиотеки:
{ "columns": ["item_number", "name", "file_size", "updated_at"], "sort": [ { "column": "item_type", "direction": "desc" }, { "column": "name", "direction": "asc" } ], "selectionMode": "multiple", "showActionsColumn": true, "pageSize": 50}selectionMode: none | single | multiple — чекбоксы в табличном режиме и массовое удаление (клиент вызывает DELETE для каждого выбранного fileRef). При удалении папки весь вложенный subtree уходит в корзину узла одной операцией.
showActionsColumn: true | false — показывать колонку контекстного меню (⋮) в таблице (по умолчанию true). Порядок колонок: при выборе — чекбокс → действия → …; без выбора — действия → ….
Если в папке есть изображения, доступен режим сетки (?display=grid|table в URL): картинки — превью, папки и остальные файлы — крупные иконки по типу. Чекбоксы и массовое удаление — только в табличном режиме (display=table). Превью картинок в сетке и таблице запрашивает /files/{fileRef}/thumbnail (маленький JPEG), а не оригинал.
Скачивание — /api/v1/files/{id}/download
Заголовок раздела «Скачивание — /api/v1/files/{id}/download»Прокси через API. Клиент не получает прямых URL S3.
Максимальный размер файла: 4 ГБ.
По умолчанию ответ отдаётся с Content-Disposition: attachment (сохранение файла). Для встроенного превью/плеера передайте ?inline=1 — тогда Content-Disposition: inline. Без inline аудио и видео тоже скачиваются как вложение (как пункт Скачать в меню ⋮ библиотеки), а не открываются как превью.
Миниатюры — /api/v1/libraries/{libraryId}/files/{fileRef}/thumbnail
Заголовок раздела «Миниатюры — /api/v1/libraries/{libraryId}/files/{fileRef}/thumbnail»Для сетки и колонки «Предпросмотр» клиент запрашивает этот маршрут, а не /download. Полноразмерный файл открывается только в просмотрщике по клику (/download).
| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET / HEAD | /libraries/{libraryId}/files/{fileRef}/thumbnail | view | JPEG-миниатюра (длинная сторона 320px) или оригинал SVG |
Поведение:
- те же права, что у скачивания (
viewна библиотеку и файл); - ответ
Content-Disposition: inline,Cache-Control: private, max-age=86400; - SVG отдаётся как есть (без растеризации);
- не-изображение, слишком большой растр (>40 Мп) или неподдерживаемый формат —
404; - миниатюра кэшируется в объектном хранилище рядом с оригиналом (
…/_thumb.jpg) и строится при загрузке файла (и при новой версии / restore); - если миниатюры ещё нет (старые файлы, сбой при загрузке), GET
/thumbnailстроит её при первом открытии папки.
Версионирование — /api/v1/libraries/{libraryId}/files/{fileId}/versions
Заголовок раздела «Версионирование — /api/v1/libraries/{libraryId}/files/{fileId}/versions»| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET | /files/{fileId}/versions | view | Текущая версия и история |
| POST | /files/{fileId}/versions | edit | Загрузить новую версию (multipart: file, опц. comment) |
| GET | /files/{fileId}/versions/{versionId}/download | view | Скачать архивную версию |
| POST | /files/{fileId}/versions/{versionId}/restore | edit | Восстановить версию как текущую |
При загрузке новой версии предыдущее содержимое сохраняется в library_file_versions. Текущая версия — в library_files.version_number.
Portal Office (WOPI) — онлайн-просмотр и редактирование
Заголовок раздела «Portal Office (WOPI) — онлайн-просмотр и редактирование»Требуется PORTAL_OFFICE_ENABLED=true и запущенный Portal Office (portal/office, см. Portal Office).
| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET | /libraries/{libraryId}/files/{fileId}/office/session?mode=view|edit | view / edit | URL iframe Portal Office + WOPI token (accessToken, accessTokenTtl) |
| POST | /libraries/{libraryId}/files/{fileId}/office/session/refresh?mode=view|edit | view / edit | Новый WOPI token для открытой сессии (фронт → Reset_Access_Token) |
| GET | /wopi/files/{fileId}?access_token=... | WOPI | CheckFileInfo |
| GET | /wopi/files/{fileId}/contents?access_token=... | WOPI | GetFile |
| POST | /wopi/files/{fileId}/contents?access_token=... | WOPI | PutFile (сохранение) |
| POST | /wopi/files/{fileId}?access_token=... | WOPI | LOCK / UNLOCK / REFRESH_LOCK |
WOPI JWT живёт 8 часов; пока открыт редактор, frontend периодически вызывает session/refresh и передаёт новый токен в iframe через postMessage Reset_Access_Token, поэтому сессия может длиться дольше (пока жива cookie Portal).
WOPI-lock — lease 30 минут с refresh; хранится в той же таблице library_file_locks, что и checkout клиентского Office. Если файл уже изъят через DAV, WOPI edit/PutFile отклоняется.
При сохранении из редактора создаётся новая версия через UploadVersionAsync с комментарием "Portal Office".
Клиентское приложение Office (WebDAV)
Заголовок раздела «Клиентское приложение Office (WebDAV)»Не требует включённого Portal Office. Работает для файлов с is_office_file (расширение из OfficeDocumentTypes), независимо от office.enabled.
| Метод | Путь | Право | Описание |
|---|---|---|---|
| GET | /libraries/{libraryId}/files/{fileId}/office/client-session?mode=view|edit | view / edit | JWT-URL WebDAV + URI-схемы для установленного Office |
| POST | /libraries/{libraryId}/files/{fileId}/office/lock/break | владелец лока, Delete на библиотеку или portal admin | Снять checkout |
Ответ GET .../office/client-session
Заголовок раздела «Ответ GET .../office/client-session»{ "webdavUrl": "https://portal.example/api/v1/dav/{jwt}/{fileId}/{fileName}", "msUri": "ms-word:ofe|u|{webdavUrl}", "libreOfficeUri": "vnd.libreoffice.command:ofe|u|{urlencoded-webdavUrl}", "mode": "edit", "fileId": "uuid", "libraryId": "uuid", "expiresIn": 1209600}mode=edit→ командаofe(open for edit);mode=view→ofv.msUri:ms-word/ms-excel/ms-powerpointпо типу файла, видms-word:ofe|u|{webdavUrl}. URL после|u|не percent-encode целиком (иначе%в имени файла станет%25, и Word на macOS откроется пустым). Chrome/Edge на Mac: фронт подставляетofe%7Cu%7C.libreOfficeUri: та же команда для LibreOffice, МойОфис, Р7 и совместимых сборок.- JWT в пути:
purpose=dav, привязан кfileId,libraryId, пользователю,modeиjti; срок 14 суток. ПослеUNLOCK/ break-lock этотjtiнедействителен. - Публичный origin берётся из запроса (
Scheme+Host+PathBase). Адрес127.0.0.1/::1подменяется наlocalhost: Excel/Word на macOS пишут на HTTP WebDAV только если хост именноlocalhost. - Если файл уже изъят другим пользователем,
mode=edit→ HTTP 423. Если у пользователя есть право Edit и чужого checkout нет, сессия всегда edit (ofe), даже если клиент передалmode=view.
Клиент (браузер) открывает msUri (на Linux — libreOfficeUri). Save в приложении — PUT на webdavUrl. Это не способ подключить библиотеку как диск: для Проводника см. Portal Drive.
Single-file WebDAV — /api/v1/dav/{token}/{fileId} и /{fileId}/{*fileName}
Заголовок раздела «Single-file WebDAV — /api/v1/dav/{token}/{fileId} и /{fileId}/{*fileName}»Анонимно с точки зрения cookie Portal: аутентификация только JWT в пути. Это не полное дерево WebDAV, а один файл.
| Метод | Назначение |
|---|---|
OPTIONS | Advertise DAV 1,2; Allow |
HEAD / GET | Содержимое файла. Без Content-Disposition: attachment — иначе Excel открывает копию и Save требует «новое имя» |
PROPFIND | Depth 0: displayname, getcontentlength, getetag, getlastmodified, resourcetype, supportedlock |
LOCK | Exclusive write lock; создаёт/обновляет checkout. Ответ timeout: Second-1209600 |
UNLOCK | Снять checkout (заголовок Lock-Token). Тот же JWT может снова PUT — checkout создастся заново |
PUT | Новая версия (UploadVersionAsync, комментарий "Клиентское приложение"). Excel часто не шлёт LOCK: первый PUT сам создаёт checkout. Тело 0 байт → 204 (проба записи), без новой версии |
Чужой PUT / LOCK при чужом checkout → HTTP 423. После lock/break jti сессии автора отзывается: его следующий PUT/LOCK → 423. GET/HEAD/PROPFIND при чужом checkout разрешены (скачать и смотреть можно).
Nginx / Helm / native Linux: отдельный location ^~ /api/v1/dav/ — client_max_body_size 4G, без буферизации, timeout 3600s. Microsoft Office сохраняет на WebDAV только по HTTPS (исключение — localhost).
Checkout в library_file_locks
Заголовок раздела «Checkout в library_file_locks»| Поле | Смысл |
|---|---|
source | dav или wopi |
dav_jti | jti текущего DAV JWT (при смене сессии того же пользователя обновляется) |
expires_at | У WOPI — конец 30-мин lease; у DAV — NULL |
last_seen_at | Активность (LOCK/PUT/refresh). DAV без активности 14 суток считается брошенным |
Пока жив DAV-checkout, второй пользователь не откроет файл на правку ни в клиенте, ни в Portal Office. Тот же пользователь может открыть файл снова: lock обновляется, выдаётся новый jti.
Снять вручную: POST .../office/lock/break. После break JWT автора отозван: следующий PUT → 423.
Ответ GET /files для каждого файла:
{ "is_office_file": true, "lock": { "user": { "id": "uuid", "display_name": "Иванов" }, "locked_at": "2026-09-15T12:00:00Z", "last_seen_at": "2026-09-15T12:05:00Z", "source": "dav" }, "can_break_lock": true, "permissions": { "canEdit": true, "canDelete": false }}lock отсутствует, если изъятия нет. can_break_lock — true для автора лока, portal admin и обладателя Delete на библиотеку.
Поддерживаемые расширения: .docx, .xlsx, .pptx, .doc, .xls, .ppt, .odt, .ods, .odp, .csv, .rtf, .txt и др. (см. OfficeDocumentTypes).
События
Заголовок раздела «События»file.uploadedfile.folderCreatedfile.updated— переименование, перемещение, свойства (field_values)file.deletedfile.versionCreatedfile.versionRestored
Payload: libraryId, file (включая field_values), user. Для file.deleted дополнительно fileId / guid. Фильтр подписчика: config.libraryId. SDK: context.GetLibraryFileAsync(api).
Portal Drive
Заголовок раздела «Portal Drive»Дерево библиотеки или папки для клиента Portal Drive (rclone). Не заменяет single-file WebDAV для «Открыть в приложении».
Токен сессии — непрозрачный идентификатор в пути /api/v1/drive/{token}/… (подписанный JWT purpose=drive хранится в кэше, claim tv = users.token_version). Выдача требует cookie Portal. Срок 7 суток. Запись идёт только если живые ACL портала это позволяют (не только mode в JWT).
Готовые сборки клиента: GET /downloads/portal-drive/manifest.json и архивы рядом. Окно Диск само выбирает пакет по ОС браузера.
| Метод | Путь | Описание |
|---|---|---|
| POST | /libraries/{libraryId}/drive/sessions | Тело { "folderId": "uuid?" }. Непрозрачный токен на 7 суток, apiBase, client.url / client.token / точка монтирования для ссылки portaldrive://. Команд оболочки в ответе нет. |
| GET | /drive/{token}/ | Метаданные корня (library/folder, canWrite, presign) |
| GET | /drive/{token}/list?path= | Дети папки (с учётом ACL) |
| GET | /drive/{token}/stat?path= | Файл или папка |
| GET | /drive/{token}/file?path= | Содержимое: 302 на presigned S3 либо поток через API |
| PUT | /drive/{token}/file?path= | Загрузка/версия через API (fallback без presign) |
| POST | /drive/{token}/mkdir | { "path": "a/b" } |
| DELETE | /drive/{token}/node?path= | В корзину узла |
| POST | /drive/{token}/move | { "from", "to" } |
| GET | /drive/{token}/delta?cursor= | Изменения с курсора (updated_at, без корзины; пустой/старый курсор — truncated) |
| POST | /drive/{token}/uploads | Старт загрузки: { path, name, size, mimeType, replace } → putUrl (S3) или proxy |
| PUT | /drive/{token}/uploads/{id} | Тело файла, если proxy: true |
| POST | /drive/{token}/uploads/{id}/complete | Регистрация объекта, новая версия, checkout source=drive на время записи |
S3_PUBLIC_ENDPOINT — необязательный хост того же хранилища с компьютеров сотрудников. Пусто (как после установки) — содержимое файлов диска идёт через API. Это не отдельный сервис. Подробно: Portal Drive: как идут файлы.
Инструкция: Portal Drive.