Appearance
Развёртывание Contour Node
Пошаговое руководство по установке узла на сервере компании и его регистрации в студии. Перед чтением рекомендуется ознакомиться с обзором и архитектурой.
1. Предварительные требования
На сервере компании (Contour Node)
| Требование | Значение |
|---|---|
| ОС | Linux x86_64 (рекомендуется) |
| Docker / Docker Compose | актуальная версия (узел и MinIO разворачиваются контейнерами) |
| FFmpeg | для задач сжатия и водяного знака (обычно в составе образа узла) |
| Tailscale | установлен и подключён в ту же tailnet, что и студия |
| Открытые порты внутри Tailscale | apiPort (по умолчанию 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)
- Установите Tailscale на сервере компании и выполните
tailscale up. - Убедитесь, что узел получил адрес из CGNAT-диапазона
100.64.0.0/10(это весь диапазон Tailscale; конкретный узел — например100.97.17.7). - Зафиксируйте у узла статический Tailscale-IP (через
--advertise/ настройки tailnet), потому что этот IP:- сохраняется в студии как
networkIpузла; - используется студией для построения
apiBaseUrl=http://<networkIp>:<apiPort>; - используется для проверки источника webhook'ов — колбэк принимается только если IP отправителя совпадает с зарегистрированным
networkIp.
- сохраняется в студии как
- Если backend студии выходит в интернет через корпоративный/SSH-прокси — добавьте Tailscale-сеть в
NO_PROXY, иначе запросы к узлу пойдут через прокси и не дойдут. Пример из.env.example:70:Если узлов несколько — можно перечислить их IP явно через запятую.NO_PROXY=100.64.0.0/10,localhost,127.0.0.1,::1
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"
}Что происходит на сервере
- Создаёт компанию с временным
contourNodeId = 'pending-node-id'. - Создаёт
ContourNodeиз node-полей,status: 'offline',companyId= id компании. - Проставляет компании реальный
contourNodeId. - 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-node→status: online,minio: ok. - [ ] Сквозной тест: загрузка медиа → сжатие → webhook
completed→ файл доступен.