iac/apps/workspaces/openapi.yaml

907 lines
32 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.0.3
info:
title: workspaces-api
description: >-
HTTP API сервиса workspaces-api (репозиторий `pdm/workspaces-api`).
Хранит рабочие области (workspaces), документы, состояния (states),
динамические состояния и приложения (apps). Схема составлена по
маршрутам `cmd/api/bootstrap.go` и структурам пакетов `workspace` и `apps`.
Группы маршрутов:
* публичные (`/ping`, `/metrics`) — без middleware;
* `/api/v1` — внешний API, требует JWT (middleware `auth.JWTToCtx`);
* `/internal/v1`, `/internal/v2` — внутренний API без JWT
(доступ ограничивается сетевой политикой/ingress).
version: "1.0.0"
servers:
- url: https://api.sarex.io/workspaces
description: production
- url: https://api.preprod.sarex.io/workspaces
description: preprod
- url: https://stage-api.sarex.io/workspaces
description: stage
- url: http://localhost:6666
description: local
tags:
- name: health
description: Liveness/readiness и метрики
- name: workspaces
description: Рабочие области
- name: documents
description: Документы рабочих областей
- name: states
description: Состояния рабочих областей
- name: dynamic-states
description: Динамические состояния
- name: apps
description: Приложения и их инстансы
- name: internal
description: Внутренние эндпоинты (без JWT)
security:
- bearerAuth: []
paths:
# ------------------------------------------------------------------ health
/ping:
get:
tags: [health]
summary: Liveness/readiness проба
security: []
responses:
"200":
description: Сервис жив
/metrics:
get:
tags: [health]
summary: Метрики Prometheus
security: []
responses:
"200":
description: Метрики в формате Prometheus
content:
text/plain:
schema:
type: string
# -------------------------------------------------------------- api/v1 · ws
/api/v1/workspaces:
post:
tags: [workspaces]
summary: Создать рабочую область (v2)
description: Создаёт рабочую область по списку id документов (`CreateV2Workspace`).
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateV2WorkspaceRequest"
responses:
"200":
description: Созданная рабочая область
content:
application/json:
schema:
$ref: "#/components/schemas/Workspace"
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{ws_id}:
get:
tags: [workspaces]
summary: Получить рабочую область
parameters:
- $ref: "#/components/parameters/WsId"
- name: archived
in: query
description: Включать архивные сущности
schema: { type: integer, enum: [0, 1] }
- name: state
in: query
description: uuid состояния; при указании в ответ добавляется последнее состояние
schema: { type: string, format: uuid }
responses:
"200":
description: Рабочая область
content:
application/json:
schema:
$ref: "#/components/schemas/Workspace"
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{id}/archive:
post:
tags: [workspaces]
summary: Архивировать рабочую область
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{id}/unarchive:
post:
tags: [workspaces]
summary: Разархивировать рабочую область
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{id}/cache:
delete:
tags: [workspaces]
summary: Очистить кеш бандлов рабочей области
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { description: Кеш очищен }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{ws_id}/states:
get:
tags: [states]
summary: Список состояний рабочей области
parameters:
- $ref: "#/components/parameters/WsId"
- name: expand
in: query
description: "`state_json` — вернуть полные данные состояний (иначе — краткий список)"
schema: { type: string, enum: [state_json] }
responses:
"200":
description: Состояния
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/State" }
"500": { $ref: "#/components/responses/InternalError" }
post:
tags: [states]
summary: Создать состояние рабочей области
parameters: [ { $ref: "#/components/parameters/WsId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CreateStateRequest" }
responses:
"200":
description: Созданное состояние
content:
application/json:
schema: { $ref: "#/components/schemas/State" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{ws_id}/documents:
post:
tags: [documents]
summary: Добавить документ в рабочую область
parameters: [ { $ref: "#/components/parameters/WsId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/AddDocumentRequest" }
responses:
"200": { description: Документ добавлен }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{ws_id}/documents/{doc_id}:
delete:
tags: [documents]
summary: Удалить документ из рабочей области
parameters:
- $ref: "#/components/parameters/WsId"
- $ref: "#/components/parameters/DocId"
responses:
"200": { description: Документ удалён }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{ws_id}/dynamic_states:
get:
tags: [dynamic-states]
summary: Список динамических состояний рабочей области
parameters: [ { $ref: "#/components/parameters/WsId" } ]
responses:
"200":
description: Динамические состояния
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/DynamicState" }
"500": { $ref: "#/components/responses/InternalError" }
post:
tags: [dynamic-states]
summary: Создать динамическое состояние
parameters: [ { $ref: "#/components/parameters/WsId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CreateDynamicStateRequest" }
responses:
"200":
description: Созданное динамическое состояние
content:
application/json:
schema: { $ref: "#/components/schemas/DynamicState" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/workspaces/{workspace_id}/apps/{app_id}/instances:
post:
tags: [apps]
summary: Создать инстанс приложения в рабочей области
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/AppId"
responses:
"200":
description: Созданный инстанс
content:
application/json:
schema: { $ref: "#/components/schemas/AppInstance" }
"400":
description: Некорректный запрос или дубликат инстанса
content:
application/json:
schema: { $ref: "#/components/schemas/ErrorResponse" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
# -------------------------------------------------------- api/v1 · documents
/api/v1/documents/{doc_id}:
patch:
tags: [documents]
summary: Обновить документ
description: "Не реализовано в текущем коде — всегда возвращает 400 `Implement me`."
parameters: [ { $ref: "#/components/parameters/DocId" } ]
responses:
"400": { $ref: "#/components/responses/BadRequest" }
/api/v1/documents/{id}/archive:
post:
tags: [documents]
summary: Архивировать документ
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/documents/{id}/unarchive:
post:
tags: [documents]
summary: Разархивировать документ
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
# ----------------------------------------------------------- api/v1 · states
/api/v1/states/{id}:
get:
tags: [states]
summary: Получить состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200":
description: Состояние
content:
application/json:
schema: { $ref: "#/components/schemas/State" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
patch:
tags: [states]
summary: Изменить состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/SetStateRequest" }
responses:
"200": { $ref: "#/components/responses/Ok" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
delete:
tags: [states]
summary: Удалить (архивировать) состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/states/{id}/archive:
post:
tags: [states]
summary: Архивировать состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/states/{id}/unarchive:
post:
tags: [states]
summary: Разархивировать состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { $ref: "#/components/responses/Ok" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
# -------------------------------------------------- api/v1 · dynamic states
/api/v1/dynamic_states/{id}:
get:
tags: [dynamic-states]
summary: Получить динамическое состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200":
description: Динамическое состояние
content:
application/json:
schema: { $ref: "#/components/schemas/DynamicState" }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
patch:
tags: [dynamic-states]
summary: Изменить динамическое состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/SetDynamicStateRequest" }
responses:
"200": { $ref: "#/components/responses/Ok" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
delete:
tags: [dynamic-states]
summary: Удалить динамическое состояние
parameters: [ { $ref: "#/components/parameters/Id" } ]
responses:
"200": { description: Удалено }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
# ------------------------------------------------------------ api/v1 · apps
/api/v1/company/{company_id}/apps:
get:
tags: [apps]
summary: Приложения компании
parameters: [ { $ref: "#/components/parameters/CompanyId" } ]
responses:
"200":
description: Список приложений
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/App" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
post:
tags: [apps]
summary: Создать приложение
parameters: [ { $ref: "#/components/parameters/CompanyId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/AppAttributes" }
responses:
"200":
description: Созданное приложение
content:
application/json:
schema: { $ref: "#/components/schemas/App" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/apps/{app_id}:
patch:
tags: [apps]
summary: Изменить приложение
parameters: [ { $ref: "#/components/parameters/AppId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/ChangeAppRequest" }
responses:
"200":
description: Обновлённое приложение
content:
application/json:
schema: { $ref: "#/components/schemas/App" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/apps/{app_id}/archive:
post:
tags: [apps]
summary: Архивировать приложение
parameters: [ { $ref: "#/components/parameters/AppId" } ]
responses:
"200": { description: Приложение архивировано }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/apps/{app_id}/unarchive:
post:
tags: [apps]
summary: Разархивировать приложение
parameters: [ { $ref: "#/components/parameters/AppId" } ]
responses:
"200": { description: Приложение разархивировано }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/app-instances/{instance_id}/archive:
post:
tags: [apps]
summary: Архивировать инстанс приложения
parameters: [ { $ref: "#/components/parameters/InstanceId" } ]
responses:
"200": { description: Инстанс архивирован }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/api/v1/app-instances/{instance_id}/unarchive:
post:
tags: [apps]
summary: Разархивировать инстанс приложения
parameters: [ { $ref: "#/components/parameters/InstanceId" } ]
responses:
"200": { description: Инстанс разархивирован }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
# -------------------------------------------------------------- internal/v1
/internal/v1/workspaces:
post:
tags: [internal]
summary: Создать рабочую область (v1, по ссылкам на бандлы)
description: Внутренний эндпоинт без JWT. Создаёт рабочую область по списку URL бандлов.
security: []
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CreateWorkspaceRequest" }
responses:
"200":
description: Созданная рабочая область
content:
application/json:
schema: { $ref: "#/components/schemas/Workspace" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/internal/v1/workspaces/{ws_id}/documents:
post:
tags: [internal]
summary: Добавить документ в рабочую область (без проверки прав)
security: []
parameters: [ { $ref: "#/components/parameters/WsId" } ]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/AddDocumentRequest" }
responses:
"200": { description: Документ добавлен }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/internal/v1/workspaces/{ws_id}/states:
get:
tags: [internal]
summary: Список состояний рабочей области (internal)
security: []
parameters: [ { $ref: "#/components/parameters/WsId" } ]
responses:
"200":
description: Состояния
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/State" }
"500": { $ref: "#/components/responses/InternalError" }
# -------------------------------------------------------------- internal/v2
/internal/v2/workspaces:
post:
tags: [internal]
summary: Создать рабочую область (v2, internal)
security: []
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CreateV2WorkspaceRequest" }
responses:
"200":
description: Созданная рабочая область
content:
application/json:
schema: { $ref: "#/components/schemas/Workspace" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/internal/v2/workspaces/is_workspaces_v2:
post:
tags: [internal]
summary: Проверить, являются ли рабочие области v2
security: []
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/IsWorkspaceV2Request" }
responses:
"200":
description: Карта id → признак v2
content:
application/json:
schema: { $ref: "#/components/schemas/IsWorkspaceV2Response" }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
/internal/v2/documents/{docs_id}:
delete:
tags: [internal]
summary: Удалить документ (internal)
security: []
parameters:
- name: docs_id
in: path
required: true
schema: { type: string }
responses:
"200": { description: Документ удалён }
"404": { $ref: "#/components/responses/NotFound" }
"500": { $ref: "#/components/responses/InternalError" }
/internal/v2/documents/restore:
patch:
tags: [internal]
summary: Восстановить документы и рабочие области (internal)
security: []
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/RestoreDocumentsRequest" }
responses:
"200": { description: Восстановлено }
"400": { $ref: "#/components/responses/BadRequest" }
"500": { $ref: "#/components/responses/InternalError" }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: >-
JWT в заголовке `Authorization: Bearer <token>`. Токен также может
передаваться query-параметром `jwt` (удаляется middleware после разбора).
parameters:
Id:
name: id
in: path
required: true
schema: { type: string, format: uuid }
WsId:
name: ws_id
in: path
required: true
schema: { type: string, format: uuid }
WorkspaceId:
name: workspace_id
in: path
required: true
schema: { type: string, format: uuid }
DocId:
name: doc_id
in: path
required: true
schema: { type: string }
AppId:
name: app_id
in: path
required: true
schema: { type: string, format: uuid }
InstanceId:
name: instance_id
in: path
required: true
schema: { type: string, format: uuid }
CompanyId:
name: company_id
in: path
required: true
schema: { type: integer, format: int64 }
responses:
Ok:
description: Успех
content:
application/json:
schema: { $ref: "#/components/schemas/OkResponse" }
BadRequest:
description: Некорректный запрос
content:
application/json:
schema: { $ref: "#/components/schemas/ErrorResponse" }
NotFound:
description: Ресурс не найден
content:
application/json:
schema: { $ref: "#/components/schemas/ErrorResponse" }
InternalError:
description: Внутренняя ошибка сервера
content:
application/json:
schema: { $ref: "#/components/schemas/ErrorResponse" }
schemas:
ErrorResponse:
type: object
properties:
error: { type: string }
required: [error]
OkResponse:
type: object
properties:
ok: { type: boolean }
required: [ok]
# ---- requests ----
CreateWorkspaceRequest:
type: object
description: "`workspace/api/workspace/create.go` (v1)"
properties:
title: { type: string, maxLength: 254 }
subtitle: { type: string, maxLength: 254 }
target: { type: integer, nullable: true }
files:
type: array
description: Ссылки на бандлы документов
items: { type: string }
features:
type: array
items: { type: string }
required: [title, files]
CreateV2WorkspaceRequest:
type: object
description: "`workspace/api/workspace/createV2.go` (v2)"
properties:
title: { type: string, maxLength: 254 }
subtitle: { type: string, maxLength: 254 }
target: { type: integer, nullable: true }
documents:
type: array
description: id документов сервиса documentation
items: { type: integer, format: int64 }
features:
type: array
items: { type: string }
auto_created: { type: boolean }
required: [documents]
IsWorkspaceV2Request:
type: object
properties:
ids:
type: array
items: { type: string }
required: [ids]
IsWorkspaceV2Response:
type: object
properties:
result:
type: object
additionalProperties: { type: boolean }
description: Карта uuid рабочей области → является ли она v2
AddDocumentRequest:
type: object
properties:
document_id: { type: integer, format: int64 }
required: [document_id]
RestoreDocumentsRequest:
type: object
properties:
document_ids:
type: array
minItems: 1
items: { type: integer, format: int64 }
required: [document_ids]
CreateStateRequest:
type: object
properties:
name: { type: string, maxLength: 254 }
data: { type: string }
dynamic_state_id: { type: string, format: uuid, nullable: true }
required: [name, data]
SetStateRequest:
type: object
description: Частичное обновление; все поля опциональны
properties:
name: { type: string, nullable: true }
is_default: { type: boolean, nullable: true }
data: { type: string, nullable: true }
CreateDynamicStateRequest:
type: object
properties:
name: { type: string, maxLength: 254 }
autoload: { type: boolean }
required: [name]
SetDynamicStateRequest:
type: object
properties:
name: { type: string, nullable: true }
autoload: { type: boolean, nullable: true }
AppAttributes:
type: object
description: "`apps/models.go`"
properties:
name: { type: string, minLength: 1, maxLength: 100 }
entrypoint: { type: string, minLength: 1, maxLength: 1024, format: uri }
devices:
type: array
items: { type: string, enum: [mobile, tablet, desktop] }
module_name: { type: string, minLength: 1, maxLength: 100 }
required: [name, entrypoint, devices, module_name]
ChangeAppRequest:
type: object
description: >-
Частичное обновление приложения; должно быть задано хотя бы одно поле
(`apps/api/changeapp.go`).
properties:
name: { type: string, maxLength: 100 }
entrypoint: { type: string, maxLength: 1024, format: uri }
devices:
type: array
items: { type: string, enum: [mobile, tablet, desktop] }
module_name: { type: string, maxLength: 100 }
# ---- models ----
Workspace:
type: object
description: "Агрегат рабочей области (`workspace.Model`)"
properties:
id: { type: string, format: uuid }
title: { type: string }
subtitle: { type: string }
features:
type: array
items: { type: string }
target: { type: integer, nullable: true }
auto_created: { type: boolean, nullable: true }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
documents:
type: array
items: { $ref: "#/components/schemas/Document" }
documents_v2:
type: array
items: { $ref: "#/components/schemas/DocumentV2" }
state:
allOf: [ { $ref: "#/components/schemas/State" } ]
nullable: true
app_instances:
type: array
items: { $ref: "#/components/schemas/AppInstance" }
Document:
type: object
description: "`workspace.ModelDocument`"
properties:
id: { type: string, format: uuid }
name: { type: string }
workspace_id: { type: string, format: uuid }
is_archived: { type: boolean }
kind: { type: string }
url: { type: string }
bundle: { type: string }
createdAt: { type: string, format: date-time }
updatedAt: { type: string, format: date-time }
DocumentV2:
type: object
description: "`workspace.ModelDocumentV2`"
properties:
id: { type: string, format: uuid }
document_id: { type: integer, format: int64 }
created_at: { type: string, format: date-time }
State:
type: object
description: "`workspace.ModelState`"
properties:
id: { type: string, format: uuid }
name: { type: string }
workspace_id: { type: string, format: uuid }
created_at: { type: string, format: date-time }
data: { type: string }
author_id: { type: integer, format: int64, nullable: true }
is_default: { type: boolean }
indestructible: { type: boolean }
dynamic_state_id: { type: string, format: uuid, nullable: true }
DynamicState:
type: object
description: "`workspace.ModelDynamicState`"
properties:
id: { type: string, format: uuid }
name: { type: string }
author_id: { type: integer, format: int64, nullable: true }
autoload: { type: boolean }
workspace_id: { type: string, format: uuid }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
states:
type: array
items: { $ref: "#/components/schemas/State" }
App:
type: object
description: "`apps.App`"
properties:
id: { type: string, format: uuid }
company_id: { type: integer, format: int64 }
name: { type: string }
entrypoint: { type: string }
devices:
type: array
items: { type: string, enum: [mobile, tablet, desktop] }
module_name: { type: string }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
AppInstance:
type: object
description: "`apps.AppInstance`"
properties:
id: { type: string, format: uuid }
app_id: { type: string, format: uuid }
workspace_id: { type: string, format: uuid }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
app:
allOf: [ { $ref: "#/components/schemas/App" } ]
nullable: true