Продукты

Личный кабинет Юмобайл: 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, выдача номера при скане, привязка в ЛК оператора, сессии, админкада
Нативная оболочка iOSnative/SwiftUI + WKWebView; нативные чат, сканер SIM, пуши через Firebaseзаморожена с 26.08.2026, см. ниже
Конфиги nginxserver/справочные копии боевых конфигов обоих доменов и зоны микрокэшасправочно
Выкатка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.

Откуда данные и куда уходят

С какими нашими сервисами связан

Как устроено

Веб-кабинет

Активация SIM

  1. Контактный номер → код из SMS. Код шлёт и проверяет ЛК оператора (регистрация по коду), в amoCRM заводится сделка.
  2. Скан серийника пустышки. Сервис активации бронирует номер из пула на 30 минут, берёт у чат-бэкенда id сделки, привязывает номер в ЛК. Пул разделён на B2C и B2B, пропорция выдачи настраивается в админке.
  3. Оплата, дальше развилка по сегменту номера: B2C подписывает договор через Госуслуги и Госключ, B2B снимает паспорт своей камерой с рамкой (/activation/kyc).
  4. На каждом шаге фронт двигает сделку по этапам воронки (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/ в нативку не попадают.

Авторизация в общих словах

Переменные окружения

Значения — в .env рядом с docker-compose.yml каждого бэкенда, в git их нет. Полный список чата — chat-backend/config.py.

СервисПеременные
chat-backendDATABASE_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-adminDATABASE_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 на сервере.

Локальный запуск

Выкатка

На что наступают

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.

Путь звонка

  1. Лид в очереди. amoCRM шлёт вебхук этапа на /api/admin/webhook/<токен кампании>. Ручка только кладёт id сделки в таблицу webhook_queue. Воркер раз в 2 с берёт до 5 строк, тянет сделку и телефон контакта из amoCRM и добавляет контакт в кампанию.
  2. Менеджер «Готов». Виджет держит WebSocket с бэкендом (auth, agent_status, sip_registered…) и SIP-регистрацию в Asterisk через WebRTC. Звонить начинают только после sip_registered.
  3. Выбор контакта. PL/pgSQL-функция pick_next_contact атомарно выдаёт контакт (FOR UPDATE SKIP LOCKED) с учётом приоритета кампаний, закрепления за менеджером, окна звонков по часовому поясу клиента и циклов повторов.
  4. Звонок менеджеру. Виджету уходит screen_pop с карточкой лида, через 300 мс — ARI originate на PJSIP/<extension> в Stasis-приложение (STASIS_APP, по умолчанию pbx-dialer) с аргументами agent,<callId>.
  5. Менеджер ответил (событие StasisStart с ролью agent) — виджету dialing_client, статус менеджера становится oncall, менеджер назначается ответственным за сделку в amoCRM (кроме нескольких воронок-исключений в коде). Затем originate клиенту через транк: по умолчанию шаблон OUTBOUND_ENDPOINT_TEMPLATE, у оператора может быть свой транк.
  6. Клиент ответил (StasisStart с ролью client) — оба канала в mixing-bridge, виджету connected, стартует запись: стерео через AMI MixMonitor, при сбое — моно через ARI.
  7. Конец разговора (StasisEnd) — строка в call_log, примечание-звонок в сделку amoCRM со ссылкой на запись, виджету call_ended; если клиент ответил, менеджер уходит в постобработку. Если трубку первым положил менеджер, а разговор длился не меньше RATING_SURVEY_MIN_TALK_SEC (40 с), клиента оставляют на линии и просят оценить разговор цифрой 1–5 (включается RATING_SURVEY_ENABLED=1, оценки — в call_ratings).

Если SIP менеджера отвалился посреди звонка, есть восстановление: 15 с на переподключение и повторный originate. Состояние активных звонков живёт только в памяти процесса.

Другие пути через тот же диалер:

Входящих звонков в диалере нет: StasisStart без известной роли игнорируется.

Откуда данные и куда уходят

Связи с нашими сервисами:

СервисКак связан
mpp2-touchespbx берёт у него 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 «Офф».

Где код

Локальный запуск

  1. Нужны Node.js 20+ и PostgreSQL. Asterisk не обязателен: без ARI backend пишет Running in offline mode (no Asterisk), поднимает HTTP и WebSocket, но звонить не может.
  2. Схема базы — только дампом схемы с прода (как снять — в README): миграции её с нуля не создают. Из settings удалить токены amoCRM, если локальный процесс не должен ходить в боевой amoCRM.
  3. cd backend && cp .env.example .env, задать PG_*, любое значение ASTERISK_AMI_PASSWORD и локальные пути PJSIP_AGENTS_PATH / PJSIP_APP_CLIENTS_PATH.
  4. npm install, npm run migrate, npm start, проверить curl -s localhost:3000/health — ответ {"status":"ok","asterisk":"offline",…}.
  5. WebSocket — любым клиентом на ws://localhost:3001, первым сообщением {"type":"auth","agent_id":…}. Страницы admin/ и leadboard/ нужно открывать с того же адреса, что и API.

Выкатка

На что наступают