Корпоративный чат
Корпоративный чат (модуль korport.comms.chat)
Заголовок раздела «Корпоративный чат (модуль korport.comms.chat)»Эта инструкция рассчитана на администратора, который уже установил Portal по быстрому старту и теперь хочет включить корпоративный чат (переписка, файлы, звонки).
Читайте сверху вниз. На каждом шаге есть команда «что ввести» и способ проверить, что шаг удался.
Если Portal у вас ещё не установлен — сначала выберите канал и пройдите быстрый старт.
0. Что вы получите в итоге
Заголовок раздела «0. Что вы получите в итоге»После всех шагов:
- В шапке Portal появится иконка чата.
- Можно писать в личные и групповые чаты, прикреплять файлы.
- При включённых звонках — аудио/видеозвонок между двумя пользователями.
- В меню появится узел Чат (страница-подсказка).
Модуль чата покупается отдельно и не входит в базовый дистрибутив 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).
2. Выберите свой раздел
Заголовок раздела «2. Выберите свой раздел»| Как у вас установлен 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.
3. Подготовьте данные (заполните один раз)
Заголовок раздела «3. Подготовьте данные (заполните один раз)»Перед правкой файлов запишите:
| Что | Пример | Куда пойдёт |
|---|---|---|
| Адрес сайта Portal в браузере | https://portal.example.com | LIVEKIT_URL=wss://portal.example.com/livekit |
IP сервера, который видят ПК пользователей (не 127.0.0.1) | 203.0.113.10 или LAN 192.168.1.50 | LIVEKIT_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.
4. Docker product — самый частый вариант
Заголовок раздела «4. Docker product — самый частый вариант»Ориентир: Portal уже стоит по быстрому старту Docker.
В каталоге установки есть файлы docker-compose.yml и .env.
Шаг 4.1. Откройте каталог установки
Заголовок раздела «Шаг 4.1. Откройте каталог установки»cd /путь/к/вашему/portal-*-docker-bundle# пример:# cd /opt/portalpwdls -la docker-compose.yml .envДолжны увидеть оба файла. Если .env нет — установка Portal не завершена, вернитесь к быстрому старту.
Шаг 4.2. Проверьте, что сервисы чата есть в compose
Заголовок раздела «Шаг 4.2. Проверьте, что сервисы чата есть в compose»docker compose config --services | sortВ списке должны быть имена вроде:
chatlivekitcoturnbackendfrontend
Если 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=truePORTAL_CHAT_INTERNAL_URL=http://chat:3010PORTAL_CHAT_DEV_BYPASS_MODULE=falseCHAT_POSTGRES_DATABASE=portal_chat
# Звонки (если звонки не нужны — поставьте LIVEKIT_ENABLED=false и пропустите TURN_*)LIVEKIT_ENABLED=trueLIVEKIT_MODE=dockerLIVEKIT_URL=wss://portal.example.com/livekitLIVEKIT_API_KEY=devkeyLIVEKIT_API_SECRET=portal_livekit_dev_secret_change_meLIVEKIT_NODE_IP=203.0.113.10
TURN_HOST=portal.example.comTURN_PORT=3478TURN_USERNAME=portalTURN_PASSWORD=portal_turn_change_meTURN_EXTERNAL_IP=203.0.113.10Правила:
- В
LIVEKIT_URLиTURN_HOST— тот же хост, что в адресной строке браузера (безhttps://, для URL — сwss://и путём/livekit). LIVEKIT_NODE_IPиTURN_EXTERNAL_IP— реальный IP, не127.0.0.1и неlocalhost.LIVEKIT_API_SECRET— не короче 32 символов.LIVEKIT_MODE=docker— обычный режим на Linux-сервере (LiveKit в том же Docker).
Сохраните файл.
Шаг 4.5. Синхронизируйте ключи LiveKit в конфиге
Заголовок раздела «Шаг 4.5. Синхронизируйте ключи LiveKit в конфиге»Файл обычно лежит так:
ls -la docker/livekit/livekit.yaml# иногда путь задан в .env как PORTAL_LIVEKIT_CONFIG_DIRgrep 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/tcpsudo ufw allow 443/tcpsudo ufw allow 7881/tcpsudo ufw allow 7882/udpsudo ufw allow 3478/tcpsudo ufw allow 3478/udpsudo ufw allow 50200:50240/udpsudo ufw statusЕсли firewall другой (firewalld, облачный Security Group) — откройте те же порты там.
Если Portal и все пользователи только в одной LAN — иногда достаточно LAN; для удалённых сотрудников порты обязательны.
Шаг 4.7. Пересоздайте контейнеры с новым .env
Заголовок раздела «Шаг 4.7. Пересоздайте контейнеры с новым .env»docker compose up -ddocker compose up -d --force-recreate backend frontend chat livekit coturnПодождите 20–40 секунд.
Шаг 4.8. Проверьте, что службы живы
Заголовок раздела «Шаг 4.8. Проверьте, что службы живы»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/healthdocker compose logs --tail=80 chatЕсли в логах ошибка про Postgres / permission — см. §13.
База portal_chat: Chat создаёт её сама. Отдельно CREATE DATABASE в Docker обычно не нужен.
Шаг 4.9. Дальше
Заголовок раздела «Шаг 4.9. Дальше»Перейдите к §9.
5. Полный Docker Compose
Заголовок раздела «5. Полный Docker Compose»Для пилота или стека с корневым docker-compose.yml (когда вы уже запускаете Portal через docker compose из каталога с .env).
Шаг 5.1. Каталог и файл .env
Заголовок раздела «Шаг 5.1. Каталог и файл .env»cd /путь/к/каталогу/с/docker-compose.ymlnano .envШаг 5.2. Минимум для переписки (без звонков)
Заголовок раздела «Шаг 5.2. Минимум для переписки (без звонков)»Добавьте:
PORTAL_CHAT_ENABLED=truePORTAL_CHAT_INTERNAL_URL=http://chat:3010PORTAL_CHAT_DEV_BYPASS_MODULE=falseCHAT_POSTGRES_DATABASE=portal_chatJWT_SECRET уже должен быть в .env и совпадать у backend и chat (обычно общий файл).
Запуск:
docker compose up -d chatdocker compose up -d --force-recreate backend frontenddocker compose ps chatdocker compose logs --tail=50 chatШаг 5.3. Звонки в режиме docker (Linux-сервер)
Заголовок раздела «Шаг 5.3. Звонки в режиме docker (Linux-сервер)»В .env добавьте к уже существующим профилям livekit (через запятую, без пробелов или как принято у вас):
COMPOSE_PROFILES=seaweedfs,livekitLIVEKIT_ENABLED=trueLIVEKIT_MODE=dockerLIVEKIT_URL=wss://portal.example.com/livekitLIVEKIT_API_KEY=devkeyLIVEKIT_API_SECRET=portal_livekit_dev_secret_change_meLIVEKIT_NODE_IP=203.0.113.10TURN_HOST=portal.example.comTURN_PORT=3478TURN_USERNAME=portalTURN_PASSWORD=portal_turn_change_meTURN_EXTERNAL_IP=203.0.113.10Сверьте docker/livekit/livekit.yaml с LIVEKIT_API_KEY / SECRET (как в шаге 4.5).
docker compose up -d chat coturn livekitdocker compose up -d --force-recreate backend frontenddocker compose ps chat coturn livekitДальше — §9.
Шаг 5.4. Режим host (только Mac + Docker Desktop, если звонок не соединяется)
Заголовок раздела «Шаг 5.4. Режим host (только Mac + Docker Desktop, если звонок не соединяется)»Используйте, только если на Mac ICE/звонок нестабилен в режиме docker.
- В
.envуберитеlivekitизCOMPOSE_PROFILES. - Задайте:
LIVEKIT_MODE=hostPORTAL_NGINX_UPSTREAM_WATCH_HOSTS=backend chatLIVEKIT_URL=wss://localhost/livekitLIVEKIT_NODE_IP=$(ipconfig getifaddr en0)- Установите LiveKit на Mac и запустите скрипт из поставки:
brew install livekit./docker/livekit/run-host.sh startdocker compose up -d coturn chatdocker compose up -d --force-recreate frontendАвтозапуск после логина: ./docker/livekit/install-macos-autostart.sh.
На обычном Linux-сервере этот режим не нужен — используйте шаг 5.3.
Смена LAN IP (после смены Wi‑Fi / сети)
Заголовок раздела «Смена LAN IP (после смены Wi‑Fi / сети)»Скрипты host-режима при старте LiveKit сами берут актуальный LAN IP, если
LIVEKIT_NODE_IP в .env устарел. Файл .env и контейнеры chat/coturn
сами не обновляются — без правки TURN звонок снова может падать
(could not establish pc connection / «TURN appears to be broken»).
# 1) новый IPipconfig 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 restartdocker compose up -d --force-recreate coturn chatПодробнее: docker/livekit/README.md — смена LAN IP.
6. Kubernetes
Заголовок раздела «6. Kubernetes»Чарт Portal не ставит Chat/LiveKit/coturn. Их добавляют отдельными манифестами после быстрого старта K8s.
Ниже — рабочий минимальный набор. Подставьте свой registry, домен и секреты.
Шаг 6.1. Проверьте, что Portal уже работает
Заголовок раздела «Шаг 6.1. Проверьте, что Portal уже работает»kubectl get pods -n portalkubectl get ingress -n portalPods API/frontend должны быть Running. Запомните namespace (часто portal) и имя Ingress-хоста.
Шаг 6.2. Секреты для чата и звонков
Заголовок раздела «Шаг 6.2. Секреты для чата и звонков»# Секрет 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'И подставьте нужное имя в команду выше.
Шаг 6.3. База portal_chat
Заголовок раздела «Шаг 6.3. База portal_chat»Подключитесь к 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.
Шаг 6.4. Файл манифестов
Заголовок раздела «Шаг 6.4. Файл манифестов»Создайте файл portal-chat-runtime.yaml на своём компьютере (или bastion):
nano portal-chat-runtime.yamlВставьте (замените IMAGE_REGISTRY, домен portal.example.com, IP/имя для ICE):
apiVersion: v1kind: ConfigMapmetadata: name: portal-livekit-config namespace: portaldata: 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/v1kind: Deploymentmetadata: name: portal-chat namespace: portalspec: 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: v1kind: Servicemetadata: name: portal-chat namespace: portalspec: selector: { app: portal-chat } ports: - name: http port: 3010 targetPort: 3010---apiVersion: apps/v1kind: Deploymentmetadata: name: portal-livekit namespace: portalspec: 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: v1kind: Servicemetadata: name: portal-livekit namespace: portalspec: 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.yamlkubectl -n portal rollout status deploy/portal-chatkubectl -n portal logs deploy/portal-chat --tail=50coturn в кластере часто ставят отдельным Deployment/hostNetwork или выносят на VM (см. §10). Для пилота в одной сети иногда достаточно LiveKit без TURN; для пользователей за NAT TURN обязателен.
Шаг 6.5. Ingress: пути чата и LiveKit
Заголовок раздела «Шаг 6.5. Ingress: пути чата и LiveKit»Добавьте к существующему Ingress (или создайте второй) path:
| Path | Service | Порт | Особенность |
|---|---|---|---|
/chat-api | portal-chat | 3010 | HTTP |
/chat-hub | portal-chat | 3010 | WebSocket |
/livekit | portal-livekit | 7880 | WebSocket |
Пример аннотаций для 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.
Шаг 6.6. Включите glue в Portal.Api
Заголовок раздела «Шаг 6.6. Включите glue в Portal.Api»В 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 deploykubectl -n portal rollout status deploy/portal-backendПеременная LIVEKIT_MODE в Kubernetes не нужна — маршрутизацию делает Ingress.
Дальше — §9. С нод/API нужен исходящий доступ к https://korport.ru.
7. Native Linux
Заголовок раздела «7. Native Linux»Portal уже установлен по Native Linux: работают portal-api, nginx, Postgres.
Шаг 7.1. Проверьте базовый Portal
Заголовок раздела «Шаг 7.1. Проверьте базовый Portal»systemctl status portal-api --no-pagercurl -sS http://127.0.0.1/healthls -la /etc/portal/portal.env /opt/portalШаг 7.2. Найдите бинарник Chat в поставке
Заголовок раздела «Шаг 7.2. Найдите бинарник Chat в поставке»В native-бандле бинарник чата обычно лежит рядом с API (имя/путь зависят от версии бандла). Ищите:
# в распакованном бандле до/после install:find . -iname 'Portal.Chat' 2>/dev/null | headls -la /opt/portal/Скопируйте в каталог службы (пример):
sudo mkdir -p /opt/portal/chatsudo 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Шаг 7.3. База данных portal_chat
Заголовок раздела «Шаг 7.3. База данных portal_chat»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.
Шаг 7.4. Файл окружения Chat
Заголовок раздела «Шаг 7.4. Файл окружения Chat»Скопируйте из portal.env значения JWT_SECRET, POSTGRES_*, S3_*:
grep -E '^(JWT_SECRET|POSTGRES_|S3_)' /etc/portal/portal.envsudo nano /etc/portal/chat.envСодержимое (подставьте свои значения):
ASPNETCORE_URLS=http://127.0.0.1:3010JWT_SECRET=тот_же_что_в_portal.envJWT_COOKIE_NAME=portal_tokenPOSTGRES_HOST=127.0.0.1POSTGRES_PORT=5432POSTGRES_USER=portalPOSTGRES_PASSWORD=пароль_из_portal.envCHAT_POSTGRES_DATABASE=portal_chatPORTAL_API_INTERNAL_URL=http://127.0.0.1:3001CHAT_INTERNAL_API_KEY=тот_же_или_отдельный_секрет
LIVEKIT_ENABLED=trueLIVEKIT_URL=wss://portal.example.com/livekitLIVEKIT_API_KEY=devkeyLIVEKIT_API_SECRET=portal_livekit_dev_secret_change_meTURN_HOST=portal.example.comTURN_PORT=3478TURN_USERNAME=portalTURN_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.envsudo chmod 640 /etc/portal/chat.envШаг 7.5. systemd: создать службу и запустить
Заголовок раздела «Шаг 7.5. systemd: создать службу и запустить»sudo nano /etc/systemd/system/portal-chat.serviceВставьте целиком:
[Unit]Description=Portal ChatAfter=network.target postgresql.serviceWants=network-online.target
[Service]Type=simpleUser=portalGroup=portalEnvironmentFile=/etc/portal/chat.envWorkingDirectory=/opt/portal/chatExecStart=/opt/portal/chat/Portal.ChatRestart=alwaysRestartSec=3LimitNOFILE=65535
[Install]WantedBy=multi-user.targetsudo systemctl daemon-reloadsudo systemctl enable --now portal-chatsudo systemctl status portal-chat --no-pagercurl -sS http://127.0.0.1:3010/healthjournalctl -u portal-chat -n 80 --no-pagerОжидается: служба active (running), curl возвращает JSON.
Если упало на миграциях/БД — смотрите journalctl и шаг 7.3.
Шаг 7.6. LiveKit и coturn (проще всего — Docker рядом)
Заголовок раздела «Шаг 7.6. LiveKit и coturn (проще всего — Docker рядом)»Даже при native Portal удобно поднять только LiveKit/coturn в Docker.
sudo mkdir -p /etc/portalsudo cp /путь/из/поставки/docker/livekit/livekit.yaml /etc/portal/livekit.yamlsudo cp /путь/из/поставки/docker/coturn/turnserver.conf /etc/portal/turnserver.confsudo 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-coturncurl -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.
Шаг 7.7. nginx: добавить прокси чата и LiveKit
Заголовок раздела «Шаг 7.7. nginx: добавить прокси чата и LiveKit»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 -tsudo systemctl reload nginxcurl -sI https://portal.example.com/livekit/ | headcurl -sS http://127.0.0.1:3010/healthШаг 7.8. Включите чат в Portal.Api
Заголовок раздела «Шаг 7.8. Включите чат в Portal.Api»sudo nano /etc/portal/portal.envДобавьте:
PORTAL_CHAT_ENABLED=truePORTAL_CHAT_INTERNAL_URL=http://127.0.0.1:3010PORTAL_CHAT_DEV_BYPASS_MODULE=falsesudo systemctl restart portal-apisudo systemctl status portal-api --no-pagercurl -sS http://127.0.0.1/healthLIVEKIT_MODE на Native не используется.
Дальше — §9.
8. Native Windows
Заголовок раздела «8. Native Windows»- Установите Portal по Native Windows.
- Запустите
Portal.Chatкак службу Windows (тот же бинарник targetchatиз бандла) с переменными окружения как в/etc/portal/chat.envиз §7.4 (через «Службы» / NSSM / sc.exe — как принято у вас дляPortal.Api). - Создайте БД:
# пример через psql, если установлен клиент PostgreSQLpsql -U postgres -c "CREATE DATABASE portal_chat OWNER portal;"- В IIS (или ваш reverse proxy) добавьте reverse proxy / ARR на:
/chat-api→http://127.0.0.1:3010/chat-hub→ тот же backend с WebSocket/livekit→http://127.0.0.1:7880с WebSocket
- LiveKit + coturn — Docker Desktop на том же хосте (команды как в шаге 7.6) или отдельные Windows-совместимые бинарники.
- В env API:
PORTAL_CHAT_ENABLED=true,PORTAL_CHAT_INTERNAL_URL=http://127.0.0.1:3010, перезапуск API. - Перейдите к §9.
Если нужна пошаговая автоматизация под ваш IIS — запросите у поставщика сопровождение: штатных unit-файлов Chat в Windows-бандле может не быть.
9. Лицензия и установка модуля в админке
Заголовок раздела «9. Лицензия и установка модуля в админке»Делается после того, как curl …/health у Chat отвечает.
Шаг 9.1. Активируйте лицензию с правом на чат
Заголовок раздела «Шаг 9.1. Активируйте лицензию с правом на чат»- Откройте Portal в браузере под администратором.
- Перейдите: Админка → Настройки → Продукт и лицензия.
- Загрузите/активируйте
LICENSE.key, в котором естьkorport.comms.*илиkorport.comms.chat
(trial сmodules: ["*"]тоже подходит на период оценки).
Из командной строки (Docker-бандл):
./portal-cli/portal-cli license activate --file /путь/к/LICENSE.keyШаг 9.2. Установите модуль с korport.ru
Заголовок раздела «Шаг 9.2. Установите модуль с korport.ru»- Админка → Модули.
- Нажмите Установить с korport.ru.
- Выберите шаблон «Коммуникации (чат)» (
comms). - Дождитесь сообщения об успехе.
Нельзя: загрузить файл .portalmod с диска для модулей korport.* — так задумано. Только каталог.
Сервер Portal (API) должен иметь доступ в интернет к https://korport.ru.
Обычно уже стоит шаблон Базовый портал (узел portal) — он нужен как родитель для узла «Чат».
10. Звонки на отдельном сервере
Заголовок раздела «10. Звонки на отдельном сервере»Можно оставить Chat на сервере Portal, а LiveKit + coturn вынести на машину с лучшей сетью.
На сервере звонков
Заголовок раздела «На сервере звонков»- Установите Docker.
- Скопируйте
livekit.yamlиturnserver.conf. - Запустите контейнеры (как шаг 7.6), подставив публичный IP сервера звонков.
- Откройте firewall:
7881/tcp,7882/udp,3478/tcp+udp,50200-50240/udp. - Настройте 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; }}На сервере Portal (в env Chat)
Заголовок раздела «На сервере Portal (в env Chat)»LIVEKIT_URL=wss://calls.example.com/livekitLIVEKIT_API_KEY=devkeyLIVEKIT_API_SECRET=тот_же_что_в_livekit.yaml_на_сервере_звонковLIVEKIT_NODE_IP=публичный_IP_сервера_звонковTURN_HOST=calls.example.comTURN_USERNAME=portalTURN_PASSWORD=тот_же_что_у_coturnПересоздайте/перезапустите Chat (и frontend, если он сам проксирует /livekit/ — либо проксируйте /livekit/ на внешний хост, либо отдавайте клиенту только URL из токена).
Пути /chat-api и /chat-hub остаются на основном сайте Portal.
11. Air-gapped (без интернета)
Заголовок раздела «11. Air-gapped (без интернета)»- Образы
portal/chat, LiveKit, coturn можно загрузить офлайн (docker load) и поднять runtime как в §4/§7. - Установить модуль
korport.comms.chatштатно нельзя без доступа к каталогу korport.ru.
См. Офлайн установка. Решение для закрытого контура — через поставщика.
12. Проверка: что всё работает
Заголовок раздела «12. Проверка: что всё работает»Выполняйте по порядку.
12.1. Службы
Заголовок раздела «12.1. Службы»Docker:
docker compose ps chat livekit coturndocker compose logs --tail=30 chatNative:
systemctl is-active portal-chatcurl -sS http://127.0.0.1:3010/health12.2. В браузере (под админом)
Заголовок раздела «12.2. В браузере (под админом)»| Проверка | Ожидание |
|---|---|
| Админка → Модули | Есть Корпоративный чат / korport.comms.chat |
| Шапка Portal | Иконка чата |
| Клик по иконке | Открывается панель, можно создать личный чат |
| Боковое меню | Узел Чат |
12.3. Техническая проверка bootstrap
Заголовок раздела «12.3. Техническая проверка bootstrap»В браузере: DevTools → Network, или с сервера под своей cookie-сессией.
Проще: откройте в браузере (будучи залогиненым) URL:
https://portal.example.com/api/v1/chat/bootstrap
В ответе должны быть:
"enabled": true"moduleInstalled": true"entitled": true"serviceReachable": true- при звонках:
"features": { "calls": true, ... }
12.4. Сигналинг LiveKit
Заголовок раздела «12.4. Сигналинг LiveKit»curl -sI https://portal.example.com/livekit/ | head -n 15Не должно быть 502 Bad Gateway. Код 200/101/редирект — нормально в зависимости от прокси.
12.5. Пробный звонок
Заголовок раздела «12.5. Пробный звонок»- Два пользователя (или два браузера/профиля).
- Общий чат → начать звонок.
- Должны быть слышны/видны друг другу.
Если текст есть, а звонок «крутится» — см. §13 (ICE / IP / firewall).
13. Типичные проблемы и что делать
Заголовок раздела «13. Типичные проблемы и что делать»| Что видите | Что сделать по шагам |
|---|---|
| Нет иконки чата | 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-hub | docker 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 снова 502 | docker compose exec frontend nginx -s reload и подождите 10 с. |
| K8s: после Helm чата нет | Ожидаемо — выполните §6 целиком. |
Общие проблемы Portal: Устранение неполадок.
14. После установки: хранение и push
Заголовок раздела «14. После установки: хранение и push»Очистка старых сообщений
Заголовок раздела «Очистка старых сообщений»- Админка → Службы → Очистка чата.
- Задайте срок хранения сообщений и вложений (дни;
0= не удалять). - При необходимости включите cron и нажмите «Запустить».
Подробнее: Службы.
Web Push (необязательно)
Заголовок раздела «Web Push (необязательно)»Нужны 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 — нет.
15. Краткая шпаргалка переменных
Заголовок раздела «15. Краткая шпаргалка переменных»| Переменная | Где | Зачем |
|---|---|---|
PORTAL_CHAT_ENABLED | API | Включить интеграцию с чатом |
PORTAL_CHAT_INTERNAL_URL | API | Куда API/Worker стучатся в Chat |
CHAT_POSTGRES_DATABASE | Chat | Имя БД (по умолчанию portal_chat) |
LIVEKIT_ENABLED | Chat | Включить звонки |
LIVEKIT_URL | Chat | Адрес сигналинга для браузера (wss://…/livekit) |
LIVEKIT_API_KEY / SECRET | Chat + livekit.yaml | Должны совпадать |
LIVEKIT_NODE_IP | LiveKit | IP для медиа, не localhost |
TURN_HOST / PASSWORD | Chat + coturn | NAT traversal |
LIVEKIT_MODE | только frontend в Docker | docker или host |
Полный список — блок Portal Chat в .env.example вашей поставки.