Skip to content

ТЗ: Workbench для API документации

1. Цель

Нужно реализовать интерактивный модуль Workbench, похожий по идее на Stripe Workbench Console: пользователь находится на странице метода API, видит готовую форму запроса, может изменить параметры, отправить запрос и увидеть ответ без перехода из документации.

Модуль должен покрывать:

  • все страницы раздела docs/tcp/**;
  • все страницы раздела docs/client-api/rest/**;
  • React-интерфейс внутри существующего приложения app;
  • небольшой backend-модуль, через который Workbench будет выполнять запросы;
  • TCP-вызовы только через backend, так как браузер не может безопасно и напрямую работать с raw TCP.

2. Контекст текущего проекта

Проект сейчас устроен так:

  • документация собирается через MkDocs Material;
  • исходники документации находятся в docs;
  • основная навигация описана в mkdocs.yml;
  • React-код находится в app;
  • Vite собирает app/main.jsx в UMD bundle docs/assets/app/bundle.js;
  • React-бандл подключается глобально через extra_javascript в mkdocs.yml;
  • текущие React-модули монтируются по root-элементам: root_profile, root_login, root_contact и т.д.;
  • страницы TCP и REST API сейчас являются markdown-страницами с таблицами параметров и JSON-примерами;
  • формального OpenAPI/JSON Schema слоя для всех методов сейчас нет.

Вывод: Workbench не должен полагаться на парсинг HTML/markdown как на основной источник схемы метода. Нужен отдельный слой метаданных или сгенерированный manifest.

3. Основное решение

Рекомендуемая архитектура:

  1. В документации появляется machine-readable manifest методов.
  2. React Workbench загружает manifest/схему текущего метода.
  3. Пользователь редактирует запрос в UI.
  4. Frontend отправляет нормализованный запрос в backend Workbench.
  5. Backend проверяет права, метод, окружение и payload.
  6. Backend выполняет REST или TCP-вызов к целевому API.
  7. Frontend показывает нормализованный ответ.

4. Размещение Workbench на страницах

Workbench должен отображаться на страницах методов:

  • /tcp/...;
  • /client-api/rest/....

Workbench не должен автоматически отображаться на обзорных страницах, если метод не описан в manifest.

Рекомендуемый способ монтирования:

  • добавить MkDocs hook, который на этапе сборки вставляет контейнер:
<div id="root_workbench" data-workbench-method="tcp.finance.BalanceIn"></div>
  • в app/main.jsx добавить монтирование React-компонента, если найден root_workbench.

Альтернатива: React сам проверяет window.location.pathname и динамически создает контейнер. Этот вариант проще для прототипа, но хуже контролирует, на каких страницах появляется модуль.

5. Manifest методов

Нужно добавить manifest, например:

docs/assets/workbench/manifest.json

Минимальная структура для TCP:

{
  "id": "tcp.finance.BalanceIn",
  "page": "/tcp/finance/BalanceIn/",
  "protocol": "tcp",
  "title": "BalanceIn",
  "category": "finance",
  "command": "BalanceIn",
  "auth": "manager",
  "executionMode": "mutating",
  "parameters": [
    {
      "name": "login",
      "type": "int",
      "required": true,
      "location": "body",
      "description": "User login ID"
    },
    {
      "name": "amount",
      "type": "double",
      "required": true,
      "location": "body",
      "description": "Positive amount"
    }
  ],
  "requestExample": {
    "login": 123456,
    "amount": 1000,
    "comment": "Initial deposit"
  }
}

Минимальная структура для REST:

{
  "id": "rest.trade.OpenTrade",
  "page": "/client-api/rest/trade/OpenTrade/",
  "protocol": "rest",
  "title": "Open Trade",
  "method": "POST",
  "path": "/trade/open",
  "auth": "customer",
  "contentType": "application/json",
  "executionMode": "mutating",
  "parameters": [],
  "requestExample": {
    "login": 1001,
    "cmd": 0,
    "volume": 100,
    "symbol": "EURUSD",
    "sl": 1.08,
    "tp": 1.1
  }
}

Варианты наполнения manifest:

  • вручную описать методы в одном JSON-файле;
  • добавить workbench front matter в каждую markdown-страницу;
  • написать генератор, который извлекает таблицы и JSON-примеры из markdown, а спорные места закрываются override-файлом.

Рекомендуемое решение для первого этапа: начать с manifest.json и 5-10 методов, потом автоматизировать генерацию.

5.1 Синхронизация manifest с документацией

Manifest не должен поддерживаться как независимый ручной JSON без проверки, иначе он быстро разойдется с markdown-страницами.

Рекомендуемая модель синхронизации:

  1. Источник правды находится рядом с документацией метода:
---
title: "ScaleTrade TCP API - Balance In"
workbench:
  id: tcp.finance.BalanceIn
  protocol: tcp
  command: BalanceIn
  auth: manager
  executionMode: mutating
---
  1. Markdown-страница продолжает содержать таблицу параметров и Request Example.
  2. Скрипт сборки scripts/mkdocs_workbench_manifest.py читает front matter, таблицы параметров и JSON-примеры.
  3. Скрипт генерирует docs/assets/workbench/manifest.json.
  4. Для нестандартных методов используется override-файл, например docs/assets/workbench/overrides.json.
  5. CI проверяет, что сгенерированный manifest актуален. Если markdown изменился, а manifest не обновлен, сборка падает.

На первом этапе допустим ручной manifest для ограниченного набора методов, но в production-версии нужен генератор и CI-check.

6. React-модуль

Рекомендуемая структура:

app/components/Workbench/Workbench.jsx
app/components/Workbench/Workbench.module.css
app/components/Workbench/fields/
app/api/workbench.js

В app/main.jsx добавить:

  • импорт Workbench;
  • проверку document.getElementById("root_workbench");
  • чтение data-workbench-method;
  • ReactDOM.render(<Workbench methodId={methodId} />, root).

UI должен содержать:

  • badge протокола: TCP или REST;
  • название метода/команды;
  • выбранное окружение: Sandbox, Development, Production, если разрешено;
  • для TCP: поля host, port, auth token внутри Workbench;
  • сохранение TCP-настроек сессии в browser storage только если пользователь явно включил сохранение;
  • форму параметров на основе manifest;
  • режим raw JSON для сложных payload;
  • автозаполнение из requestExample;
  • кнопку reset к примеру из документации;
  • кнопку отправки;
  • копирование request/response;
  • отображение ответа в форматированном JSON;
  • transport metadata: HTTP status, duration, request id/correlation id;
  • понятные ошибки: validation error, auth error, timeout, TCP connection error, upstream API error.

Если пользователь не авторизован, Workbench должен показывать prompt на вход и блокировать отправку.

7. Backend-модуль Workbench

Backend нужен обязательно, потому что:

  • TCP нельзя вызывать из браузера напрямую;
  • браузер должен отправлять TCP-параметры на backend по защищенному WebSocket, а backend уже открывает TCP-соединение;
  • нельзя доверять frontend выбору command, execution mode и разрешений;
  • нужен audit log и rate limit.

Рекомендуемый стек: Node.js/TypeScript. Основной backend ScaleTrade работает на C++, поэтому Workbench bridge логично делать отдельным небольшим Node.js-модулем, который будет жить рядом со статической документацией и проксироваться через тот же nginx/host/port.

Ключевое deployment-требование:

  • Workbench backend должен работать из-под основного Docker image документации;
  • внешний порт остается тем же, что сейчас у nginx;
  • nginx отдает статические страницы документации;
  • nginx проксирует /workbench/* во внутренний Node.js bridge;
  • WebSocket /workbench/tcp должен работать через nginx reverse proxy с Upgrade/Connection headers;
  • Node.js bridge слушает только localhost/internal port внутри контейнера, например 127.0.0.1:3100.

Логическая структура Node.js-модуля:

workbench/
  package.json
  tsconfig.json
  src/
    server.ts         # HTTP endpoints and WebSocket route
    tcpBridge.ts      # WS session -> TCP connection lifecycle
    tcpCodec.ts       # ScaleTrade TCP serialize/parse
    manifest.ts       # method schema loading and validation
    security.ts       # auth, host policy, rate limits, masking
    audit.ts          # execution audit log

Минимальные endpoint-ы и каналы:

GET /workbench/config

Возвращает только frontend-safe настройки:

{
  "enabled": true,
  "environments": [
    {
      "id": "sandbox",
      "label": "Sandbox",
      "default": true
    }
  ],
  "limits": {
    "timeoutMs": 10000,
    "maxPayloadBytes": 65536
  }
}

GET /workbench/methods/:methodId

Возвращает схему метода для текущего пользователя.

POST /workbench/execute

Выполняет REST или TCP-запрос.

Request:

{
  "methodId": "rest.trade.OpenTrade",
  "environment": "sandbox",
  "payload": {},
  "query": {},
  "headers": {}
}

Response:

{
  "ok": true,
  "protocol": "rest",
  "status": 200,
  "durationMs": 120,
  "body": {},
  "error": null
}

Error response:

{
  "ok": false,
  "protocol": "tcp",
  "status": null,
  "durationMs": 10000,
  "body": null,
  "error": {
    "code": "TCP_TIMEOUT",
    "message": "TCP request timed out"
  }
}

WS /workbench/tcp

WebSocket-канал для TCP-сессии Workbench.

Назначение:

  • принять от React host, port, auth token и параметры сессии;
  • открыть TCP-соединение с указанным endpoint;
  • выполнить auth handshake с TCP API;
  • принимать команды Workbench;
  • возвращать ответы и события состояния соединения.

Frontend не должен открывать raw TCP. Он работает только с WebSocket backend-модуля.

Пример сообщения инициализации:

{
  "type": "connect",
  "requestId": "req_001",
  "host": "tcp.example.com",
  "port": 443,
  "authToken": "manager-or-api-token",
  "tls": true,
  "timeoutMs": 10000
}

Пример выполнения TCP-команды:

{
  "type": "execute",
  "requestId": "req_002",
  "methodId": "tcp.finance.BalanceIn",
  "payload": {
    "login": 123456,
    "amount": 1000,
    "comment": "Initial deposit"
  }
}

Пример ответа:

{
  "type": "result",
  "requestId": "req_002",
  "ok": true,
  "durationMs": 84,
  "body": {
    "accepted": true,
    "order": 1242279
  }
}

8. REST execution flow

  1. React отправляет запрос в /workbench/execute.
  2. Backend проверяет авторизацию.
  3. Backend проверяет, что methodId есть в manifest.
  4. Backend проверяет права пользователя и доступность окружения.
  5. Backend валидирует payload по схеме метода.
  6. Backend собирает upstream REST-запрос из server-side config.
  7. Backend отправляет запрос в целевой REST API.
  8. Backend возвращает нормализованный ответ во frontend.

Важно: frontend не должен иметь возможность передать произвольный URL для REST-вызова.

9. TCP execution flow

  1. React открывает WS /workbench/tcp.
  2. React отправляет connect с host, port, authToken, TLS-флагом и timeout.
  3. Backend проверяет авторизацию пользователя Workbench.
  4. Backend валидирует host, port, лимиты и право пользователя открывать TCP-сессии.
  5. Backend открывает TCP-соединение с указанным host/port.
  6. Backend выполняет auth handshake с TCP API, используя token из Workbench.
  7. React отправляет execute с methodId и payload.
  8. Backend проверяет methodId, execution mode, payload и confirmation-флаг для mutating-методов.
  9. Backend сериализует payload в формат ScaleTrade TCP protocol.
  10. Backend отправляет запрос в TCP API.
  11. Backend читает ответ с timeout.
  12. Backend парсит ответ и возвращает нормализованный JSON через WebSocket.

Требования к TCP-адаптеру:

  • host, port, authToken задаются в Workbench UI и передаются на backend только по HTTPS/WSS;
  • backend не сохраняет authToken без явного требования;
  • backend маскирует authToken во всех логах;
  • backend валидирует host/port и не должен становиться неограниченным TCP-прокси;
  • timeout на connect и read;
  • max payload size;
  • max response size;
  • structured logs;
  • запрет логирования токенов, паролей, OTP и секретов;
  • connection pool только если протокол поддерживает безопасное переиспользование соединения;
  • явная обработка ошибок framing, auth, timeout, connection refused, malformed response.

В качестве основы для формата TCP-команд нужно использовать примеры из страницы https://scaletrade.com/tcp/ и конкретных страниц методов docs/tcp/**.

Открытая зависимость: нужно подтвердить точный TCP framing, auth handshake, request envelope, response envelope и correlation id.

10. Права и безопасность

Обязательные требования:

  • выполнение запросов доступно только авторизованным пользователям;
  • production execution выключен по умолчанию;
  • backend хранит allowlist исполняемых методов;
  • mutating-методы требуют дополнительного confirmation step;
  • dangerous-методы отключены, пока отдельно не разрешены;
  • rate limit по user, IP, method, environment;
  • audit log на каждую попытку выполнения;
  • маскирование sensitive полей: password, token, secret, otp, authorization;
  • frontend не является источником правды по правам, endpoint, command и окружению;
  • если host/port вводятся пользователем, нужно отдельно согласовать SSRF-защиту: allowlist доменов, запрет localhost/internal ranges или tenant-level allowlist.

Режимы выполнения метода:

  • disabled - показываем форму/пример, но не даем отправить;
  • read - read-only выполнение;
  • mutating - выполнение с подтверждением;
  • dangerous - скрыто или доступно только внутренним ролям.

Рекомендуемый rollout:

  1. Sandbox read-only REST.
  2. Sandbox read-only TCP.
  3. Sandbox selected mutating с confirmation.
  4. Production только после согласования ролей, audit и rollback-политики.

11. Изменения в документации

Нужно добавить:

  • docs/assets/workbench/manifest.json;
  • опционально страницу docs/workbench.md с описанием Workbench;
  • опционально скрипт scripts/mkdocs_workbench_manifest.py для генерации manifest;
  • MkDocs hook для вставки root_workbench на нужные страницы.

Manifest должен версионироваться вместе с документацией, чтобы форма Workbench соответствовала опубликованной странице метода.

12. Docker и nginx reverse proxy

Текущий Dockerfile собирает MkDocs и в runtime stage использует nginx:alpine, который отдает только статику:

FROM nginx:alpine AS prod
EXPOSE 80
COPY --from=build /out/ /usr/share/nginx/html/
CMD ["nginx", "-g", "daemon off;"]

Для Workbench нужно доработать runtime image так, чтобы внутри одного контейнера работали:

  • nginx на внешнем порту 80;
  • Node.js Workbench bridge на внутреннем порту, например 3100;
  • nginx reverse proxy для /workbench/.

Варианты запуска процессов:

  • использовать supervisord или s6-overlay для запуска nginx и Node bridge;
  • либо использовать простой entrypoint script, который стартует Node bridge в background и затем запускает nginx foreground.

Для production предпочтительнее process supervisor, потому что он корректнее обрабатывает restart/exit одного из процессов.

Пример nginx location:

location /workbench/ {
    proxy_pass http://127.0.0.1:3100/workbench/;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
}

Для WebSocket upgrade также нужен map в nginx config:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

Dockerfile должен быть расширен:

  • добавить stage сборки Workbench bridge через Node.js;
  • скопировать собранный bridge в runtime image;
  • добавить nginx config;
  • добавить supervisor/entrypoint;
  • прокинуть environment variables для bridge.

Минимальные переменные:

WORKBENCH_PORT=3100
WORKBENCH_ALLOWED_HOSTS=*.scaletrade.com,*.internal.example
WORKBENCH_MAX_PAYLOAD_BYTES=65536
WORKBENCH_CONNECT_TIMEOUT_MS=10000
WORKBENCH_READ_TIMEOUT_MS=10000
WORKBENCH_AUDIT_ENABLED=true

13. Acceptance criteria

Функциональность считается готовой, если:

  • Workbench отображается на страницах методов tcp и client-api/rest;
  • Workbench не отображается на нерелевантных страницах;
  • request prefilled из manifest;
  • пользователь может редактировать поля и raw JSON;
  • required-поля валидируются до отправки;
  • backend отклоняет неизвестный methodId;
  • backend отклоняет произвольные REST URL;
  • backend валидирует пользовательские TCP host/port по согласованной security policy;
  • TCP-запросы выполняются только через backend;
  • успешный ответ отображается в UI;
  • ошибки транспорта и ошибки upstream API различаются;
  • неавторизованный пользователь не может выполнить запрос;
  • mutating-методы требуют подтверждения;
  • /workbench/config, /workbench/methods/:methodId, /workbench/execute доступны через тот же host/port, что и документация;
  • WS /workbench/tcp работает через nginx reverse proxy;
  • Node.js bridge не доступен напрямую снаружи контейнера;
  • текущая сборка docs/assets/app/bundle.js и bundle.css продолжает работать;
  • существующие React-виджеты login/profile/contact не ломаются.

14. План реализации

Этап 1. Прототип

  • Создать manifest для 5-10 методов.
  • Добавить React-компонент Workbench.
  • Добавить mount logic в app/main.jsx.
  • Добавить mock/stub backend endpoint.
  • Реализовать отображение схемы, JSON editor и локальную валидацию.

Этап 2. REST execution

  • Реализовать /workbench/config.
  • Реализовать /workbench/methods/:methodId.
  • Реализовать /workbench/execute для REST.
  • Добавить auth, allowlist, audit log и rate limit.

Этап 3. TCP execution

  • Реализовать TCP adapter.
  • Добавить server-side конфиг окружений.
  • Реализовать сериализацию и парсинг TCP envelope.
  • Включить несколько read-only TCP-команд в sandbox.

Этап 4. Покрытие документации

  • Расширить manifest на все docs/tcp/** и docs/client-api/rest/**.
  • Классифицировать методы по executionMode.
  • Добавить masking rules.
  • Добавить confirmation UX для mutating-методов.

Этап 5. Hardening

  • Добавить role-based access.
  • Добавить метрики и мониторинг.
  • Добавить интеграционные тесты против sandbox REST/TCP.
  • Принять отдельное решение по production execution.

15. Вопросы для согласования

  1. Node.js bridge запускаем в том же контейнере через supervisord или через entrypoint script?
  2. Какой точный формат ScaleTrade TCP protocol: framing, auth handshake, envelope, correlation id?
  3. Какие роли пользователей могут выполнять TCP/server API запросы?
  4. Workbench должен использовать customer auth, manager auth или оба режима в зависимости от раздела?
  5. Какие окружения доступны в первом релизе: только sandbox, dev, staging, production?
  6. Разрешаем ли mutating-операции из документации или стартуем строго с read-only?
  7. Нужна ли изоляция по brand/company/tenant?
  8. Нужно ли пользователю сохранять свои environments/tokens или все окружения только server-side?
  9. Какой срок хранения audit log?
  10. Manifest делаем вручную на первом этапе или сразу пишем генератор из markdown?
  11. Какая security policy для пользовательского host/port: wildcard allowlist, tenant-level allowlist или запрет private/internal ranges?

16. Решения, которые предлагаю принять сейчас

  • Начать с server-side/static manifest, а не с парсинга markdown в браузере.
  • Первый релиз делать sandbox-only.
  • Production execution отключить до отдельного security review.
  • TCP выполнять только через backend.
  • Workbench bridge реализовать на Node.js/TypeScript.
  • Запускать Node.js bridge внутри Docker image документации и проксировать /workbench/ через nginx.
  • Добавлять Workbench на страницы через MkDocs hook с явным root_workbench.
  • Для всех mutating-методов требовать подтверждение.
  • host, port, auth token вводятся в Workbench UI и отправляются в bridge по WSS.