Перейти к содержимому

Корпоративный чат

Эта инструкция рассчитана на администратора, который уже установил Portal по быстрому старту и теперь хочет включить корпоративный чат (переписка, файлы, звонки).

Читайте сверху вниз. На каждом шаге есть команда «что ввести» и способ проверить, что шаг удался.
Если Portal у вас ещё не установлен — сначала выберите канал и пройдите быстрый старт.


После всех шагов:

  1. В шапке Portal появится иконка чата.
  2. Можно писать в личные и групповые чаты, прикреплять файлы.
  3. При включённых звонках — аудио/видеозвонок между двумя пользователями.
  4. В меню появится узел Чат (страница-подсказка).

Модуль чата покупается отдельно и не входит в базовый дистрибутив Portal.
Страница модуля: korport.ru/modules/comms-chat. После оплаты на email приходит обновлённый файл LICENSE.key с правом korport.comms.*.


1. Две обязательные части (без них чат не заработает)

Заголовок раздела «1. Две обязательные части (без них чат не заработает)»
ЧастьПростыми словамиКто делает
A. Службы (runtime)Программы на сервере: мессенджер Portal.Chat, для звонков ещё LiveKit и coturnАдминистратор сервера (эта инструкция, разделы 4–8)
B. Модуль из каталогаУстановка из админки Portal с сайта korport.ruАдминистратор Portal в браузере (раздел 9)

Важно:

  • Кнопка «Установить модуль» в админке не запускает Docker и не ставит LiveKit. Сначала службы, потом модуль.
  • Если службы не запущены, иконка может появиться, но сообщения и звонки не работают.
  • Если модуль не установлен (и нет права в лицензии), иконки чата не будет, даже если контейнер chat работает.

База сообщений называется portal_chat. В Docker и при нормальных правах Postgres служба Chat создаёт её сама при первом запуске — вручную обычно ничего делать не нужно. На Native иногда нужно создать БД руками (см. раздел 7).


Как у вас установлен PortalОткройте раздел
Docker из коробочного бандла (portal-cli install --mode docker)§4 ← начните здесь
Полный docker compose (пилот / расширенный стек)§5
Kubernetes / Helm§6
Native Linux (systemd + nginx)§7
Native Windows§8
Звонки на другом сервере§10
Закрытый контур без интернета§11

После настройки служб все идут в §9§12.


Перед правкой файлов запишите:

ЧтоПримерКуда пойдёт
Адрес сайта Portal в браузереhttps://portal.example.comLIVEKIT_URL=wss://portal.example.com/livekit
IP сервера, который видят ПК пользователей (не 127.0.0.1)203.0.113.10 или LAN 192.168.1.50LIVEKIT_NODE_IP, часто TURN_HOST / TURN_EXTERNAL_IP
Секрет для LiveKit (≥ 32 символа)длинная случайная строка.env и файл livekit.yamlодинаковые
Пароль TURNдругая случайная строка.env и coturn

Узнать IP сервера (Linux):

Окно терминала
hostname -I | awk '{print $1}'

Или спросите у сетевого администратора публичный IP / DNS-имя, по которому сотрудники открывают Portal.


Ориентир: Portal уже стоит по быстрому старту Docker.
В каталоге установки есть файлы docker-compose.yml и .env.

Окно терминала
cd /путь/к/вашему/portal-*-docker-bundle
# пример:
# cd /opt/portal
pwd
ls -la docker-compose.yml .env

Должны увидеть оба файла. Если .env нет — установка Portal не завершена, вернитесь к быстрому старту.

Шаг 4.2. Проверьте, что сервисы чата есть в compose

Заголовок раздела «Шаг 4.2. Проверьте, что сервисы чата есть в compose»
Окно терминала
docker compose config --services | sort

В списке должны быть имена вроде:

  • chat
  • livekit
  • coturn
  • backend
  • frontend

Если chat / livekit / coturn нет — обновите бандл у поставщика до версии, где runtime чата уже включён, и повторите шаг.

Проверка текущего состояния:

Окно терминала
docker compose ps

Шаг 4.3. Узнайте IP и откройте .env в редакторе

Заголовок раздела «Шаг 4.3. Узнайте IP и откройте .env в редакторе»
Окно терминала
hostname -I | awk '{print $1}'
nano .env

(nano: сохранить — Ctrl+O, Enter; выйти — Ctrl+X. Можно vim или любой редактор.)

