openapi: 3.0.3
info:
  title: Documentations API
  version: "1.0"
  description: |
    REST-API сервиса документаций (`documentation-api`, бинарник `cmd/api`, образ
    `documentations`, deployment `documentations-api` в неймспейсе `documentations`).

    Спецификация реконструирована из исходного кода маршрутов
    (`cmd/api/routes_api.go`, `cmd/api/routes_internal.go`) и middleware
    (`cmd/api/bootstrap.go`, `pkg/midleware/*`). В репозитории **нет сгенерированного
    swagger/openapi**, поэтому схемы тел запросов/ответов приведены обобщённо
    (в коде они не описаны декларативно). Пути, методы и параметры пути —
    достоверные, из роутера `gorilla/mux`.

    ## Базовые пути
    - Публичный API: `/api/v1` (описан ниже).
    - Внутренний API: `/internal/v1` (сервис-к-сервису, облегчённая авторизация;
      здесь не детализируется — см. `cmd/api/routes_internal.go`).
    - Потоковая отдача/приём файлов вынесены в **отдельный сервис `filestream`**
      (`cmd/filestreamer`, образ `documentations-api-files`): `/api/v1/bundles/...`,
      `/api/v1/documents/...`, `/api/v1/pages/...`, `/api/v1/documents/folders`,
      `/api/v1/bundles_mrpas/...`, `/api/v1/public_link_mrpas/...`.
    - Health-check: `GET /ping` (предоставляется каркасом роутера `rest`).
    - Профилирование: `GET /debug/pprof/...` (net/http/pprof).

    ## Аутентификация
    Основной способ — JWT в заголовке `Authorization: Bearer <token>`, проверяемый по
    RSA-публичному ключу (`PUBLIC_KEY`, PEM/PKIX). Из claims извлекаются `company_ids`
    и `service_accounts`. Опционально включается проверка через Zitadel (`USE_ZITADEL`),
    а также разбор заголовка `Identity`. Часть ручек (пути `/public/...`,
    `/public_link_mrpas/...` и запросы с `download_type=temporary`) авторизуются по
    HMAC-JWT публичных ссылок (`DOCUMENT_PUBLIC_LINK_JWT_SECRET`). В сервисе filestream
    ссылки скачивания дополнительно подписываются (`signature` + `expires_at` в query,
    секрет `SIGNATURE_SECRET_KEY`).

    ## Формат ошибок
    Ответы оборачиваются middleware `rest.JSONResponse`; ошибки возвращаются в JSON.
    Нестандартный код `523` (`StatusOriginIsUnreachable`, `network/consts.go`)
    используется, когда исходный сервис недоступен.

    ## Пагинация
    Единого декларативного механизма пагинации в роутере нет; списки, где она нужна,
    принимают параметры фильтрации в теле POST-запроса (напр. `/documents/metadata`,
    `/documents/batch`). Эндпоинт `/documents/metadata` дополнительно ограничивается
    rate-limit (`METADATA_RATE_LIMIT_*`).

servers:
  - url: https://api.sarex.io/documentations/api/v1
    description: production
  - url: https://api.preprod.sarex.io/documentations/api/v1
    description: preprod
  - url: https://stage-api.sarex.io/documentations/api/v1
    description: stage

security:
  - bearerAuth: []

tags:
  - name: disks
  - name: documents
  - name: bundles
  - name: uploads
  - name: permissions
  - name: workspaces
  - name: dashboards
  - name: workflows
  - name: pages
  - name: marks
  - name: public-links
  - name: related-documents
  - name: changelogs
  - name: favorite-documents
  - name: name-templates
  - name: misc

