Skip to content

Развёртывание Contour Node

Пошаговое руководство по установке узла на сервере компании и его регистрации в студии. Перед чтением рекомендуется ознакомиться с обзором и архитектурой.

1. Предварительные требования

На сервере компании (Contour Node)

ТребованиеЗначение
ОСLinux x86_64 (рекомендуется)
Docker / Docker Composeактуальная версия (узел и MinIO разворачиваются контейнерами)
FFmpegдля задач сжатия и водяного знака (обычно в составе образа узла)
Tailscaleустановлен и подключён в ту же tailnet, что и студия
Открытые порты внутри TailscaleapiPort (по умолчанию 8000) и minioPort (по умолчанию 9000)
Дискдостаточный объём под исходники + сжатые версии медиа компании

Порты не нужно публиковать в публичный интернет — весь трафик «студия ↔ узел» идёт по Tailscale. Публичный доступ к MinIO (для браузера пользователя) при необходимости решается через nginx-прокси на стороне студии — см. MinIO reverse-proxy.

На стороне студии NeuroCast

  • Backend развёрнут и подключён к той же Tailscale-сети.
  • Переменная APP_BASE_URL = Tailscale-IP машины со студией — именно по этому адресу узел отправляет webhook-колбэки, дефолт http://host.docker.internal:80, server/app/сontour/domain/services/ContourService.ts:106).
  • Учётная запись с ролью admin — регистрация компаний/узлов доступна только админу.

2. Сетевой контур (Tailscale)

  1. Установите Tailscale на сервере компании и выполните tailscale up.
  2. Убедитесь, что узел получил адрес из CGNAT-диапазона 100.64.0.0/10 (это весь диапазон Tailscale; конкретный узел — например 100.97.17.7).
  3. Зафиксируйте у узла статический Tailscale-IP (через --advertise / настройки tailnet), потому что этот IP:
    • сохраняется в студии как networkIp узла;
    • используется студией для построения apiBaseUrl = http://<networkIp>:<apiPort>;
    • используется для проверки источника webhook'ов — колбэк принимается только если IP отправителя совпадает с зарегистрированным networkIp.
  4. Если backend студии выходит в интернет через корпоративный/SSH-прокси — добавьте Tailscale-сеть в NO_PROXY, иначе запросы к узлу пойдут через прокси и не дойдут. Пример из .env.example:70:
    NO_PROXY=100.64.0.0/10,localhost,127.0.0.1,::1
    Если узлов несколько — можно перечислить их IP явно через запятую.

3. Регистрация узла в студии

Узел регистрируется не отдельно, а вместе с компанией — при создании компании студия автоматически провижинит для неё Contour Node.

Через UI

Студия → раздел «Компании» (web/src/pages/Companies.tsx) → создать компанию. Поля узла в форме:

Поле формыОбязательноеПо умолчаниюНазначение
nodeNameдачеловекочитаемое имя узла (напр. «Main Node»)
nodeNetworkIpдаTailscale-IP узла → networkIp
nodeNetworkHostnameдаhostname в сети
nodePublicUrlнетпубличный URL узла, если есть
nodeApiPortнет8000порт Contour API
nodeMinioPortнет9000порт MinIO
nodeMinioBucketдаимя MinIO-бакета компании

Валидация — Zod-схема CompanySchema.

Через API

POST /api/companies          (роль: admin)
Content-Type: application/json

{
  "name": "ООО Пример",
  "nodeName": "Main Node",
  "nodeNetworkIp": "100.97.17.7",
  "nodeNetworkHostname": "contour-main",
  "nodePublicUrl": "https://...",      // опц.
  "nodeApiPort": 8000,                 // опц.
  "nodeMinioPort": 9000,               // опц.
  "nodeMinioBucket": "company-prefix"
}

Что происходит на сервере

  1. Создаёт компанию с временным contourNodeId = 'pending-node-id'.
  2. Создаёт ContourNode из node-полей, status: 'offline', companyId = id компании.
  3. Проставляет компании реальный contourNodeId.
  4. Rollback: если создание узла упало — компания удаляется, возвращается ошибка «Не удалось создать Contour Node для компании. Компания не создана.»

⚠️ Уникальность «1 узел на компанию» держится только на этом коде — в MongoContourNodeSchema нет уникального индекса по companyId. Не вызывайте создание узла повторно для одной компании.

4. Проверка работоспособности (health-check)

После регистрации проверьте связь студия → узел:

  • UI: в списке компаний кнопка «Проверить Contour Node».
  • API: POST /api/companies/:id/check-node.

Студия резолвит узел компании, дёргает GET /api/v1/health, обновляет status (online/offline) и lastHealthCheck, возвращает:

json
{ "companyId": "...", "nodeId": "...", "status": "online" | "offline",
  "minio": "ok" | "bucket_missing", "checkedAt": "..." }

Критерий успеха: status: "online" и minio: "ok". Если minio: "bucket_missing" — создайте бакет nodeMinioBucket на MinIO узла.

5. Чек-лист развёртывания

  • [ ] Сервер компании готов (Docker, FFmpeg, диск).
  • [ ] Tailscale поднят, узел в tailnet, IP из 100.64.0.0/10, IP статический.
  • [ ] Contour Node запущен, слушает apiPort (8000) и minioPort (9000) в Tailscale.
  • [ ] MinIO поднят, бакет компании создан.
  • [ ] Узел реализует все эндпоинты /api/v1/* из API-контракта.
  • [ ] Узел умеет слать webhook'и на ${APP_BASE_URL}/api/contour/webhook/* с корректным телом и со своего зарегистрированного IP.
  • [ ] На студии: APP_BASE_URL = Tailscale-IP студии; при наличии прокси задан NO_PROXY.
  • [ ] (Опц.) MINIO_PROXY_BASE_URL + nginx location — см. MinIO reverse-proxy.
  • [ ] Компания + узел заведены в студии (поля node*).
  • [ ] check-nodestatus: online, minio: ok.
  • [ ] Сквозной тест: загрузка медиа → сжатие → webhook completed → файл доступен.