Шаг 4.4. Добавьте или поправьте переменные в .env

Заголовок раздела «Шаг 4.4. Добавьте или поправьте переменные в .env»

Найдите строки ниже. Если их нет — добавьте в конец файла.
Подставьте свой домен и IP вместо примеров.

Окно терминала
# --- Корпоративный чат ---
PORTAL_CHAT_ENABLED=true
PORTAL_CHAT_INTERNAL_URL=http://chat:3010
PORTAL_CHAT_DEV_BYPASS_MODULE=false
CHAT_POSTGRES_DATABASE=portal_chat
# Звонки (если звонки не нужны — поставьте LIVEKIT_ENABLED=false и пропустите TURN_*)
LIVEKIT_ENABLED=true
LIVEKIT_MODE=docker
LIVEKIT_URL=wss://portal.example.com/livekit
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=portal_livekit_dev_secret_change_me
LIVEKIT_NODE_IP=203.0.113.10
TURN_HOST=portal.example.com
TURN_PORT=3478
TURN_USERNAME=portal
TURN_PASSWORD=portal_turn_change_me
TURN_EXTERNAL_IP=203.0.113.10

Правила:

  1. В LIVEKIT_URL и TURN_HOSTтот же хост, что в адресной строке браузера (без https://, для URL — с wss:// и путём /livekit).
  2. LIVEKIT_NODE_IP и TURN_EXTERNAL_IPреальный IP, не 127.0.0.1 и не localhost.
  3. LIVEKIT_API_SECRETне короче 32 символов.
  4. LIVEKIT_MODE=docker — обычный режим на Linux-сервере (LiveKit в том же Docker).

Сохраните файл.

Шаг 4.5. Синхронизируйте ключи LiveKit в конфиге

Заголовок раздела «Шаг 4.5. Синхронизируйте ключи LiveKit в конфиге»

Файл обычно лежит так:

Окно терминала
ls -la docker/livekit/livekit.yaml
# иногда путь задан в .env как PORTAL_LIVEKIT_CONFIG_DIR
grep PORTAL_LIVEKIT_CONFIG_DIR .env || true

Откройте livekit.yaml:

Окно терминала
nano docker/livekit/livekit.yaml

Блок keys: должен совпадать с .env:

keys:
devkey: portal_livekit_dev_secret_change_me

Здесь devkey = значение LIVEKIT_API_KEY, справа = значение LIVEKIT_API_SECRET.
Если в .env вы сменили секрет — сюда тоже. Сохраните.

Пароль TURN в образе часто подставляется из .env entrypoint’ом. Если в бандле есть docker/coturn/turnserver.conf со строкой user=portal:..., пароль после : должен совпадать с TURN_PASSWORD.

Шаг 4.6. Откройте порты firewall (если звонки из других сетей)

Заголовок раздела «Шаг 4.6. Откройте порты firewall (если звонки из других сетей)»

На сервере (пример для ufw на Ubuntu):

Окно терминала
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 7881/tcp
sudo ufw allow 7882/udp
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp
sudo ufw allow 50200:50240/udp
sudo ufw status

Если firewall другой (firewalld, облачный Security Group) — откройте те же порты там.
Если Portal и все пользователи только в одной LAN — иногда достаточно LAN; для удалённых сотрудников порты обязательны.

Шаг 4.7. Пересоздайте контейнеры с новым .env

Заголовок раздела «Шаг 4.7. Пересоздайте контейнеры с новым .env»
Окно терминала
docker compose up -d
docker compose up -d --force-recreate backend frontend chat livekit coturn

Подождите 20–40 секунд.

Окно терминала
docker compose ps chat livekit coturn backend frontend

Статус должен быть running / Up (не Exit / Restarting).

Проверка здоровья Chat:

Окно терминала
docker compose exec chat wget -qO- http://127.0.0.1:3010/health || \
curl -sS http://127.0.0.1:3010/health

Ожидается JSON. При включённых звонках внутри будет что-то вроде "calls":true.

Если команда внутри контейнера не сработала, с хоста (если порт проброшен) или через frontend:

Окно терминала
curl -sS http://127.0.0.1/health
docker compose logs --tail=80 chat

Если в логах ошибка про Postgres / permission — см. §13.

База portal_chat: Chat создаёт её сама. Отдельно CREATE DATABASE в Docker обычно не нужен.

Перейдите к §9.


Для пилота или стека с корневым docker-compose.yml (когда вы уже запускаете Portal через docker compose из каталога с .env).

Окно терминала
cd /путь/к/каталогу/с/docker-compose.yml
nano .env

Шаг 5.2. Минимум для переписки (без звонков)

Заголовок раздела «Шаг 5.2. Минимум для переписки (без звонков)»

Добавьте:

Окно терминала
PORTAL_CHAT_ENABLED=true
PORTAL_CHAT_INTERNAL_URL=http://chat:3010
PORTAL_CHAT_DEV_BYPASS_MODULE=false
CHAT_POSTGRES_DATABASE=portal_chat

JWT_SECRET уже должен быть в .env и совпадать у backend и chat (обычно общий файл).

Запуск:

Окно терминала
docker compose up -d chat
docker compose up -d --force-recreate backend frontend
docker compose ps chat
docker compose logs --tail=50 chat

В .env добавьте к уже существующим профилям livekit (через запятую, без пробелов или как принято у вас):

Окно терминала
COMPOSE_PROFILES=seaweedfs,livekit
LIVEKIT_ENABLED=true
LIVEKIT_MODE=docker
LIVEKIT_URL=wss://portal.example.com/livekit
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=portal_livekit_dev_secret_change_me
LIVEKIT_NODE_IP=203.0.113.10
TURN_HOST=portal.example.com
TURN_PORT=3478
TURN_USERNAME=portal
TURN_PASSWORD=portal_turn_change_me
TURN_EXTERNAL_IP=203.0.113.10

Сверьте docker/livekit/livekit.yaml с LIVEKIT_API_KEY / SECRET (как в шаге 4.5).

Окно терминала
docker compose up -d chat coturn livekit
docker compose up -d --force-recreate backend frontend
docker compose ps chat coturn livekit

Дальше — §9.

Шаг 5.4. Режим host (только Mac + Docker Desktop, если звонок не соединяется)

Заголовок раздела «Шаг 5.4. Режим host (только Mac + Docker Desktop, если звонок не соединяется)»

Используйте, только если на Mac ICE/звонок нестабилен в режиме docker.

  1. В .env уберите livekit из COMPOSE_PROFILES.
  2. Задайте:
Окно терминала
LIVEKIT_MODE=host
PORTAL_NGINX_UPSTREAM_WATCH_HOSTS=backend chat
LIVEKIT_URL=wss://localhost/livekit
LIVEKIT_NODE_IP=$(ipconfig getifaddr en0)
  1. Установите LiveKit на Mac и запустите скрипт из поставки:
Окно терминала
brew install livekit
./docker/livekit/run-host.sh start
docker compose up -d coturn chat
docker compose up -d --force-recreate frontend

Автозапуск после логина: ./docker/livekit/install-macos-autostart.sh.

На обычном Linux-сервере этот режим не нужен — используйте шаг 5.3.

Скрипты host-режима при старте LiveKit сами берут актуальный LAN IP, если LIVEKIT_NODE_IP в .env устарел. Файл .env и контейнеры chat/coturn сами не обновляются — без правки TURN звонок снова может падать (could not establish pc connection / «TURN appears to be broken»).

Окно терминала
# 1) новый IP
ipconfig getifaddr en0
# 2) в .env одинаково:
# LIVEKIT_NODE_IP=НОВЫЙ_IP
# TURN_HOST=НОВЫЙ_IP
# TURN_EXTERNAL_IP=НОВЫЙ_IP
# 3) перезапуск
launchctl kickstart -k "gui/$(id -u)/ru.korport.livekit-host"
# или: ./docker/livekit/run-host.sh restart
docker compose up -d --force-recreate coturn chat

Подробнее: docker/livekit/README.md — смена LAN IP.


Чарт Portal не ставит Chat/LiveKit/coturn. Их добавляют отдельными манифестами после быстрого старта K8s.

Ниже — рабочий минимальный набор. Подставьте свой registry, домен и секреты.

Окно терминала
kubectl get pods -n portal
kubectl get ingress -n portal

Pods API/frontend должны быть Running. Запомните namespace (часто portal) и имя Ingress-хоста.

Окно терминала
# Секрет LiveKit (≥32 символа) и TURN — свои значения:
export LK_SECRET='portal_livekit_dev_secret_change_me'
export TURN_PASS='portal_turn_change_me'
kubectl -n portal create secret generic portal-chat-secrets \
--from-literal=jwt-secret="$(kubectl -n portal get secret portal-secrets -o jsonpath='{.data.jwt-secret}' | base64 -d)" \
--from-literal=livekit-api-secret="$LK_SECRET" \
--from-literal=turn-password="$TURN_PASS" \
--dry-run=client -o yaml | kubectl apply -f -

Если ключ в portal-secrets называется иначе — посмотрите:

Окно терминала
kubectl -n portal get secret portal-secrets -o jsonpath='{.data}' | tr ',' '\n'

И подставьте нужное имя в команду выше.

Подключитесь к Postgres Portal и создайте БД (Chat тоже умеет создавать сам, если у пользователя есть право; так надёжнее сделать явно):

Окно терминала
kubectl -n portal exec -it statefulset/portal-postgres -- \
psql -U portal -d portal -c 'CREATE DATABASE portal_chat OWNER portal;'

Если БД уже есть, Postgres напишет ошибка «already exists» — это нормально.

Имя StatefulSet/пода может отличаться:

Окно терминала
kubectl -n portal get pods | grep -i postgres

Подставьте своё имя пода вместо statefulset/portal-postgres.

Создайте файл portal-chat-runtime.yaml на своём компьютере (или bastion):

Окно терминала
nano portal-chat-runtime.yaml

Вставьте (замените IMAGE_REGISTRY, домен portal.example.com, IP/имя для ICE):

apiVersion: v1
kind: ConfigMap
metadata:
name: portal-livekit-config
namespace: portal
data:
livekit.yaml: |
port: 7880
bind_addresses: [""]
rtc:
tcp_port: 7881
udp_port: 7882
use_external_ip: false
allow_tcp_fallback: true
logging:
level: info
keys:
devkey: portal_livekit_dev_secret_change_me
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: portal-chat
namespace: portal
spec:
replicas: 1
selector:
matchLabels: { app: portal-chat }
template:
metadata:
labels: { app: portal-chat }
spec:
containers:
- name: chat
image: IMAGE_REGISTRY/portal/chat:1.0.0
ports:
- containerPort: 3010
env:
- name: ASPNETCORE_URLS
value: http://+:3010
- name: JWT_SECRET
valueFrom: { secretKeyRef: { name: portal-chat-secrets, key: jwt-secret } }
- name: POSTGRES_HOST
value: portal-postgres
- name: POSTGRES_USER
value: portal
- name: POSTGRES_PASSWORD
valueFrom: { secretKeyRef: { name: portal-secrets, key: postgres-password } }
- name: CHAT_POSTGRES_DATABASE
value: portal_chat
- name: PORTAL_API_INTERNAL_URL
value: http://portal-backend:3001
- name: LIVEKIT_ENABLED
value: "true"
- name: LIVEKIT_URL
value: wss://portal.example.com/livekit
- name: LIVEKIT_API_KEY
value: "devkey"
- name: LIVEKIT_API_SECRET
valueFrom: { secretKeyRef: { name: portal-chat-secrets, key: livekit-api-secret } }
- name: TURN_HOST
value: portal.example.com
- name: TURN_PORT
value: "3478"
- name: TURN_USERNAME
value: portal
- name: TURN_PASSWORD
valueFrom: { secretKeyRef: { name: portal-chat-secrets, key: turn-password } }
# S3_* — скопируйте те же, что у Deployment API / Worker
---
apiVersion: v1
kind: Service
metadata:
name: portal-chat
namespace: portal
spec:
selector: { app: portal-chat }
ports:
- name: http
port: 3010
targetPort: 3010
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: portal-livekit
namespace: portal
spec:
replicas: 1
selector:
matchLabels: { app: portal-livekit }
template:
metadata:
labels: { app: portal-livekit }
spec:
containers:
- name: livekit
image: livekit/livekit-server:v1.8.4
args: ["--config", "/etc/livekit.yaml", "--node-ip", "203.0.113.10"]
ports:
- containerPort: 7880
name: signal
- containerPort: 7881
name: rtc-tcp
- containerPort: 7882
name: rtc-udp
protocol: UDP
volumeMounts:
- name: cfg
mountPath: /etc/livekit.yaml
subPath: livekit.yaml
volumes:
- name: cfg
configMap:
name: portal-livekit-config
---
apiVersion: v1
kind: Service
metadata:
name: portal-livekit
namespace: portal
spec:
type: LoadBalancer
selector: { app: portal-livekit }
ports:
- name: signal
port: 7880
targetPort: 7880
- name: rtc-tcp
port: 7881
targetPort: 7881
- name: rtc-udp
port: 7882
targetPort: 7882
protocol: UDP

Важно:

  • В ConfigMap keys.devkey должен байт-в-байт совпадать с секретом livekit-api-secret.
  • Имена Service Postgres/API (portal-postgres, portal-backend) возьмите из:
Окно терминала
kubectl -n portal get svc
  • Ключ пароля Postgres в Secret может называться иначе — сверьте с Deployment API.

Примените:

Окно терминала
kubectl apply -f portal-chat-runtime.yaml
kubectl -n portal rollout status deploy/portal-chat
kubectl -n portal logs deploy/portal-chat --tail=50

coturn в кластере часто ставят отдельным Deployment/hostNetwork или выносят на VM (см. §10). Для пилота в одной сети иногда достаточно LiveKit без TURN; для пользователей за NAT TURN обязателен.

Добавьте к существующему Ingress (или создайте второй) path:

PathServiceПортОсобенность
/chat-apiportal-chat3010HTTP
/chat-hubportal-chat3010WebSocket
/livekitportal-livekit7880WebSocket

Пример аннотаций для nginx-ingress (фрагмент):

metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"

Для WebSocket обычно достаточно стандартного nginx-ingress; если 502 — проверьте backend Service и что path без лишнего rewrite.

В values / env Deployment API добавьте и сделайте rollout:

PORTAL_CHAT_ENABLED: "true"
PORTAL_CHAT_INTERNAL_URL: "http://portal-chat:3010"
PORTAL_CHAT_DEV_BYPASS_MODULE: "false"
Окно терминала
kubectl -n portal rollout restart deploy/portal-backend
# имя Deployment API уточните: kubectl -n portal get deploy
kubectl -n portal rollout status deploy/portal-backend

Переменная LIVEKIT_MODE в Kubernetes не нужна — маршрутизацию делает Ingress.

Дальше — §9. С нод/API нужен исходящий доступ к https://korport.ru.


Portal уже установлен по Native Linux: работают portal-api, nginx, Postgres.

Окно терминала
systemctl status portal-api --no-pager
curl -sS http://127.0.0.1/health
ls -la /etc/portal/portal.env /opt/portal

В native-бандле бинарник чата обычно лежит рядом с API (имя/путь зависят от версии бандла). Ищите:

Окно терминала
# в распакованном бандле до/после install:
find . -iname 'Portal.Chat' 2>/dev/null | head
ls -la /opt/portal/

Скопируйте в каталог службы (пример):

Окно терминала
sudo mkdir -p /opt/portal/chat
sudo cp -a /путь/из/бандла/к/Portal.Chat /opt/portal/chat/
sudo chmod +x /opt/portal/chat/Portal.Chat
# если рядом есть папка Data/Migrations — скопируйте её тоже:
# sudo cp -a Data /opt/portal/chat/
sudo chown -R portal:portal /opt/portal/chat

Пользователь portal уже должен существовать после установки Portal. Проверка:

Окно терминала
id portal

Chat при старте сам вызывает CREATE DATABASE, если у пользователя Postgres есть право создавать БД. Надёжный ручной вариант:

Окно терминала
sudo -u postgres psql -c "CREATE DATABASE portal_chat OWNER portal;"

Если пользователь не portal, подставьте своего из /etc/portal/portal.env (POSTGRES_USER).

Проверка:

Окно терминала
sudo -u postgres psql -c '\l' | grep portal_chat

Должна быть строка с portal_chat.

Скопируйте из portal.env значения JWT_SECRET, POSTGRES_*, S3_*:

Окно терминала
grep -E '^(JWT_SECRET|POSTGRES_|S3_)' /etc/portal/portal.env
sudo nano /etc/portal/chat.env

Содержимое (подставьте свои значения):

Окно терминала
ASPNETCORE_URLS=http://127.0.0.1:3010
JWT_SECRET=тот_же_что_в_portal.env
JWT_COOKIE_NAME=portal_token
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=portal
POSTGRES_PASSWORD=пароль_из_portal.env
CHAT_POSTGRES_DATABASE=portal_chat
PORTAL_API_INTERNAL_URL=http://127.0.0.1:3001
CHAT_INTERNAL_API_KEY=тот_же_или_отдельный_секрет
LIVEKIT_ENABLED=true
LIVEKIT_URL=wss://portal.example.com/livekit
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=portal_livekit_dev_secret_change_me
TURN_HOST=portal.example.com
TURN_PORT=3478
TURN_USERNAME=portal
TURN_PASSWORD=portal_turn_change_me
# Файлы вложений — те же S3_*, что у API:
# S3_ENDPOINT=...
# S3_PORT=...
# S3_ACCESS_KEY=...
# S3_SECRET_KEY=...
# S3_BUCKET=...
# S3_USE_SSL=...
Окно терминала
sudo chown root:portal /etc/portal/chat.env
sudo chmod 640 /etc/portal/chat.env
Окно терминала
sudo nano /etc/systemd/system/portal-chat.service

Вставьте целиком:

[Unit]
Description=Portal Chat
After=network.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=portal
Group=portal
EnvironmentFile=/etc/portal/chat.env
WorkingDirectory=/opt/portal/chat
ExecStart=/opt/portal/chat/Portal.Chat
Restart=always
RestartSec=3
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
Окно терминала
sudo systemctl daemon-reload
sudo systemctl enable --now portal-chat
sudo systemctl status portal-chat --no-pager
curl -sS http://127.0.0.1:3010/health
journalctl -u portal-chat -n 80 --no-pager

Ожидается: служба active (running), curl возвращает JSON.
Если упало на миграциях/БД — смотрите journalctl и шаг 7.3.

Даже при native Portal удобно поднять только LiveKit/coturn в Docker.

Окно терминала
sudo mkdir -p /etc/portal
sudo cp /путь/из/поставки/docker/livekit/livekit.yaml /etc/portal/livekit.yaml
sudo cp /путь/из/поставки/docker/coturn/turnserver.conf /etc/portal/turnserver.conf
sudo nano /etc/portal/livekit.yaml

В livekit.yaml выставьте тот же keys.devkey: <секрет>, что в chat.env.

Окно терминала
PUBLIC_IP=$(hostname -I | awk '{print $1}')
echo "PUBLIC_IP=$PUBLIC_IP"
docker run -d --restart=unless-stopped --name portal-livekit \
-p 7880:7880 -p 7881:7881 -p 7882:7882/udp \
-v /etc/portal/livekit.yaml:/etc/livekit.yaml:ro \
livekit/livekit-server:v1.8.4 \
--config /etc/livekit.yaml --node-ip "$PUBLIC_IP"
docker run -d --restart=unless-stopped --name portal-coturn \
-p 3478:3478 -p 3478:3478/udp -p 50200-50240:50200-50240/udp \
-e TURN_EXTERNAL_IP="$PUBLIC_IP" \
-v /etc/portal/turnserver.conf:/etc/coturn/turnserver.conf:ro \
coturn/coturn:4.6.2

Если у образа coturn нет нужного entrypoint — отредактируйте turnserver.conf: строки external-ip= / user=portal:пароль вручную под ваш IP и TURN_PASSWORD.

Проверка:

Окно терминала
docker ps --filter name=portal-livekit --filter name=portal-coturn
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:7880

Без Docker: скачайте бинарник LiveKit с GitHub Releases, поставьте пакет coturn из репозитория ОС (sudo apt install coturn / dnf install coturn) и укажите те же порты и конфиги в systemd — смысл тот же.

Firewall — как в шаге 4.6.

Окно терминала
sudo cp /etc/nginx/sites-available/portal /etc/nginx/sites-available/portal.bak.$(date +%F)
sudo nano /etc/nginx/sites-available/portal

Внутри блока server { ... }, перед строкой location / {, вставьте:

location ^~ /chat-api/v1/admin/ {
deny all;
return 403;
}
location ^~ /chat-api/ {
proxy_pass http://127.0.0.1:3010;
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;
client_max_body_size 4G;
}
location ^~ /chat-hub {
proxy_pass http://127.0.0.1:3010;
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 "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off;
}
location ^~ /livekit/ {
proxy_pass http://127.0.0.1:7880/;
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 "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off;
}

Проверка и применение:

Окно терминала
sudo nginx -t
sudo systemctl reload nginx
curl -sI https://portal.example.com/livekit/ | head
curl -sS http://127.0.0.1:3010/health
Окно терминала
sudo nano /etc/portal/portal.env

Добавьте:

Окно терминала
PORTAL_CHAT_ENABLED=true
PORTAL_CHAT_INTERNAL_URL=http://127.0.0.1:3010
PORTAL_CHAT_DEV_BYPASS_MODULE=false
Окно терминала
sudo systemctl restart portal-api
sudo systemctl status portal-api --no-pager
curl -sS http://127.0.0.1/health

LIVEKIT_MODE на Native не используется.

Дальше — §9.


  1. Установите Portal по Native Windows.
  2. Запустите Portal.Chat как службу Windows (тот же бинарник target chat из бандла) с переменными окружения как в /etc/portal/chat.env из §7.4 (через «Службы» / NSSM / sc.exe — как принято у вас для Portal.Api).
  3. Создайте БД:
Окно терминала
# пример через psql, если установлен клиент PostgreSQL
psql -U postgres -c "CREATE DATABASE portal_chat OWNER portal;"
  1. В IIS (или ваш reverse proxy) добавьте reverse proxy / ARR на:
    • /chat-apihttp://127.0.0.1:3010
    • /chat-hub → тот же backend с WebSocket
    • /livekithttp://127.0.0.1:7880 с WebSocket
  2. LiveKit + coturn — Docker Desktop на том же хосте (команды как в шаге 7.6) или отдельные Windows-совместимые бинарники.
  3. В env API: PORTAL_CHAT_ENABLED=true, PORTAL_CHAT_INTERNAL_URL=http://127.0.0.1:3010, перезапуск API.
  4. Перейдите к §9.

Если нужна пошаговая автоматизация под ваш IIS — запросите у поставщика сопровождение: штатных unit-файлов Chat в Windows-бандле может не быть.


Делается после того, как curl …/health у Chat отвечает.

Шаг 9.1. Активируйте лицензию с правом на чат

Заголовок раздела «Шаг 9.1. Активируйте лицензию с правом на чат»
  1. Откройте Portal в браузере под администратором.
  2. Перейдите: Админка → Настройки → Продукт и лицензия.
  3. Загрузите/активируйте LICENSE.key, в котором есть korport.comms.* или korport.comms.chat
    (trial с modules: ["*"] тоже подходит на период оценки).

Из командной строки (Docker-бандл):

Окно терминала
./portal-cli/portal-cli license activate --file /путь/к/LICENSE.key
  1. Админка → Модули.
  2. Нажмите Установить с korport.ru.
  3. Выберите шаблон «Коммуникации (чат)» (comms).
  4. Дождитесь сообщения об успехе.

Нельзя: загрузить файл .portalmod с диска для модулей korport.* — так задумано. Только каталог.

Сервер Portal (API) должен иметь доступ в интернет к https://korport.ru.

Обычно уже стоит шаблон Базовый портал (узел portal) — он нужен как родитель для узла «Чат».


Можно оставить Chat на сервере Portal, а LiveKit + coturn вынести на машину с лучшей сетью.

  1. Установите Docker.
  2. Скопируйте livekit.yaml и turnserver.conf.
  3. Запустите контейнеры (как шаг 7.6), подставив публичный IP сервера звонков.
  4. Откройте firewall: 7881/tcp, 7882/udp, 3478/tcp+udp, 50200-50240/udp.
  5. Настройте reverse proxy, например nginx:
server {
listen 443 ssl;
server_name calls.example.com;
# ssl_certificate ...;
location /livekit/ {
proxy_pass http://127.0.0.1:7880/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
}
Окно терминала
LIVEKIT_URL=wss://calls.example.com/livekit
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=тот_же_что_в_livekit.yaml_на_сервере_звонков
LIVEKIT_NODE_IP=публичный_IP_сервера_звонков
TURN_HOST=calls.example.com
TURN_USERNAME=portal
TURN_PASSWORD=тот_же_что_у_coturn

Пересоздайте/перезапустите Chat (и frontend, если он сам проксирует /livekit/ — либо проксируйте /livekit/ на внешний хост, либо отдавайте клиенту только URL из токена).

Пути /chat-api и /chat-hub остаются на основном сайте Portal.


  • Образы portal/chat, LiveKit, coturn можно загрузить офлайн (docker load) и поднять runtime как в §4/§7.
  • Установить модуль korport.comms.chat штатно нельзя без доступа к каталогу korport.ru.

См. Офлайн установка. Решение для закрытого контура — через поставщика.


Выполняйте по порядку.

Docker:

Окно терминала
docker compose ps chat livekit coturn
docker compose logs --tail=30 chat

Native:

Окно терминала
systemctl is-active portal-chat
curl -sS http://127.0.0.1:3010/health
ПроверкаОжидание
Админка → МодулиЕсть Корпоративный чат / korport.comms.chat
Шапка PortalИконка чата
Клик по иконкеОткрывается панель, можно создать личный чат
Боковое менюУзел Чат

В браузере: DevTools → Network, или с сервера под своей cookie-сессией.
Проще: откройте в браузере (будучи залогиненым) URL:

https://portal.example.com/api/v1/chat/bootstrap

В ответе должны быть:

  • "enabled": true
  • "moduleInstalled": true
  • "entitled": true
  • "serviceReachable": true
  • при звонках: "features": { "calls": true, ... }
Окно терминала
curl -sI https://portal.example.com/livekit/ | head -n 15

Не должно быть 502 Bad Gateway. Код 200/101/редирект — нормально в зависимости от прокси.

  1. Два пользователя (или два браузера/профиля).
  2. Общий чат → начать звонок.
  3. Должны быть слышны/видны друг другу.

Если текст есть, а звонок «крутится» — см. §13 (ICE / IP / firewall).


Что видитеЧто сделать по шагам
Нет иконки чата1) Откройте /api/v1/chat/bootstrap. 2) Если serviceConfigured: false — в .env API нет PORTAL_CHAT_ENABLED=true, пересоздайте backend. 3) Если moduleInstalled: false — §9.2. 4) Если entitled: false — другой LICENSE.key с korport.comms.*.
«Нет лицензии на этот модуль»Активируйте ключ с entitlement (§9.1), обновите страницу модулей.
502 на /chat-api или /chat-hubdocker compose ps chat / systemctl status portal-chat. Логи. В Docker: docker compose exec frontend nginx -s reload.
Сообщения есть, звонков нетВ health Chat calls: false: включите LIVEKIT_ENABLED=true, поднимите LiveKit, секрет ≥ 32 символов и равен livekit.yaml, recreate chat/livekit.
Звонок не соединяется1) LIVEKIT_NODE_IP не 127.0.0.1. 2) TURN_HOST доступен с ПК пользователя. 3) Открыты UDP/TCP из шага 4.6. 4) Два клиента не за «двойным» NAT без TURN. 5) После смены Wi‑Fi на Mac — обновить LIVEKIT_NODE_IP / TURN_* и перезапустить LiveKit + coturn/chat (§5.4).
Не ставится .portalmod с дискаТак и должно быть — только «Установить с korport.ru».
Chat падает: cannot create databaseВыполните шаг 7.3 (CREATE DATABASE portal_chat OWNER …).
После recreate Docker снова 502docker compose exec frontend nginx -s reload и подождите 10 с.
K8s: после Helm чата нетОжидаемо — выполните §6 целиком.