paths:
  /conversion:
    post:
      tags: [misc]
      summary: Запустить конвертацию документа
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /disks:
    get:
      tags: [disks]
      summary: Список дисков (доступных пользователю)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    post:
      tags: [disks]
      summary: Создать диск (требуются права администратора)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /disks/{disk_id}:
    delete:
      tags: [disks]
      summary: Удалить диск (требуются права администратора)
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /disks/{disk_id}/documents:
    get:
      tags: [disks, documents]
      summary: Документы диска
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    post:
      tags: [disks, documents]
      summary: Документы диска по списку id
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /disks/{disk_id}/projects:
    get:
      tags: [disks]
      summary: Проекты диска
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /disks/{disk_id}/service_accounts:
    get:
      tags: [disks, permissions]
      summary: Сервисные аккаунты диска
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /disks/{disk_id}/size_migration:
    get:
      tags: [misc]
      summary: Миграция размеров (служебное)
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /disks/{disk_id}/delete_documents_from_ws_migration:
    get:
      tags: [misc]
      summary: Удаление документов при миграции воркспейса (служебное)
      parameters: [{ $ref: "#/components/parameters/DiskId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /documents:
    post:
      tags: [documents]
      summary: Создать документ/папку
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    delete:
      tags: [documents]
      summary: Массовое удаление документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/create_report:
    post:
      tags: [documents]
      summary: Сформировать отчёт по метаданным документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/metadata:
    post:
      tags: [documents]
      summary: Список метаданных документов (rate-limited)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
        "429": { $ref: "#/components/responses/TooManyRequests" }
  /documents/batch:
    post:
      tags: [documents]
      summary: Пакетное получение документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/flows:
    post:
      tags: [documents]
      summary: Документы в трансмиттале/ревью
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/public_link:
    post:
      tags: [public-links]
      summary: Создать публичную ссылку на документ
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /public/documents/public_link/{id}:
    get:
      tags: [public-links]
      summary: Прочитать публичную ссылку (публичный доступ по HMAC-JWT)
      security: []
      parameters: [{ $ref: "#/components/parameters/StrId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/public_link/{id}:
    patch:
      tags: [public-links]
      summary: Обновить публичную ссылку
      parameters: [{ $ref: "#/components/parameters/StrId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    delete:
      tags: [public-links]
      summary: Удалить публичную ссылку
      parameters: [{ $ref: "#/components/parameters/StrId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/update-path:
    patch:
      tags: [documents]
      summary: Сменить родителя документа
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/update-path:
    patch:
      tags: [documents]
      summary: Массовая смена родителя документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/super_create:
    post:
      tags: [documents]
      summary: Создание документа суперпользователем (требуются права администратора)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /documents/types:
    get:
      tags: [documents]
      summary: Справочник типов документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/get_folders_download_url:
    get:
      tags: [documents]
      summary: Ссылка на скачивание папок (подписанная)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /download_url/documents:
    get:
      tags: [documents]
      summary: Ссылка на скачивание документа
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/name_template:
    get:
      tags: [documents, name-templates]
      summary: Шаблон имени документа
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}:
    get:
      tags: [documents]
      summary: Документ по id
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
        "404": { $ref: "#/components/responses/NotFound" }
    patch:
      tags: [documents]
      summary: Переименовать/изменить документ (числовой id)
      parameters: [{ $ref: "#/components/parameters/DocumentIdNum" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    delete:
      tags: [documents]
      summary: Удалить документ (числовой id)
      parameters: [{ $ref: "#/components/parameters/DocumentIdNum" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/bundles:
    get:
      tags: [documents, bundles]
      summary: Бандлы документа
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/add_bundle:
    post:
      tags: [documents, bundles]
      summary: Привязать бандл к документу
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/move_bundles:
    patch:
      tags: [documents, bundles]
      summary: Переместить бандлы
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/ancestors:
    get:
      tags: [documents]
      summary: Предки документа
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/filetypes_by_extension:
    post:
      tags: [documents]
      summary: Определить тип файла по расширению
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/copy:
    post:
      tags: [documents]
      summary: Копировать документы (долгая операция, таймаут 120 мин)
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/copy_structure:
    post:
      tags: [documents]
      summary: Копировать структуру папок
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/bin:
    delete:
      tags: [documents]
      summary: Окончательно удалить документы из корзины
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/bin/restore:
    patch:
      tags: [documents]
      summary: Восстановить документы из корзины
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_ids}/company:
    get:
      tags: [documents]
      summary: Компания документов
      parameters:
        - name: document_ids
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/permissions:
    get:
      tags: [permissions]
      summary: Права доступа документа
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    post:
      tags: [permissions]
      summary: Выдать права на документ
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /documents/{document_id}/download:
    get:
      tags: [documents]
      summary: Скачать документ (отдаётся сервисом filestream)
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /permissions:
    get:
      tags: [permissions]
      summary: Справочник прав доступа
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /bundles:
    post:
      tags: [bundles]
      summary: Создать бандл
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}:
    get:
      tags: [bundles]
      summary: Бандл по id
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    patch:
      tags: [bundles]
      summary: Изменить бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    delete:
      tags: [bundles]
      summary: Удалить бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/download:
    get:
      tags: [bundles]
      summary: Скачать бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/{bundle_key}/download:
    get:
      tags: [bundles]
      summary: Скачать файл бандла по ключу
      parameters:
        - { $ref: "#/components/parameters/BundleId" }
        - { $ref: "#/components/parameters/BundleKey" }
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/{bundle_key}/upload_single:
    post:
      tags: [bundles, uploads]
      summary: Загрузить файл целиком (single upload)
      parameters:
        - { $ref: "#/components/parameters/BundleId" }
        - { $ref: "#/components/parameters/BundleKey" }
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/{bundle_key}/upload_multipart:
    post:
      tags: [bundles, uploads]
      summary: Начать multipart-загрузку файла бандла
      parameters:
        - { $ref: "#/components/parameters/BundleId" }
        - { $ref: "#/components/parameters/BundleKey" }
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/upload_finish:
    post:
      tags: [bundles, uploads]
      summary: Завершить загрузку бандла
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/marks:
    put:
      tags: [marks]
      summary: Добавить штампы/QR/подписи в бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/sign:
    post:
      tags: [bundles, marks]
      summary: Подписать бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/restart:
    post:
      tags: [bundles, workflows]
      summary: Перезапустить workflow бандла
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/cancel_qr:
    patch:
      tags: [marks]
      summary: Отменить QR-код
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/comment:
    patch:
      tags: [bundles]
      summary: Обновить комментарий бандла
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/presigned_url:
    get:
      tags: [bundles]
      summary: Presigned URL бандла
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/mrpas:
    get:
      tags: [bundles]
      summary: MRPA бандла
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundles/{bundle_id}/copy:
    post:
      tags: [bundles]
      summary: Копировать бандл
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /bundle/version:
    get:
      tags: [bundles]
      summary: Версии бандла
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /uploads/multipart/{upload_id}/complete:
    post:
      tags: [uploads]
      summary: Завершить multipart-загрузку
      parameters: [{ $ref: "#/components/parameters/UploadId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /uploads/multipart/{upload_id}/abort:
    post:
      tags: [uploads]
      summary: Прервать multipart-загрузку
      parameters: [{ $ref: "#/components/parameters/UploadId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /uploads/multipart/{upload_id}/{part_num}:
    post:
      tags: [uploads]
      summary: Загрузить часть (part) файла
      parameters:
        - { $ref: "#/components/parameters/UploadId" }
        - name: part_num
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /workspaces:
    post:
      tags: [workspaces]
      summary: Создать воркспейс
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /workspaces/{ws_id}:
    get:
      tags: [workspaces]
      summary: Документ воркспейса
      parameters:
        - name: ws_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /dashboards:
    post:
      tags: [dashboards]
      summary: Создать дашборд
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /dashboards/{db_id}:
    get:
      tags: [dashboards]
      summary: Документ дашборда
      parameters:
        - name: db_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /workflows/{workflow_id}:
    get:
      tags: [workflows]
      summary: Workflow по id
      parameters:
        - name: workflow_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /pages:
    post:
      tags: [pages]
      summary: Создать страницу
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /pages/{data_source}/{page_key}/download:
    get:
      tags: [pages]
      summary: Скачать страницу
      parameters:
        - name: data_source
          in: path
          required: true
          schema: { type: string }
        - name: page_key
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /public/qr/{public_uuid}/document_info:
    get:
      tags: [marks]
      summary: Публичная информация о документе по QR
      security: []
      parameters:
        - name: public_uuid
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /related_documents:
    post:
      tags: [related-documents]
      summary: Создать связь документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    get:
      tags: [related-documents]
      summary: Получить связанные документы
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /related_documents/bulk_delete:
    post:
      tags: [related-documents]
      summary: Массово удалить связи документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /templates/{bundle_id}:
    get:
      tags: [misc]
      summary: Шаблон по бандлу
      parameters: [{ $ref: "#/components/parameters/BundleId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /changelogs/create:
    post:
      tags: [changelogs]
      summary: Создать changelog
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /changelogs/{changelog_id}:
    patch:
      tags: [changelogs]
      summary: Обновить changelog
      parameters:
        - name: changelog_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /links:
    post:
      tags: [misc]
      summary: Создать ссылку
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /favorite_documents:
    post:
      tags: [favorite-documents]
      summary: Добавить документ в избранное
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    get:
      tags: [favorite-documents]
      summary: Список избранных документов
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /favorite_documents/{document_id}:
    delete:
      tags: [favorite-documents]
      summary: Убрать документ из избранного
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }

  /name_templates/create:
    post:
      tags: [name-templates]
      summary: Создать шаблон имени
      responses:
        "200": { $ref: "#/components/responses/Ok" }
  /name_templates/{document_id}:
    get:
      tags: [name-templates]
      summary: Шаблон имени по документу
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    patch:
      tags: [name-templates]
      summary: Обновить шаблон имени
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }
    delete:
      tags: [name-templates]
      summary: Удалить шаблон имени
      parameters: [{ $ref: "#/components/parameters/DocumentId" }]
      responses:
        "200": { $ref: "#/components/responses/Ok" }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        JWT, подписанный ключом, соответствующим `PUBLIC_KEY` (RSA/PKIX).
        Для публичных ссылок используется HMAC-JWT (`DOCUMENT_PUBLIC_LINK_JWT_SECRET`).

  parameters:
    DiskId:
      name: disk_id
      in: path
      required: true
      schema: { type: string }
    DocumentId:
      name: document_id
      in: path
      required: true
      schema: { type: string }
    DocumentIdNum:
      name: document_id
      in: path
      required: true
      description: Числовой идентификатор документа (маршрут ограничен regex `[0-9]+`)
      schema: { type: integer }
    BundleId:
      name: bundle_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    BundleKey:
      name: bundle_key
      in: path
      required: true
      schema: { type: string }
    UploadId:
      name: upload_id
      in: path
      required: true
      schema: { type: string }
    StrId:
      name: id
      in: path
      required: true
      schema: { type: string }

  responses:
    Ok:
      description: Успешный ответ (тело зависит от ручки; в JSON)
      content:
        application/json:
          schema: { type: object, additionalProperties: true }
    Forbidden:
      description: Недостаточно прав
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Ресурс не найден
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    TooManyRequests:
      description: Превышен лимит запросов (rate limit)
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
      additionalProperties: true
