Перейти к содержимому

API: Библиотеки документов и файлы

Требуется авторизация. Права наследуются от узла-родителя (nodeId).

МетодПутьПраво на узелОписание
GET/libraries?nodeId={uuid}viewБиблиотеки узла
POST/librarieseditСоздать библиотеку
GET/libraries/{id}viewОдна библиотека
PATCH/libraries/{id}editОбновить
DELETE/libraries/{id}deleteУдалить в корзину узла (снимок библиотеки целиком)
POST/libraries/{id}/recycle-contentdeleteПереместить все корневые файлы/папки в корзину (библиотека остаётся)

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}/fieldsviewСхема полей
POST/libraries/{id}/fieldsportal admin + editСоздать поле
PATCH/libraries/{id}/fields/{fieldId}portal admin + editИзменить поле (в т.ч. тип и isRequired)
DELETE/libraries/{id}/fields/{fieldId}portal admin + editУдалить поле и значения во всех файлах/папках
GET/libraries/{id}/person-optionsviewПоиск пользователей/групп для person-поля
GET/libraries/{id}/lookup-optionsviewВарианты lookup (targetListId или targetLibraryId)

Значения: Поля библиотек. UI: вкладка Поля в настройках библиотеки.

Настройки библиотеки → Общие → Участвует в поиске, либо:

PATCH /api/v1/libraries/{libraryId}
Content-Type: application/json
{ "searchEnabled": false }

Изменение searchEnabled — только администратор портала. При выключении библиотека и файлы сразу убираются из индекса. См. Поиск.

Настройки библиотеки → Общие → Как открывать документы Office, либо:

PATCH /api/v1/libraries/{libraryId}
Content-Type: application/json
{ "officeOpenMode": "client" }
ЗначениеКлик по имениPortal Office нужен
downloadСкачать файл (по умолчанию)Нет
portal_officeСессия WOPI в браузереДа; иначе PATCH → 400
clientURI-схема установленного Office + WebDAVНет

Клик по имени следует этой настройке; в меню строки остаются все доступные действия. Настройка на библиотеку, не на установку.

Пользовательская инструкция: Открытие документов Office.

МетодПутьПравоОписание
GET/files?parentId={uuid}viewСодержимое папки (без parentId — корень)
GET/files/liveviewSSE: версия и checkout файла после Save (без polling)
GET/files/{fileRef}viewОдин файл/папка (номер или GUID), включая field_values
POST/files/foldersaddСоздать папку (опц. fieldValues)
POST/files/uploadaddЗагрузить файл (multipart, поле file, опц. parentId, опц. fieldValues)
PATCH/files/{fileId}editПереименовать / переместить / fieldValues (merge + required)
DELETE/files/{fileId}deleteУдалить файл или папку (рекурсивно)
GET/files/{fileRef}/permission-settingsviewНастройки наследования прав (inheritsPermissions, parentLabel)
PATCH/files/{fileRef}/permission-settingsadminВключить/отключить наследование: { "inheritFromParent": true|false }
GET / HEAD/files/{fileRef}/thumbnailviewМиниатюра изображения для сетки и колонки «Предпросмотр»

Для fileRef допускается числовой id или guid. Управление наследованием — только администратор портала. Явные права на файл/папку — через /api/v1/permissions с resourceType=file и libraryId.

Ответ GET /files включает meta.breadcrumb — цепочка папок до текущей. Каждый элемент содержит inherits_permissions и permissions (canEdit, canDelete, canManagePermissions).

GET /api/v1/libraries/{libraryId}/files?parentId={uuid}&page=1&limit=50&viewId={uuid}&sort=...&orderBy=...&filter=...&q=...
ПараметрОписание
parentIdUUID папки (без параметра — корень библиотеки)
pageНомер страницы (по умолчанию 1)
limitРазмер страницы (1–500; иначе из pageSize представления)
viewIdUUID представления (иначе — default view)
sortOverride сортировки: JSON-массив или column:asc,column2:desc
orderByТо же, что sort (приоритет у orderBy, если указаны оба)
filterOverride фильтра (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. Клиент не получает прямых 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}/thumbnailviewJPEG-миниатюра (длинная сторона 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}/versionsviewТекущая версия и история
POST/files/{fileId}/versionseditЗагрузить новую версию (multipart: file, опц. comment)
GET/files/{fileId}/versions/{versionId}/downloadviewСкачать архивную версию
POST/files/{fileId}/versions/{versionId}/restoreeditВосстановить версию как текущую

При загрузке новой версии предыдущее содержимое сохраняется в 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|editview / editURL iframe Portal Office + WOPI token (accessToken, accessTokenTtl)
POST/libraries/{libraryId}/files/{fileId}/office/session/refresh?mode=view|editview / editНовый WOPI token для открытой сессии (фронт → Reset_Access_Token)
GET/wopi/files/{fileId}?access_token=...WOPICheckFileInfo
GET/wopi/files/{fileId}/contents?access_token=...WOPIGetFile
POST/wopi/files/{fileId}/contents?access_token=...WOPIPutFile (сохранение)
POST/wopi/files/{fileId}?access_token=...WOPILOCK / 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".

Не требует включённого Portal Office. Работает для файлов с is_office_file (расширение из OfficeDocumentTypes), независимо от office.enabled.

МетодПутьПравоОписание
GET/libraries/{libraryId}/files/{fileId}/office/client-session?mode=view|editview / editJWT-URL WebDAV + URI-схемы для установленного Office
POST/libraries/{libraryId}/files/{fileId}/office/lock/breakвладелец лока, Delete на библиотеку или portal adminСнять checkout
{
"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, а один файл.

МетодНазначение
OPTIONSAdvertise DAV 1,2; Allow
HEAD / GETСодержимое файла. Без Content-Disposition: attachment — иначе Excel открывает копию и Save требует «новое имя»
PROPFINDDepth 0: displayname, getcontentlength, getetag, getlastmodified, resourcetype, supportedlock
LOCKExclusive 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).

ПолеСмысл
sourcedav или wopi
dav_jtijti текущего 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.uploaded
  • file.folderCreated
  • file.updated — переименование, перемещение, свойства (field_values)
  • file.deleted
  • file.versionCreated
  • file.versionRestored

Payload: libraryId, file (включая field_values), user. Для file.deleted дополнительно fileId / guid. Фильтр подписчика: config.libraryId. SDK: context.GetLibraryFileAsync(api).

Дерево библиотеки или папки для клиента 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.