Общие проблемы Portal: Устранение неполадок.


  1. Админка → Службы → Очистка чата.
  2. Задайте срок хранения сообщений и вложений (дни; 0 = не удалять).
  3. При необходимости включите cron и нажмите «Запустить».

Подробнее: Службы.

Нужны VAPID-ключи в env Chat:

Окно терминала
# на машине с Node.js:
npx web-push generate-vapid-keys

Пропишите CHAT_VAPID_PUBLIC_KEY, CHAT_VAPID_PRIVATE_KEY, CHAT_VAPID_SUBJECT=mailto:admin@example.com, перезапустите Chat.
Без ключей текстовый чат работает, фоновые push — нет.


ПеременнаяГдеЗачем
PORTAL_CHAT_ENABLEDAPIВключить интеграцию с чатом
PORTAL_CHAT_INTERNAL_URLAPIКуда API/Worker стучатся в Chat
CHAT_POSTGRES_DATABASEChatИмя БД (по умолчанию portal_chat)
LIVEKIT_ENABLEDChatВключить звонки
LIVEKIT_URLChatАдрес сигналинга для браузера (wss://…/livekit)
LIVEKIT_API_KEY / SECRETChat + livekit.yamlДолжны совпадать
LIVEKIT_NODE_IPLiveKitIP для медиа, не localhost
TURN_HOST / PASSWORDChat + coturnNAT traversal
LIVEKIT_MODEтолько frontend в Dockerdocker или host

Полный список — блок Portal Chat в .env.example вашей поставки.