1259 lines
44 KiB
YAML
1259 lines
44 KiB
YAML
openapi: 3.0.3
|
||
|
||
info:
|
||
title: Documentation API v2
|
||
version: "1.0"
|
||
description: |
|
||
REST API сервиса **documentation-api-v2** (`pdm/documentation-api-v2`) —
|
||
Go-сервис домена «documentations» (v2): управление дисками, документами,
|
||
бандлами, data source, страницами, публичными ссылками, workflow-обработкой
|
||
и загрузкой файлов (в т.ч. multipart).
|
||
|
||
Сервис написан на Go (**Fiber v2**, `github.com/gofiber/fiber/v2`). HTTP-сервер
|
||
собирается в `internal/api/httpserver/server.go`. Спецификация ниже получена
|
||
конвертацией сгенерированной `swag`-схемы (`docs/swagger.yaml`, Swagger 2.0)
|
||
в OpenAPI 3.0.3 и нормализацией путей под реальные маршруты Fiber.
|
||
|
||
Роутинг состоит из двух групп (`server.go`, `App.Run`):
|
||
|
||
- публичный API — префикс `/api/v1`;
|
||
- внутренний API — префикс `/internal/v1` (для вызовов внутри кластера).
|
||
|
||
Интерактивная документация Swagger UI доступна по `/swagger/*`.
|
||
Служебный эндпоинт проб k8s — `GET /ping` (вне групп, без аутентификации).
|
||
|
||
### Аутентификация
|
||
JWT-middleware (`pkg/middleware/jwt_auth`) подключён **только к группе
|
||
`/api/v1`**. Токен передаётся заголовком `Authorization: Bearer <jwt>`
|
||
либо query-параметром `?auth_jwt=<jwt>` (`JWTToCtx`). Поддерживаются два
|
||
режима (`jwtauth.New`):
|
||
|
||
1. **Zitadel** — если передан заголовок `Identity: Bearer <jwt>`, полезная
|
||
нагрузка (`urn:zitadel:iam:user:metadata`) берётся из identity-токена
|
||
без проверки подписи (доверие обеспечивает Istio).
|
||
2. **sarex-backend** — если заголовка `Identity` нет, подпись основного
|
||
токена проверяется публичным RSA-ключом `PUBLIC_KEY` (PKIX).
|
||
|
||
Для маршрутов публичных/временных ссылок (`/api/v1/public/*` и
|
||
`download_type=temporary`) токен проверяется HMAC-секретом
|
||
`DOCUMENT_PUBLIC_LINK_JWT_SECRET`.
|
||
|
||
Эндпоинты `/internal/v1/*` middleware аутентификации на уровне приложения
|
||
**не проходят** — доступ ограничивается сетевым слоем кластера. (В исходной
|
||
swag-схеме параметр `Authorization` у них помечен как обязательный —
|
||
это артефакт аннотаций, а не рантайм-поведение.)
|
||
|
||
### Пагинация
|
||
Явной пагинации у списочных ответов нет: коллекции возвращаются целиком
|
||
(массивом или объектом-обёрткой, напр. `GetDiskListResponse.disks`,
|
||
`ServiceAccountsResponse.service_account`). Часть выборок ограничивается
|
||
параметрами запроса (`company_id`, `extend`, `upload_path`).
|
||
|
||
### Обработка ошибок
|
||
Ошибки возвращаются как JSON `application/json` со схемой `AppError` —
|
||
`{ message, error_code }` (`internal/api/apperrors`). Код `error_code`
|
||
определяет HTTP-статус (`apperrors/error_response.go`):
|
||
|
||
| `error_code` | Константа | HTTP-статус |
|
||
| --- | --- | --- |
|
||
| `PDM-0000` | SystemErrorCode | 500 |
|
||
| `PDM-0001` | ErrNotFoundCode | 404 |
|
||
| `PDM-0002` | ErrNoAuthCode | (ошибки авторизации → 401) |
|
||
| `PDM-0003` | ErrNoAccessCode | 403 |
|
||
| `PDM-0004` | ErrInvalidCode | 400 |
|
||
| `PDM-0005` | ErrGoneCode | 410 |
|
||
|
||
### Замечания (расхождения кода/схемы)
|
||
- Ошибки аутентификации middleware отдаёт статусом **401** (в swag-схеме
|
||
эти ответы не описаны — там только `400`/`404`/`500`).
|
||
- Ошибка «истёкшая публичная ссылка» маппится на **`410 Gone`**
|
||
(`ErrGoneCode`), а не на `404`.
|
||
- Путь загрузки части файла содержит двойной слэш —
|
||
`POST /api/v1/uploads//multipart/{upload_id}/{part_num}` (так в схеме и
|
||
маршруте).
|
||
- `api_server` задаёт очень большой `BodyLimit` (5 ТБ) для загрузки файлов.
|
||
|
||
contact:
|
||
name: Sarex
|
||
url: https://gitlab/pdm/documentation-api-v2
|
||
|
||
servers:
|
||
- url: http://api-v2-service.documentations
|
||
description: Stage (ClusterIP, порт 80 → 8080)
|
||
- url: http://api-v2-service.documentations-preprod
|
||
description: Preprod (ClusterIP)
|
||
- url: http://api-v2-service.documentations-prod
|
||
description: Production (ClusterIP)
|
||
- url: http://localhost:8000
|
||
description: Локальный запуск (API_ADDRESS)
|
||
|
||
tags:
|
||
- name: document
|
||
description: Документы (создание, изменение, перемещение, скачивание)
|
||
- name: disk
|
||
description: Диски и их проекты/сервис-аккаунты
|
||
- name: bundle
|
||
description: Бандлы и загрузка файлов
|
||
- name: page
|
||
description: Страницы data source
|
||
- name: upload
|
||
description: Multipart-загрузка
|
||
- name: workflow
|
||
description: Workflow-обработка и вебхуки
|
||
- name: workspace
|
||
description: Рабочие области
|
||
- name: permission
|
||
description: Типы прав доступа
|
||
- name: data_source
|
||
description: Data source (внутренние проверки)
|
||
|
||
security:
|
||
- bearerAuth: []
|
||
|
||
paths:
|
||
# ==========================================================================
|
||
# Documents (public /api/v1)
|
||
# ==========================================================================
|
||
/api/v1/documents:
|
||
post:
|
||
tags: [document]
|
||
summary: create document
|
||
description: Create document with bundle and data_sources.
|
||
operationId: createDocument
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateDocumentRequest'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Document'
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/types:
|
||
get:
|
||
tags: [document]
|
||
summary: types
|
||
description: Descriptions of possible documents.
|
||
operationId: getDocumentTypes
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/BundleTypeStruct'
|
||
|
||
/api/v1/documents/{document_id}:
|
||
get:
|
||
tags: [document]
|
||
summary: get document
|
||
description: >-
|
||
Get document by document_id and optional "extend". If "extend" is
|
||
"bundles", returns bundles with all data_sources and pages.
|
||
operationId: getDocument
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
- { name: extend, in: query, required: false, schema: { type: string }, example: bundles }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
patch:
|
||
tags: [document]
|
||
summary: change document
|
||
description: Change document's name.
|
||
operationId: changeDocument
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/PatchDocument' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
delete:
|
||
tags: [document]
|
||
summary: delete document
|
||
description: Delete document (soft delete).
|
||
operationId: deleteDocument
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_id}/update-path:
|
||
patch:
|
||
tags: [document]
|
||
summary: move document to another folder
|
||
description: Move document to another folder.
|
||
operationId: updateDocumentPath
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/PatchDocumentPath' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_id}/move_bundles:
|
||
patch:
|
||
tags: [document]
|
||
summary: move bundles
|
||
description: >-
|
||
Move bundles to document. The source document is marked as deleted if it
|
||
has no bundles left after the move.
|
||
operationId: moveBundles
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: string }, example: "1" }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/MoveBundleRequest' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Bundle' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_id}/add_bundle:
|
||
post:
|
||
tags: [document]
|
||
summary: add bundle to document
|
||
description: Add created and uploaded bundle to an existing document.
|
||
operationId: addBundleToDocument
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/AddBundleRequest' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Bundle' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_id}/ancestors:
|
||
get:
|
||
tags: [document]
|
||
summary: get ancestors of documents
|
||
operationId: getDocumentAncestors
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_id}/download:
|
||
get:
|
||
tags: [document]
|
||
summary: get url for download document
|
||
operationId: getDocumentDownloadURL
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/DownloadURLResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/documents/{document_ids}/company:
|
||
get:
|
||
tags: [document]
|
||
summary: get company_ids by document_ids
|
||
description: Get map of documents and companies.
|
||
operationId: getDocumentsCompanyMap
|
||
parameters:
|
||
- { name: document_ids, in: path, required: true, schema: { type: string }, example: "1,2,3,4,5" }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/DocumentCompanyMap' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Disks
|
||
# ==========================================================================
|
||
/api/v1/disks:
|
||
get:
|
||
tags: [disk]
|
||
summary: get disks
|
||
description: >-
|
||
Get disks. company_id requests disks of a specific company; by default
|
||
the user gets disks of all their companies.
|
||
operationId: getDisks
|
||
parameters:
|
||
- { name: company_id, in: query, required: true, schema: { type: integer }, example: 1 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/GetDiskListResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
post:
|
||
tags: [disk]
|
||
summary: create disk
|
||
description: Create disk. Requires admin permissions in Django.
|
||
operationId: createDisk
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CreateDiskRequest' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Disk' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
delete:
|
||
tags: [disk]
|
||
summary: delete disk
|
||
description: >-
|
||
Soft delete of a disk (marks the disk, not the document, as deleted).
|
||
Requires admin permissions in Django. disk_id passed as path in the
|
||
underlying route.
|
||
operationId: deleteDisk
|
||
parameters:
|
||
- { name: disk_id, in: path, required: true, schema: { type: string }, example: 2e3bda68-09bb-4bc0-b3c3-984117256c8b }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/disks/{disk_id}/projects:
|
||
get:
|
||
tags: [disk]
|
||
summary: get projects
|
||
description: Get projects by disk_id.
|
||
operationId: getDiskProjects
|
||
parameters:
|
||
- { name: disk_id, in: path, required: true, schema: { type: string }, example: 2e3bda68-09bb-4bc0-b3c3-984117256c8b }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/GetDiskListResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/disks/{disk_id}/service_accounts:
|
||
get:
|
||
tags: [disk]
|
||
summary: get service accounts
|
||
description: Get service accounts by disk_id.
|
||
operationId: getDiskServiceAccounts
|
||
parameters:
|
||
- { name: disk_id, in: path, required: true, schema: { type: string }, example: 2e3bda68-09bb-4bc0-b3c3-984117256c8b }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/ServiceAccountsResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Bundles
|
||
# ==========================================================================
|
||
/api/v1/bundles/{bundle_id}/bucket_name:
|
||
get:
|
||
tags: [bundle]
|
||
summary: get bucket name
|
||
description: Get bucket name by bundle_id.
|
||
operationId: getBucketName
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/BucketNameResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/bundles/{bundle_id}/download:
|
||
get:
|
||
tags: [bundle]
|
||
summary: get url for download bundle
|
||
operationId: getBundleDownloadURL
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/DownloadURLResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/bundles/{bundle_id}/{bundle_key}/download:
|
||
get:
|
||
tags: [bundle]
|
||
summary: get url for download bundle (data_source)
|
||
description: Get url for download of a specific data_source of a bundle.
|
||
operationId: getBundleDataSourceDownloadURL
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
- { name: bundle_key, in: path, required: true, schema: { type: string }, example: las }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/DownloadURLResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/bundles/{bundle_id}/{bundle_key}/upload_multipart:
|
||
post:
|
||
tags: [bundle]
|
||
summary: initial multipart upload
|
||
description: Initialize a multipart upload for chunked file upload to storage.
|
||
operationId: initMultipartUpload
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
- { name: bundle_key, in: path, required: true, schema: { type: string }, example: potree }
|
||
- { name: upload_path, in: query, required: false, schema: { type: string }, example: r/1/1/2 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/MultipartUpload' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/bundles/{bundle_id}/{bundle_key}/upload_single:
|
||
post:
|
||
tags: [bundle]
|
||
summary: upload single file
|
||
description: >-
|
||
Upload a single file to storage for a specific bundle. Bundle and
|
||
DataSource must already exist.
|
||
operationId: uploadSingleFile
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
- { name: bundle_key, in: path, required: true, schema: { type: string }, example: potree }
|
||
- { name: upload_path, in: query, required: false, schema: { type: string }, example: data/r/r.bin }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/bundles/{bundle_id}/upload_finish:
|
||
post:
|
||
tags: [bundle]
|
||
summary: finish upload
|
||
description: >-
|
||
Finish upload: change bundle status from "created" to "uploaded", update
|
||
data_source file sizes, check for files in storage.
|
||
operationId: finishUpload
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Bundle' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Pages
|
||
# ==========================================================================
|
||
/api/v1/pages:
|
||
post:
|
||
tags: [page]
|
||
summary: create page
|
||
description: Creates a page for a specified data_source_id.
|
||
operationId: createPage
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CreatePageRequest' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Page' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/pages/{data_source}/{page_key}/download:
|
||
get:
|
||
tags: [page]
|
||
summary: get download url page
|
||
operationId: getPageDownloadURL
|
||
parameters:
|
||
- { name: data_source, in: path, required: true, schema: { type: string }, example: "1" }
|
||
- { name: page_key, in: path, required: true, schema: { type: string }, example: "2" }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/DownloadURLResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Permissions
|
||
# ==========================================================================
|
||
/api/v1/permissions:
|
||
get:
|
||
tags: [permission]
|
||
summary: permission types
|
||
operationId: getPermissionTypes
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/PermissionType' }
|
||
|
||
# ==========================================================================
|
||
# Uploads (multipart parts)
|
||
# ==========================================================================
|
||
/api/v1/uploads//multipart/{upload_id}/{part_num}:
|
||
post:
|
||
tags: [upload]
|
||
summary: upload multipart upload part
|
||
description: >-
|
||
Upload a part of a file to storage. NB: the route contains a double
|
||
slash after `uploads` (as registered).
|
||
operationId: uploadMultipartPart
|
||
parameters:
|
||
- { name: upload_id, in: path, required: true, schema: { type: string } }
|
||
- { name: part_num, in: path, required: true, schema: { type: integer }, example: 1 }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
file:
|
||
type: string
|
||
format: binary
|
||
required: [file]
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/uploads/multipart/{upload_id}/abort:
|
||
post:
|
||
tags: [upload]
|
||
summary: abort multipart upload
|
||
operationId: abortMultipartUpload
|
||
parameters:
|
||
- { name: upload_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/uploads/multipart/{upload_id}/complete:
|
||
post:
|
||
tags: [upload]
|
||
summary: complete multipart upload
|
||
description: >-
|
||
Complete a multipart upload. When all parts are uploaded, marks parts in
|
||
storage and database as "completed".
|
||
operationId: completeMultipartUpload
|
||
parameters:
|
||
- { name: upload_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Workflows
|
||
# ==========================================================================
|
||
/api/v1/workflows/{workflow_id}:
|
||
get:
|
||
tags: [workflow]
|
||
summary: get workflow
|
||
operationId: getWorkflow
|
||
parameters:
|
||
- { name: workflow_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Workflow' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
post:
|
||
tags: [workflow]
|
||
summary: webhook
|
||
description: >-
|
||
Determines and updates workflow status (and bundle workflow status). On
|
||
success both are set to "done"; if bundle has no document_id, status is
|
||
set to "No document".
|
||
operationId: workflowWebhook
|
||
parameters:
|
||
- { name: workflow_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Workspaces
|
||
# ==========================================================================
|
||
/api/v1/workspaces:
|
||
post:
|
||
tags: [workspace]
|
||
summary: create workspace
|
||
description: Creates a workspace document that may include other documents.
|
||
operationId: createWorkspace
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/CreateWorkspaceRequest' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/api/v1/workspaces/{ws_id}:
|
||
get:
|
||
tags: [workspace]
|
||
summary: get workspace document
|
||
description: Get document with workspace type.
|
||
operationId: getWorkspace
|
||
parameters:
|
||
- { name: ws_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Document' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
# ==========================================================================
|
||
# Internal (/internal/v1) — без app-аутентификации, только внутри кластера
|
||
# ==========================================================================
|
||
/internal/v1/bundles/{bundle_id}:
|
||
get:
|
||
tags: [bundle]
|
||
summary: get internal bundle
|
||
description: Get internal bundle by bundle_id.
|
||
operationId: getInternalBundle
|
||
security: []
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Bundle' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/internal/v1/bundles/{bundle_id}/workflow:
|
||
post:
|
||
tags: [bundle]
|
||
summary: add workflow
|
||
description: Add workflow to bundle.
|
||
operationId: addBundleWorkflow
|
||
security: []
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Workflow' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/internal/v1/documents/{document_id}:
|
||
delete:
|
||
tags: [document]
|
||
summary: internal delete document
|
||
description: Internal delete document (soft delete).
|
||
operationId: internalDeleteDocument
|
||
security: []
|
||
parameters:
|
||
- { name: document_id, in: path, required: true, schema: { type: integer }, example: 999 }
|
||
responses:
|
||
'200': { description: OK }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
/internal/v1/is_folder/bundles/{bundle_id}/{bundle_key}:
|
||
get:
|
||
tags: [data_source]
|
||
summary: is data_source folder handler
|
||
description: Returns whether the data_source is a folder or not.
|
||
operationId: isDataSourceFolder
|
||
security: []
|
||
parameters:
|
||
- { name: bundle_id, in: path, required: true, schema: { type: string } }
|
||
- { name: bundle_key, in: path, required: true, schema: { type: string }, example: potree }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/IsDataSourceFolderResponse' }
|
||
'400': { $ref: '#/components/responses/BadRequest' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
'500': { $ref: '#/components/responses/ServerError' }
|
||
|
||
components:
|
||
securitySchemes:
|
||
bearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
bearerFormat: JWT
|
||
description: >-
|
||
JWT в заголовке `Authorization: Bearer <jwt>` или в query-параметре
|
||
`auth_jwt`. Режим sarex-backend проверяется RSA-ключом `PUBLIC_KEY`;
|
||
режим Zitadel — по заголовку `Identity`.
|
||
|
||
responses:
|
||
BadRequest:
|
||
description: Bad Request
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/AppError' }
|
||
NotFound:
|
||
description: Not Found
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/AppError' }
|
||
ServerError:
|
||
description: Internal Server Error
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/AppError' }
|
||
|
||
schemas:
|
||
ErrCodeType:
|
||
type: string
|
||
enum: [PDM-0000, PDM-0001, PDM-0002, PDM-0003, PDM-0004, PDM-0005]
|
||
description: >-
|
||
SystemErrorCode / ErrNotFoundCode / ErrNoAuthCode / ErrNoAccessCode /
|
||
ErrInvalidCode / ErrGoneCode
|
||
|
||
AppError:
|
||
type: object
|
||
properties:
|
||
message: { type: string }
|
||
error_code: { $ref: '#/components/schemas/ErrCodeType' }
|
||
|
||
AddBundleRequest:
|
||
type: object
|
||
required: [bundle_id]
|
||
properties:
|
||
bundle_id: { type: string }
|
||
|
||
BucketNameResponse:
|
||
type: object
|
||
properties:
|
||
bucket_name: { type: string }
|
||
|
||
Bundle:
|
||
type: object
|
||
properties:
|
||
attributes: { type: object, additionalProperties: true }
|
||
author: { $ref: '#/components/schemas/User' }
|
||
bim_id: { type: integer }
|
||
bundle_copied_from: { type: string }
|
||
created_at: { type: string }
|
||
data_sources:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/DataSource' }
|
||
date: { type: string, description: optional field for django integration }
|
||
document_id: { type: integer }
|
||
has_digital_signature: { type: boolean }
|
||
has_qr_code: { type: boolean }
|
||
has_stamp: { type: boolean }
|
||
id: { type: string }
|
||
size: { type: integer }
|
||
type: { $ref: '#/components/schemas/BundleType' }
|
||
upload_status: { $ref: '#/components/schemas/BundleUploadStatus' }
|
||
workflow: { $ref: '#/components/schemas/Workflow' }
|
||
workflow_status: { $ref: '#/components/schemas/BundleWorkflowStatus' }
|
||
|
||
BundleKey:
|
||
type: string
|
||
enum:
|
||
- las
|
||
- potree
|
||
- e57
|
||
- clouds
|
||
- panoramas_json
|
||
- panoramas
|
||
- glb
|
||
- original
|
||
- tiles
|
||
- tif
|
||
- tif_dem
|
||
- metadata
|
||
- colored-relief-tif
|
||
- topographic_tiles
|
||
- geojson
|
||
- ap_rasterized_las
|
||
- ap_rasterized_potree
|
||
- ab_las
|
||
- ab_potree
|
||
- deviation_json
|
||
- bim
|
||
- bim_optimized
|
||
- bim_optimizedGz
|
||
- bim_metadata
|
||
- bim_metadataGz
|
||
- bim_metadata_static
|
||
- bim_metadata_staticGz
|
||
- diff_json
|
||
- ifc
|
||
- nwd
|
||
- rvt
|
||
- nwc
|
||
- abap_json
|
||
- cloud_ooc
|
||
- debug_abap_json
|
||
- pdf
|
||
- p7s
|
||
- xlsx
|
||
- dxf
|
||
- docx
|
||
- dwg
|
||
|
||
BundleKeyField:
|
||
type: object
|
||
properties:
|
||
allowed_extensions:
|
||
type: array
|
||
items: { type: string }
|
||
can_be_downloaded_by_user: { type: boolean }
|
||
can_be_uploaded_by_user: { type: boolean }
|
||
file: { type: boolean }
|
||
folder: { type: boolean }
|
||
key: { $ref: '#/components/schemas/BundleKey' }
|
||
required: { type: boolean }
|
||
title: { type: string }
|
||
|
||
BundleType:
|
||
type: string
|
||
enum:
|
||
- cloud
|
||
- bim
|
||
- bimv2
|
||
- surface
|
||
- other_files
|
||
- deviation
|
||
- dem
|
||
- orthophoto
|
||
- dxf
|
||
- ksg
|
||
- docx
|
||
- dwg
|
||
- pdf
|
||
- xlsx
|
||
- abap
|
||
- c2c
|
||
- c2s
|
||
|
||
BundleTypeStruct:
|
||
type: object
|
||
properties:
|
||
fields:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/BundleKeyField' }
|
||
name: { $ref: '#/components/schemas/BundleType' }
|
||
title: { type: string }
|
||
|
||
BundleUploadStatus:
|
||
type: string
|
||
enum: [created, uploaded]
|
||
|
||
BundleWorkflowStatus:
|
||
type: string
|
||
enum: [created, skipped, done, errored]
|
||
|
||
CreateDiskRequest:
|
||
type: object
|
||
required: [admin_ids, company_id, storage_type]
|
||
properties:
|
||
admin_ids:
|
||
type: array
|
||
minItems: 1
|
||
items: { type: integer }
|
||
company_id: { type: integer }
|
||
storage_type:
|
||
type: string
|
||
maxLength: 50
|
||
enum: [sarex]
|
||
|
||
CreateDocumentRequest:
|
||
type: object
|
||
required: [disk_id, is_folder, name, parent_id]
|
||
properties:
|
||
bundle_id: { type: string }
|
||
disk_id: { type: string }
|
||
is_folder: { type: boolean }
|
||
is_project: { type: boolean }
|
||
name: { type: string, maxLength: 250 }
|
||
parent_id: { type: integer }
|
||
target_id: { type: integer }
|
||
|
||
CreatePageRequest:
|
||
type: object
|
||
properties:
|
||
data_source: { type: string }
|
||
id: { type: string }
|
||
page_order: { type: integer }
|
||
thumbnail: { type: string }
|
||
|
||
CreateWorkspaceRequest:
|
||
type: object
|
||
required: [disk_id, documents, name, parent_id]
|
||
properties:
|
||
disk_id: { type: string }
|
||
documents:
|
||
type: array
|
||
items: { type: integer }
|
||
name: { type: string, maxLength: 250 }
|
||
parent_id: { type: integer }
|
||
|
||
DataSource:
|
||
type: object
|
||
properties:
|
||
file_name: { type: string }
|
||
format: { $ref: '#/components/schemas/DataSourceFormat' }
|
||
id: { type: string }
|
||
key: { $ref: '#/components/schemas/BundleKey' }
|
||
pages:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Page' }
|
||
size: { type: integer }
|
||
type: { $ref: '#/components/schemas/DataSourceUploadType' }
|
||
|
||
DataSourceFormat:
|
||
type: string
|
||
enum: [glb, json, gz, s3d]
|
||
|
||
DataSourceUploadType:
|
||
type: string
|
||
enum: [file, folder]
|
||
|
||
DisableButton:
|
||
type: string
|
||
enum: [workspace, delete, rename, create, project, download, history, add_doc_to_ws, del_doc_from_ws]
|
||
|
||
Disk:
|
||
type: object
|
||
properties:
|
||
admin_ids:
|
||
type: array
|
||
items: { type: integer }
|
||
bucket_name: { type: string }
|
||
company_id: { type: integer }
|
||
created_at: { type: string }
|
||
disable_button:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/DisableButton' }
|
||
document_id: { type: integer }
|
||
id: { type: string }
|
||
name: { type: string }
|
||
permissions_editable: { type: boolean }
|
||
storage_type: { type: string }
|
||
|
||
Document:
|
||
type: object
|
||
properties:
|
||
author: { $ref: '#/components/schemas/User' }
|
||
bundles:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Bundle' }
|
||
created_at: { type: string }
|
||
dashboard_id: { type: integer }
|
||
disable_button:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/DisableButton' }
|
||
disk_id: { type: string }
|
||
document_copied_from: { type: integer }
|
||
files:
|
||
type: array
|
||
items: { type: integer }
|
||
folders:
|
||
type: array
|
||
items: { type: integer }
|
||
has_digital_signature: { type: boolean }
|
||
has_public_link: { type: boolean }
|
||
has_qr_code: { type: boolean }
|
||
has_stamp: { type: boolean }
|
||
id: { type: integer }
|
||
is_folder: { type: boolean }
|
||
name: { type: string }
|
||
path: { type: string }
|
||
project_id: { type: integer }
|
||
public_link: { type: string }
|
||
public_link_available: { type: boolean }
|
||
public_link_expiration_time: { type: string }
|
||
public_link_id: { type: string }
|
||
type: { $ref: '#/components/schemas/DocumentType' }
|
||
workspace_id: { type: string }
|
||
|
||
DocumentCompanyMap:
|
||
type: object
|
||
properties:
|
||
result:
|
||
type: object
|
||
additionalProperties: { type: integer }
|
||
|
||
DocumentType:
|
||
type: string
|
||
enum:
|
||
- workspace
|
||
- dashboard
|
||
- cloud
|
||
- bim
|
||
- bimv2
|
||
- project
|
||
- folder
|
||
- root
|
||
- surface
|
||
- pdf
|
||
- xlsx
|
||
- orthophoto
|
||
- dem
|
||
- dxf
|
||
- ksg
|
||
- docx
|
||
- dwg
|
||
|
||
DownloadURLResponse:
|
||
type: object
|
||
properties:
|
||
download_url: { type: string }
|
||
|
||
GetDiskListResponse:
|
||
type: object
|
||
properties:
|
||
disks:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Disk' }
|
||
|
||
IsDataSourceFolderResponse:
|
||
type: object
|
||
properties:
|
||
result: { type: boolean }
|
||
|
||
MoveBundleRequest:
|
||
type: object
|
||
required: [bundle_ids]
|
||
properties:
|
||
bundle_ids:
|
||
type: array
|
||
items: { type: string }
|
||
|
||
MultipartUpload:
|
||
type: object
|
||
properties:
|
||
upload_id: { type: string }
|
||
|
||
Page:
|
||
type: object
|
||
properties:
|
||
id: { type: string }
|
||
page_order: { type: integer }
|
||
thumbnail: { type: string }
|
||
|
||
PatchDocument:
|
||
type: object
|
||
required: [name]
|
||
properties:
|
||
name: { type: string }
|
||
|
||
PatchDocumentPath:
|
||
type: object
|
||
required: [parent_id]
|
||
properties:
|
||
parent_id: { type: integer }
|
||
|
||
PermissionType:
|
||
type: object
|
||
properties:
|
||
label: { type: string }
|
||
value: { type: string }
|
||
|
||
ServiceAccount:
|
||
type: object
|
||
properties:
|
||
id: { type: string }
|
||
name: { type: string }
|
||
type: { type: string }
|
||
username: { type: string }
|
||
|
||
ServiceAccountsResponse:
|
||
type: object
|
||
properties:
|
||
service_account:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/ServiceAccount' }
|
||
|
||
User:
|
||
type: object
|
||
properties:
|
||
companies:
|
||
type: array
|
||
items: { type: integer }
|
||
departments:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/UserDepartment' }
|
||
first_name: { type: string }
|
||
id: { type: integer }
|
||
is_superuser: { type: boolean }
|
||
last_name: { type: string }
|
||
positions:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/UserPosition' }
|
||
service_account_id: { type: string }
|
||
username: { type: string }
|
||
|
||
UserDepartment:
|
||
type: object
|
||
properties:
|
||
id: { type: integer }
|
||
service_account_id: { type: string }
|
||
|
||
UserPosition:
|
||
type: object
|
||
properties:
|
||
id: { type: integer }
|
||
service_account_id: { type: string }
|
||
|
||
Workflow:
|
||
type: object
|
||
properties:
|
||
company_id: { type: integer }
|
||
created_at: { type: string }
|
||
id: { type: string }
|
||
name: { type: string }
|
||
state: { type: string }
|
||
task_runs:
|
||
type: array
|
||
items: { type: object, additionalProperties: true }
|
||
tasks:
|
||
type: array
|
||
items: { type: object, additionalProperties: true }
|
||
updated_at: { type: string }
|
||
valid_until: { type: string }
|