Продукты
Личный кабинет Юмобайл: web-app-umoible
Для разработчиков. Состояние на 28.09.2026. Переменные, ручки и выкатка подробно — в README репозитория Buharestskie/web-app-umoible; карта «экран → запрос → поля» — API-MAP.md там же.
Коротко
Личный кабинет абонента Юмобайла (MVNO на сети билайн): баланс, тариф, остатки, статус номера, несколько номеров на одном аккаунте, пополнение через СБП и картой, новости, чат с поддержкой, уведомления и активация новой SIM — от скана серийника до подписания договора в Госключе или проверки паспорта. Данные абонента — не наши: их отдаёт API оператора, приложение показывает ровно то, что он вернул. Своё у нас — два бэкенда: чат и уведомления, и выдача номеров при активации.
| Часть | Папка | Что это | Живое? |
|---|---|---|---|
| Веб-кабинет (PWA) | web/ | React 19 + TypeScript + Vite, открывается на app.umobile.ru/app/, ставится на экран «Домой» | да, основная форма продукта |
| Чат и уведомления | chat-backend/ | FastAPI + Postgres: чат с оператором через amoCRM, лента уведомлений, web-push и FCM, ведение сделки активации в amoCRM | да |
| Сервис активации | activation-admin/ | FastAPI + Postgres: пул номеров и партии SIM, выдача номера при скане, привязка в ЛК оператора, сессии, админка | да |
| Нативная оболочка iOS | native/ | SwiftUI + WKWebView; нативные чат, сканер SIM, пуши через Firebase | заморожена с 26.08.2026, см. ниже |
| Конфиги nginx | server/ | справочные копии боевых конфигов обоих доменов и зоны микрокэша | справочно |
| Выкатка | deploy/, .github/workflows/ci.yml | скрипт выкатки и CI | да |
| Документация | корень | lk_umobile_api.yaml (спека API оператора), API-MAP.md, SPEC-*.md и TZ-*.md — задачи для разработчика API оператора, ПИСЬМО-АВТОРУ-API*.md | справочно |
AGENT_CONTEXT.md местами описывает прежний фронт (оболочка app.html с экранами в iframe). Его убрали из дерева, он лежит под тегом legacy-frontend. Всё про экраны сверяйте с web/src.
Откуда данные и куда уходят
- API оператора (ЛК) —
lk.umobile.ru/api/v1, не наш. Отдаёт вход по коду из SMS, регистрацию нового абонента, профиль, данные номера (баланс, статус, тариф, остатки), новости, регистрацию в Госключе и её статус, проверку паспорта. Из браузера приложение ходит туда через наш nginx по пути/lkapi/. Спецификация —lk_umobile_api.yamlв корне репозитория. - Оплата — отдельный домен оператора
umobile.ru, через наш путь/payapi/: создание заказа (СБП или карта), статус, чек. Список банков для СБП приложение берёт напрямую у НСПК (qr.nspk.ru) — это единственный запрос мимо нашего прокси. - Чат-бэкенд (
/api/v1,/ws/) — сообщения абонента уходят в чат amoCRM (канал amojo), ответы оператора приходят вебхуком и доставляются в открытое приложение по WebSocket, а в закрытое — пушем. Сюда же биллинг оператора шлёт события о пополнении и смене статуса SIM (POST /api/v1/notify, типыbalance,status,info). - Сервис активации (
/actapp/) — выдаёт номер из своего пула, когда абонент сканирует пустышку, и привязывает его в ЛК оператора вместе с серийником SIM и id сделки amoCRM.
С какими нашими сервисами связан
- amoCRM. Чат-бэкенд ходит в amoCRM сам, напрямую, со своим ограничителем частоты — не через шлюз
amo-queue.donula26.ru. Аккаунт задаётся вAMO_SUBDOMAIN, это не основной аккаунт donula. Воронка «Активация 2026». ID воронки, этапов и полей («Выбрал номер», «Серийный номер SIM», «Ссылка на ЛК») — константы вchat-backend/funnel.py. - Мониторинг. У чата и активации есть внешние пробы, но на публичной статус-странице они скрыты.
- Других сервисов на
*.donula26.ruкод не вызывает.
Как устроено
Веб-кабинет
- Маршруты — в адресной строке (
web/src/app/App.tsx). Вкладки: «Главная», «Новости», «Поддержка» (чат), «Профиль». Отдельно: вход и восстановление пароля, заказ SIM (ссылки на магазины Ozon и Wildberries), итог оплаты/pay/:result, установка на «Домой»/install, вход по ссылке/magic, флоу активации/activation/*. - Все адреса API относительные (
web/src/shared/config.ts). Всё проксирует nginx того же домена, прямые адреса оператора из браузера не используются. - Главная одна на все состояния номера. Подписи статусов — в
web/src/shared/lib/numberStatus.ts: «Активен», «Блокирован», «Ожидает оплаты», «Ожидает регистрацию», «Активируется». Незнакомый статус подписывается нейтрально. - Service worker (
web/src/sw.ts) отвечает только за пуши и клик по уведомлению, офлайн-кеша нет. HTML отдаётся без кэша, ассеты — с хешем в имени. - Иконочный шрифт хранится у нас (
public/fonts/ms.woff2): Google Fonts из РФ недоступен.
Активация SIM
- Контактный номер → код из SMS. Код шлёт и проверяет ЛК оператора (регистрация по коду), в amoCRM заводится сделка.
- Скан серийника пустышки. Сервис активации бронирует номер из пула на 30 минут, берёт у чат-бэкенда id сделки, привязывает номер в ЛК. Пул разделён на B2C и B2B, пропорция выдачи настраивается в админке.
- Оплата, дальше развилка по сегменту номера: B2C подписывает договор через Госуслуги и Госключ, B2B снимает паспорт своей камерой с рамкой (
/activation/kyc). - На каждом шаге фронт двигает сделку по этапам воронки (
POST /api/v1/activation/stage).
Статусы в базе активации. Номер: pool (свободен), res (бронь), activ (активируется), act (активен), taken (уже занят в ЛК), blk, quar (карантин). SIM: ship → pay → prov → bound, отдельно blk. Каждое движение номера пишется в журнал number_log, в админке он открывается щелчком по номеру или ICCID. Сама таблица numbers хранит только текущее состояние.
Нативная оболочка
Приложение native/ грузит не React-сборку, а свою офлайн-копию прежнего фронта (native/Yumobile/app.html и screens/). Чат, регистрацию пуш-токена и часть API берёт с app.umobile26.ru. Собирается скриптом native/build-install.sh с бесплатной подписью разработчика. Правки в web/ в нативку не попадают.
Авторизация в общих словах
- Абонент ↔ ЛК оператора: вход по номеру и коду из SMS, в ответ Bearer-токен ЛК. Он хранится в браузере и идёт в
/lkapi/. - Абонент ↔ наши бэкенды: токен ЛК обменивается на токен нашей сессии (
POST /actapp/session). Сервис активации проверяет токен запросом профиля в ЛК и берёт номера абонента оттуда. Сессия живёт 30 дней и подписанаSESSION_SECRET— он общий у обоих бэкендов, поэтому сессию, выданную активацией, принимает и чат. Ручки чата, уведомлений, подписки на пуши, скана и состояния активации сверяют номер из запроса с номерами в сессии. - Сервис ↔ сервис: биллинг — заголовок со своим секретом (
NOTIFY_SECRET), к ЛК — внутренний ключ (LK_INTERNAL_KEY), активация к чату —APP_SECRETчата. - Админка активации —
app.umobile26.ru/activation/, за basic-auth и ключомADMIN_KEY.
Переменные окружения
Значения — в .env рядом с docker-compose.yml каждого бэкенда, в git их нет. Полный список чата — chat-backend/config.py.
| Сервис | Переменные |
|---|---|
| chat-backend | DATABASE_URL, AMO_SUBDOMAIN, AMO_CLIENT_ID, AMO_CLIENT_SECRET, AMO_REDIRECT_URI, AMOJO_CHANNEL_ID, AMOJO_CHANNEL_CODE, AMOJO_CHANNEL_SECRET, APP_SECRET, SESSION_SECRET, NOTIFY_SECRET, LK_INTERNAL_KEY, MAGIC_SECRET, MAGIC_WORKER, VAPID_PRIVATE_PATH, VAPID_SUBJECT, FCM_PROJECT_ID, FCM_CREDENTIALS_PATH, PUBLIC_BASE_URL, WORKERS |
| activation-admin | DATABASE_URL, ADMIN_KEY, APP_KEY, SESSION_SECRET, LK_BASE, LK_INTERNAL_KEY, CHAT_BASE, APP_SECRET, OTP_TEST_MODE |
| web (при сборке) | VITE_APP_SECRET, VITE_APP_KEY, VITE_VAPID_PUBLIC_KEY — необязательные, без них берутся значения из web/src/shared/config.ts |
Ссылка с автовходом
В чат-бэкенде есть догон тех, кто бросил активацию на середине. На вебхук amoCRM о новой сделке бэкенд выпускает короткую ссылку app.umobile.ru/i/<код> и пишет её в поле сделки «Ссылка на ЛК». По ссылке абонент попадает в кабинет уже вошедшим, без ввода номера и кода, а главная сама показывает следующий шаг. Код гасит не переход по ссылке, а запрос из приложения — так превью-боты мессенджеров не сжигают ссылку раньше человека. Фоновая рассылка напоминаний включается переменной MAGIC_WORKER, по умолчанию выключена. По SPEC-magic-link.md 03.09.2026 рассылку приостановили до ответа автора API оператора. Что включено сейчас, смотреть в .env на сервере.
Локальный запуск
- Фронт:
cd web && npm install && npm run dev, открытьhttp://localhost:5173/app/. Дев-сервер проксирует API на боевойapp.umobile.ru: тестового контура у оператора нет, данные настоящие. - Мок-режим — чтобы открыть экраны за авторизацией без живого абонента. В одном терминале
npm run mock-api, в другомnpm run dev:mock. Нужный экран открывается адресом/app/__dev?to=/app/news. Состояние номера в моке переключается адресами мок-сервера/__state/…,/__seg/…,/__kyc/…(см.web/mock-server.mjs). - Бэкенды:
docker compose up -d --buildвchat-backend/иactivation-admin/. Нужны Postgres и заполненный.env. Чат при первом старте связывается с amoCRM через/amo/auth, проверка —GET /health(amo_authorized,amojo_connected). Админка активации —http://localhost:8020/?k=<ADMIN_KEY>. - iOS:
cd native && ./build-install.sh sim(нужны macOS и Xcode).
Выкатка
- Выкатывает CI. Пуш в
mainзапускает.github/workflows/ci.yml:lint → sast → build → deploy. Веткаdevи pull request проходят первые три стадии без выкатки. Deploy идёт на своём раннере на боевом сервере, ключей от сервера в GitHub нет. - Раннер запускает только
deploy/umobile-deploy.shс цельюweb,activationилиchat. Порядок: фронт, активация, чат. Для бэкендов скрипт снимает копию текущей версии, пересобирает образ и возвращает старую версию, если новая не ответила или в контейнере активации пустые ключи. - У чата и активации есть внешние пробы, поэтому выкатка — в окне обслуживания. Окно открывать до мержа в
main: выкатка начинается сама. - Конфиги nginx в
server/— только справочные копии, CI их не выкатывает. Правка nginx делается на сервере руками.
На что наступают
- Разработка идёт на боевых данных.
npm run devходит в боевой ЛК и боевые бэкенды. WebSocket дев-сервер проксирует на боевой домен даже в мок-режиме. - Мерж в
main— это выкатка на прод, отдельной кнопки нет. - Выкатка фронта заменяет каталог сайта целиком (
rsync --delete). Всё, чего нет в сборкеweb/dist, с сайта пропадает. Класть файлы руками рядом со сборкой бесполезно — их сотрёт следующая выкатка. - Бэкенды — только с пересборкой образа. Код копируется в образ, поэтому
docker compose restartправку не подхватит, нуженup -d --build. - Секрет со знаком
$вenvironment:compose приезжает обрезанным. Сервис поднимается и молча отвечает 401. Секреты передавать только черезenv_file: вactivation-admin/docker-compose.ymlAPP_SECRETнарочно не выписан. SESSION_SECRETдолжен совпадать у обоих бэкендов, аAPP_SECRETактивации — сAPP_SECRETчата. Разошёлся первый — все персональные ручки чата отвечают 403. Нет второго — активация не получает сделку, и каждый скан SIM отвечаетamo_lead_missing.- Активация зависит от amoCRM и чат-бэкенда. Без сделки номер в ЛК не привязывается: скан возвращает
amo_lead_missing, бронь откатывается, номер возвращается в пул. - Ошибки этапов воронки не видны абоненту. Фронт отправляет этап и игнорирует ответ, так что сбой видно только в логах чат-бэкенда.
- Статус «Активируется» ЛК сам не отдаёт. Приложение ставит его себе, когда видит подпись в Госключе или принятые документы.
- ЛК виснет на частых запросах. Поэтому
/lkapi/profile/и/lkapi/number/data/идут через микрокэш nginx на 5 секунд с токеном в ключе. Состояние кэша видно в заголовкеX-LK-Cache(HIT,MISS,STALE). Повтор кода входа раньше чем через минуту ЛК отклоняет с425. - Таймауты фронта: обычный запрос — 15 секунд (
REQUEST_TIMEOUT_MS), отправка снимков паспорта — 90 секунд (UPLOAD_TIMEOUT_MS). Новый запрос с тяжёлым телом на общем таймауте будет сдаваться на мобильной сети. - Не писать пары «свойство +
-webkit--двойник» руками. Префиксы проставляет сборка поbuild.cssTargetвweb/vite.config.ts. Иначе минификатор выкинет одну из форм, и размытие пропадёт в Chrome или Safari. В деве CSS не минифицируется, поэтому такие вещи проверять черезnpm run preview. - Номер, снятый с пустышки после привязки в ЛК, уходит в карантин (
quar), а не в пул. Метода отвязки у ЛК нет: привязку снимают в ЛК руками, потом номер переводят в «Свободен» в админке. Карантин B2C и B2B не смешивается. - OTP-ручки сервиса активации (
/actapp/send-code,/actapp/verify-code) SMS не отправляют, SMS-шлюза нет. Веб подтверждает номер через ЛК оператора и эти ручки не вызывает. - Нативка живёт своей копией фронта. Изменения веба туда не доезжают. Бесплатная подпись разработчика действует 7 дней, потом приложение на телефоне перестаёт запускаться — пересобрать
./build-install.sh device. - Postgres стоит на хосте, не в контейнере. Контейнеры ходят к нему через
host.docker.internal. Если оба сервиса падают на старте с ошибкой подключения к базе, сначала смотреть, на каких адресах слушает Postgres.
WebRTC-телефония: pbx
Для разработчиков. Состояние на 28.09.2026. Подробности, полный список ручек и переменных — README репозитория Buharestskie/pbx. Кода самого виджета amoCRM в репозитории нет — только его серверная часть.
Коротко
pbx — собственная WebRTC-телефония отдела продаж на Asterisk. Главное в ней — автодозвонщик: менеджер ставит статус «Готов», система берёт из очереди следующий лид, звонит сначала менеджеру, после его ответа — клиенту, и соединяет их. Менеджер разговаривает из браузера: виджет в amoCRM или приложение-софтфон. После звонка в сделке появляется примечание со ссылкой на запись.
Лиды попадают в очередь из amoCRM вебхуками этапов: сделка перешла на этап — контакт встал в кампанию. Руководители настраивают кампании, операторов и смотрят дашборды в админке.
Из чего состоит
| Каталог | Что это |
|---|---|
backend/ | Один процесс на Node.js 20 (ES-модули): HTTP API на Express (порт API_PORT, 3000), WebSocket для виджетов (WS_PORT, 3001), клиент Asterisk ARI (ari-client), свой клиент AMI (только для стерео-записи), PostgreSQL (pg). Там же фоновые задачи: ready-тик раз в 10 с, воркер вебхуков, сторожа зависших контактов и каналов, перевод из затянувшейся постобработки в «Паузу», отправка статусов менеджеров наружу. |
admin/ | Админка — статические HTML-страницы: кампании, операторы, группы, транки, график работы, история звонков, дашборды. Ходят в /api/admin/… по относительному пути. Вкладка «Трансфер» — пока статичный макет без запросов к API. |
leadboard/ | Одна страница: таймлайн работы с лидами МПП-2 (/api/admin/dashboards/lead-timeline). |
guide/ | Инструкции для операторов и заметки о релизах виджета. Чистая статика, к API не обращается. |
scripts/ | Серверные скрипты по cron: бэкап (pbx-backup.sh), разбор громкости свежих записей (analyze-recordings.sh), генератор отчёта о постобработках (regen-wrapup-report.py, его же запускает кнопка в админке). |
Статику backend не раздаёт: в Express её нет, страницы отдаёт веб-сервер перед ним. Скрипты в корне backend/ (checkleads*.mjs, chat-*.js, reconcile.mjs и подобные) — разовые сверки, в работе сервиса не участвуют.
Чего в репозитории нет: исходников виджета amoCRM, софтфона для Mac/Windows и расширения браузера, конфигов Asterisk (pjsip, диалплан, звуки опроса) и coturn. Бэкенд только генерирует из базы SIP-аккаунты операторов и клиентов приложения и перезагружает pjsip.
Путь звонка
- Лид в очереди. amoCRM шлёт вебхук этапа на
/api/admin/webhook/<токен кампании>. Ручка только кладёт id сделки в таблицуwebhook_queue. Воркер раз в 2 с берёт до 5 строк, тянет сделку и телефон контакта из amoCRM и добавляет контакт в кампанию. - Менеджер «Готов». Виджет держит WebSocket с бэкендом (
auth,agent_status,sip_registered…) и SIP-регистрацию в Asterisk через WebRTC. Звонить начинают только послеsip_registered. - Выбор контакта. PL/pgSQL-функция
pick_next_contactатомарно выдаёт контакт (FOR UPDATE SKIP LOCKED) с учётом приоритета кампаний, закрепления за менеджером, окна звонков по часовому поясу клиента и циклов повторов. - Звонок менеджеру. Виджету уходит
screen_popс карточкой лида, через 300 мс — ARI originate наPJSIP/<extension>в Stasis-приложение (STASIS_APP, по умолчаниюpbx-dialer) с аргументамиagent,<callId>. - Менеджер ответил (событие
StasisStartс рольюagent) — виджетуdialing_client, статус менеджера становитсяoncall, менеджер назначается ответственным за сделку в amoCRM (кроме нескольких воронок-исключений в коде). Затем originate клиенту через транк: по умолчанию шаблонOUTBOUND_ENDPOINT_TEMPLATE, у оператора может быть свой транк. - Клиент ответил (
StasisStartс рольюclient) — оба канала в mixing-bridge, виджетуconnected, стартует запись: стерео через AMI MixMonitor, при сбое — моно через ARI. - Конец разговора (
StasisEnd) — строка вcall_log, примечание-звонок в сделку amoCRM со ссылкой на запись, виджетуcall_ended; если клиент ответил, менеджер уходит в постобработку. Если трубку первым положил менеджер, а разговор длился не меньшеRATING_SURVEY_MIN_TALK_SEC(40 с), клиента оставляют на линии и просят оценить разговор цифрой 1–5 (включаетсяRATING_SURVEY_ENABLED=1, оценки — вcall_ratings).
Если SIP менеджера отвалился посреди звонка, есть восстановление: 15 с на переподключение и повторный originate. Состояние активных звонков живёт только в памяти процесса.
Другие пути через тот же диалер:
- Ручной звонок —
manual_callпо WebSocket или click-to-call из amoCRM (/api/app/click-call). Личный транк менеджера (линия «Личная») используется только здесь, автообзвон всегда идёт с общей линии. - Перевод МПП-1 → МПП-2 (
/api/transfer/initiate): клоузер выбирается среди «Готовых» по rr-credit из mpp2-touches, ему уходитscreen_popи originate с рольюcloser; если заданыMPP2_PIPELINE_IDиMPP2_STATUS_TRANSFERRED, сделка переставляется в воронку МПП-2 на клоузера. - Звонок в мобильное приложение ДОНУЛЯ (
/api/app/provision,/call,/connect): клиенту заводится SIP-аккаунт, бэкенд приложения получает запрос на push, после ответа в приложении Asterisk соединяет его через диалплан, а не через Stasis.
Входящих звонков в диалере нет: StasisStart без известной роли игнорируется.
Откуда данные и куда уходят
- amoCRM — напрямую, не через шлюз amo-queue. Своя OAuth-интеграция: токены в таблице
settings, обновляются по 401 и пишутся туда же. Все запросы идут через общий лимитер процесса (не чаще ~4 в секунду, пауза на 429/403). Читает сделки и контакты, пишет примечания о звонках, ответственного, этапы при переводе и в дедупликации. - PostgreSQL — вся логика очередей, кампании, операторы, журнал звонков
call_log, оценки, лента событий, настройки. База своя, отдельная. - Записи разговоров лежат на диске сервера телефонии. Наружу они уходят только ссылкой
https://pbx.donula26.ru/api/recordings/<файл>в примечании amoCRM: подпись HMAC (RECORDINGS_HMAC_SECRET) и срок жизни (RECORDINGS_TTL_SEC, 30 суток). Если wav уже сжат в opus, по той же ссылке отдаётся opus. - AI-разбор звонков МПП-2 — по вебхуку
/api/admin/webhook-analysis/<токен>при переходе сделки в «Думает» или «Обещал оплатить»: склейка записей, распознавание (Yandex SpeechKit через Object Storage, запасной вариант AssemblyAI), анализ YandexGPT. Результат пишется в таблицуcall_analysis. Включается настройкойmpp_analysis_enabledи флагом у менеджера. - TURN —
turn.donula26.ru: виджет и приложение берут временные креды в/api/widget/turn-configи/api/app/turn-config.
Связи с нашими сервисами:
| Сервис | Как связан |
|---|---|
| mpp2-touches | pbx берёт у него rr-credits для выбора клоузера, сообщает о назначении менеджера и о смене статусов (MPP2_TOUCHES_BASE) |
| mpp2-tasks-widget | получает смену статусов менеджеров (MPP2_TASKS_STATUS_URL); сам ставит статус «Готов» / «Офф» через POST /api/widget/agent-status и читает график работы |
| dashboard, dashboard_mpp2, SpeechApi | забирают историю звонков из /api/history — API в формате команды history Мегафон ОАТС (EXTERNAL_API_TOKEN) |
| webhook_for_call_record | скачивает записи по ссылкам из примечаний amoCRM |
бэкенд мобильного приложения, app.donula26.ru | заводит клиентам SIP-аккаунты в pbx и получает от pbx запрос на push о входящем (APP_BACKEND_URL, APP_PROVISION_SECRET) |
Статусы всех менеджеров отдаются в GET /api/manager-statuses (MANAGER_STATUS_TOKEN): ready «Готов», oncall «Занят», pause «Пауза», wrapup «Постобработка», learning «Обучение», offline «Офф».
Где код
- Репозиторий
Buharestskie/pbx, веткаmain. Точка входа —backend/src/app.js, на проде процесс стартует черезbackend/src/bootstrap.js. - Диалер —
backend/src/dialer/(agent-first.js— основной сценарий,transfer.js— перевод,originate.js), события ARI разводятся вapp.js. - WebSocket —
backend/src/ws/gateway.js, ручки —backend/src/http/routes/, amoCRM —backend/src/integrations/amo/, очередь и воркеры —backend/src/queue/, миграции —backend/src/db/migrations/. - Переменные окружения читаются из
backend/.env; полный список с описанием — в README. Часть настроек (токены amoCRM,mpp_analysis_*) живёт в таблицеsettings.
Локальный запуск
- Нужны Node.js 20+ и PostgreSQL. Asterisk не обязателен: без ARI backend пишет
Running in offline mode (no Asterisk), поднимает HTTP и WebSocket, но звонить не может. - Схема базы — только дампом схемы с прода (как снять — в README): миграции её с нуля не создают. Из
settingsудалить токены amoCRM, если локальный процесс не должен ходить в боевой amoCRM. cd backend && cp .env.example .env, задатьPG_*, любое значениеASTERISK_AMI_PASSWORDи локальные путиPJSIP_AGENTS_PATH/PJSIP_APP_CLIENTS_PATH.npm install,npm run migrate,npm start, проверитьcurl -s localhost:3000/health— ответ{"status":"ok","asterisk":"offline",…}.- WebSocket — любым клиентом на
ws://localhost:3001, первым сообщением{"type":"auth","agent_id":…}. Страницыadmin/иleadboard/нужно открывать с того же адреса, что и API.
Выкатка
- Только после 21:00 МСК. Перезапуск бэкенда обрывает текущие разговоры, а днём простой телефонии видит вся компания. Читать логи можно в любое время.
- На сервере нет git: код копируется из
main(rsync, без--delete). Перед этим — бэкапscripts/pbx-backup.sh. Дальше по обстоятельствам:npm ci --omit=dev, если менялись зависимости;npm run migrate, если появились миграции; перезапуск systemd-юнитаpbx-backend, если менялсяbackend/. Правкиadmin/,guide/,leadboard/— статика, перезапуск не нужен. - Проверка:
https://pbx.donula26.ru/healthотвечает"asterisk":"connected", в журнале юнита —Stasis app "pbx-dialer" started. Виджеты сами переподнимают SIP примерно через 5 с после старта (сообщениеforce_sip_reconnect). - Внешней пробы в мониторинге у pbx сейчас нет, поэтому окно обслуживания на статус-странице открыть не на что.
На что наступают
- Виджета amoCRM в репозитории нет. В amoCRM он загружен архивом, исходники не найдены. Меняя протокол WebSocket или
/api/widget/*, помните, что клиент поправить нечем. - Миграции не поднимают базу с нуля. Части таблиц (
agent_groups,agent_campaigns,settings,trunks,transfersи др.) нет ни в одной миграции, в нумерации дыры и дубли (два028_*, два029_*). На пустой базеnpm run migrateпадает. - Без
ASTERISK_AMI_PASSWORDпроцесс падает на старте, даже если Asterisk не нужен. - ARI подключается один раз, при старте. Если Asterisk поднялся позже бэкенда, процесс жив, но не звонит; в
/health—"asterisk":"offline". Лечится перезапуском бэкенда после Asterisk. - Прод стартует
bootstrap.js, аnpm start—app.js.bootstrap.jsограничивает параллельные HTTP-соединения к Asterisk шестью; локально этого ограничения нет.src/config/app.js— устаревшая копияapp.js, её никто не импортирует. - Бэкенд должен жить на одном хосте с Asterisk. Он сам пишет pjsip-конфиги, вызывает
asterisk -rx,ffmpeg/ffprobe, а серверный путь к генератору отчёта постобработок зашит в код. rsync --deleteсотрёт то, чего нет в git: установщики софтфона вguide/appи файлы, которые генерирует сервер.admin/wrapup-report.htmlиadmin/.wrapup-status.jsonв git лежат старыми копиями — при выкатке их исключают, иначе затрётся свежий отчёт.- Кнопка «Перезапустить Asterisk» в админке рвёт все звонки разом.
- Поддомен amoCRM по умолчанию в коде —
donulya2, а админка и скрипты ссылаются наdonula.amocrm.ru.AMO_SUBDOMAINзадавать явно. - Блокировка «Готов» работает в режиме fail-open: если от amoCRM 5 минут нет вебхуков, пропускает всех. Аварийный откат —
READY_CHECK_ENABLED=0. - Дедупликация лидов «Охотников» по умолчанию в dry-run — решения только в логе. Реальные действия в amoCRM — с
DEDUP_DRY_RUN=0. - Запись второго плеча перевода по умолчанию пишется в другой каталог (
RECORDING_DIR), чем тот, из которого раздаёт/api/recordings, — по ссылке её не получить. .env.exampleне совпадает с кодом: 8 переменных код не читает, около 20 читаемых там нет. Сверяйтесь с таблицей в README.- Ошибки в GlitchTip не уходят — SDK в коде нет. Смотреть журнал юнита и
logs/error.log.