Перейти до змісту

IP-телефонія (SIP/WebRTC) — Документація

Що це

Інтеграція IP-телефонії в DOP-додаток через протокол SIP over WebSocket з використанням WebRTC для передачі голосу. Дозволяє здійснювати телефонні дзвінки прямо з браузера — натиснув на іконку телефону в картці клієнта і одразу розмовляєш.

Бібліотека: JsSIP 3.x — зрілий SIP-стек для браузера, підтримує WebRTC, працює з Asterisk, FreeSWITCH, Obbitel, Binotel та іншими SIP-серверами.


Навіщо

  1. Швидкість — дзвінок в один клік без перемикання між програмами
  2. Контекст — оператор бачить картку клієнта прямо під час дзвінка
  3. Єдина система — всі комунікації (дзвінки, чат, email) в одному інтерфейсі
  4. Без додаткового ПЗ — не потрібен софтфон, достатньо браузера + гарнітура

Архітектура

┌──────────────┐     WebSocket (WSS)     ┌──────────────┐
│   Браузер    │ ◄──────────────────────► │  SIP-сервер  │
│  (JsSIP +    │     SIP signaling       │  (Asterisk/  │
│   WebRTC)    │                          │  FreeSWITCH) │
│              │     WebRTC (SRTP)        │              │
│   🎤 ──────► │ ◄──────────────────────► │ ──► PSTN ☎️  │
│              │     голосовий потік       │              │
└──────────────┘                          └──────────────┘
       │                                         │
       │  STUN/TURN                              │
       └──────────► NAT Traversal ◄──────────────┘

Потік дзвінка: 1. Браузер підключається до SIP-сервера через WebSocket (WSS) 2. JsSIP реєструє SIP-extension (наприклад, sip:101@pbx.example.com) 3. При натисканні на телефон — відправляється SIP INVITE 4. SIP-сервер маршрутизує дзвінок (внутрішній або через PSTN-шлюз) 5. WebRTC встановлює голосовий канал (SRTP) між браузером і сервером 6. STUN/TURN допомагають обійти NAT


Що було реалізовано

Backend (Django)

Файл Опис
backend/essentials/models/telephony_settings.py Модель TelephonySettings — налаштування SIP-сервера per tenant
backend/essentials/serializers/master_data.py TelephonySettingsSerializer
backend/essentials/views/master_data.py TelephonySettingsViewSet
backend/essentials/urls.py URL: /api/v1/essentials/telephony-settings/
backend/essentials/migrations/0042_telephony_settings.py Міграція БД

Модель TelephonySettings (singleton per tenant): - is_enabled — увімкнення/вимкнення телефонії - provider — тип АТС (Asterisk, FreeSWITCH, Binotel, Other) - sip_server — адреса SIP-сервера (наприклад pbx.example.com) - ws_url — WebSocket URL (наприклад wss://pbx.example.com:8089/ws) - sip_realm — SIP домен для автентифікації - outbound_prefix — префікс для зовнішніх номерів (наприклад 9) - stun_server, turn_server, turn_username, turn_password — NAT traversal

Frontend (React)

Файл Опис
frontend/erp/src/api/telephony.ts API-клієнт для CRUD налаштувань
frontend/erp/src/store/telephonyStore.ts Zustand store — серверні налаштування + локальні SIP credentials + стан дзвінка
frontend/erp/src/hooks/useSipPhone.ts Головний хук — JsSIP UserAgent, call(), hangup(), WebRTC audio
frontend/erp/src/components/Telephony/SipCallOverlay.tsx Floating overlay — статус дзвінка, таймер, кнопка завершення
frontend/erp/src/pages/SettingsPage/TelephonySettings.tsx Сторінка налаштувань телефонії

Інтеграція в існуючі компоненти: - ClientFields.tsx — кнопки телефону біля контактних осіб (контактна особа, директор, бухгалтер, касир) тепер підтримують SIP-дзвінки - App.tsx — глобально підключений SipCallOverlay - SettingsPage/index.tsx — додано секцію "Телефонія" в налаштування


Як працює кнопка телефону

Клік на іконку 📞 біля контактної особи
       │
       ├── SIP налаштований? (useSipPhone.isReady === true)
       │   ├── Так → sip.call(phone, name) → SIP INVITE → дзвінок через АТС
       │   └── Ні  → fallback на <a href="tel:..."> (стандартний tel: протокол)
       │
       └── Під час дзвінка → SipCallOverlay показує:
           - Ім'я / номер
           - Статус (З'єднання → Виклик → Розмова → Завершено)
           - Таймер тривалості
           - Кнопка завершення (червона)

Як налаштувати

1. Налаштування SIP-сервера (адмін тенанту)

Перейти в Settings → Телефонія і заповнити:

Поле Приклад Опис
Provider Asterisk Тип вашої АТС
SIP Server pbx.company.com Адреса SIP-сервера
WebSocket URL wss://pbx.company.com:8089/ws WebSocket endpoint (обов'язково WSS!)
SIP Realm pbx.company.com Домен автентифікації (зазвичай = SIP Server)
Outbound Prefix 9 Префікс для зовнішніх ліній (залежить від АТС)
STUN Server stun:stun.l.google.com:19302 За замовчуванням Google STUN
TURN Server turn:turn.company.com:3478 Потрібен якщо є проблеми з NAT

Натиснути "Зберегти" — налаштування збережуться на сервері для всього тенанту.

2. Налаштування SIP-акаунту (кожен користувач)

На тій же сторінці, секція "Ваш SIP-акаунт":

Поле Приклад Опис
SIP User 101 Ваш внутрішній номер (extension) на АТС
SIP Password **** Пароль до SIP-акаунту

Важливо: SIP credentials зберігаються тільки в браузері (localStorage) і ніколи не відправляються на сервер ESWF.

3. Використання

  1. Відкрити картку контрагента
  2. Біля контактної особи з'явиться зелена іконка телефону (якщо SIP активний)
  3. Клік → починається дзвінок
  4. В правому нижньому куті з'явиться overlay з інформацією про дзвінок
  5. Натиснути червону кнопку для завершення

Вимоги до SIP-сервера

Asterisk

В http.conf увімкнути WebSocket:

[general]
enabled=yes
bindaddr=0.0.0.0
bindport=8088
tlsenable=yes
tlsbindaddr=0.0.0.0:8089
tlscertfile=/etc/asterisk/keys/asterisk.pem

В pjsip.conf додати WebSocket transport:

[transport-wss]
type=transport
protocol=wss
bind=0.0.0.0:8089
cert_file=/etc/asterisk/keys/asterisk.pem

Extension для WebRTC:

[101]
type=endpoint
transport=transport-wss
context=default
disallow=all
allow=opus
allow=ulaw
webrtc=yes
auth=101
aors=101

FreeSWITCH

В vars.xml:

<X-PRE-PROCESS cmd="set" data="internal_ssl_enable=true"/>

WebSocket listener в internal.xml:

<param name="ws-binding" value=":5066"/>
<param name="wss-binding" value=":7443"/>


Стан дзвінка (CallStatus)

Статус Опис Колір
idle Немає дзвінка —
connecting Відправлено SIP INVITE жовтий
ringing Абонент отримав виклик синій
active Розмова зелений
ended Дзвінок завершено сірий
error Помилка (мережа, авторизація тощо) червоний

Технічні деталі

Zustand Store (telephonyStore)

Зберігає в localStorage під ключем eswf-telephony: - Серверні налаштування (кеш з backend) — wsUrl, sipServer, stunServer тощо - Локальні SIP credentials — sipUser, sipPassword - НЕ зберігає стан дзвінка (транзієнтний)

JsSIP UserAgent lifecycle

  1. При зміні credentials/config → створюється новий JsSIP.UA
  2. UA підключається до WebSocket і реєструється на SIP-сервері
  3. При call() → створюється RTCSession з WebRTC audio
  4. Remote audio stream прив'язується до прихованого <audio> елемента
  5. При unmount або зміні config → UA зупиняється

Fallback поведінка

Якщо телефонія не налаштована (useSipPhone.isReady === false): - Кнопка телефону працює як звичайний <a href="tel:..."> лінк - На мобільних — відкриє дайлер - На десктопі — відкриє прив'язану програму (Skype, Teams тощо)


Своя безкоштовна АТС в компанії — що для цього потрібно

Загальна ідея

Asterisk (або FreePBX) — це безкоштовна open-source АТС. Встановлюється на будь-який сервер або ПК, перетворюючи його в повноцінну телефонну станцію. Всі дзвінки між співробітниками йдуть через інтернет — безкоштовно і безлімітно. Для дзвінків на мобільні/стаціонарні — підключається SIP-провайдер (копійки за хвилину).

Що потрібно

Компонент Ціна Що це
Asterisk / FreePBX 0 грн Сервер АТС — маршрутизація дзвінків, голосова пошта, IVR, запис
Сервер VPS від $5/міс або будь-який старий ПК в офісі Де крутиться Asterisk
Інтернет Вже є Передача голосу (VoIP), ~100 Kbps на дзвінок
Гарнітури / навушники від 200 грн Або просто навушники від телефону
Софтфони 0 грн Браузер (як в ESWF), або Obbitel, MicroSIP, Obbitel (безкоштовні)
Docker (для тесту) 0 грн Запуск Asterisk в контейнері за 2 хвилини

Що отримуєш безкоштовно (внутрішні дзвінки)

  • Дзвінки між співробітниками — безлімітно, 0 грн, з будь-якої точки світу
  • Конференц-дзвінки — скільки завгодно учасників одночасно
  • Голосова пошта — якщо не відповіли, запис залишається
  • IVR — голосове меню ("Натисніть 1 для відділу продаж, 2 для бухгалтерії...")
  • Черги дзвінків — call center функціонал (оператори по черзі)
  • Запис розмов — всі дзвінки записуються автоматично
  • Дзвінки між офісами (Київ ↔ Львів ↔ Одеса) — 0 грн через інтернет
  • Віддалені працівники — працюють з дому як ніби сидять в офісі

Дзвінки на мобільні / стаціонарні (SIP-провайдери)

Для виходу на реальну телефонну мережу (PSTN) підключається SIP-trunk — це як міст між інтернетом і телефонними лініями:

Провайдер (Україна) Вартість Що дає
Binotel від 300 грн/міс Український номер + хвилини + свій CRM
Zadarma від 0 (pay-as-you-go) ~0.30 грн/хв на мобільний UA, безкоштовні внутрішні
IPtel від 150 грн/міс Українські міські номери (044, 032, 048...)
VoIPstudio від $5/юзер/міс Міжнародні дзвінки, номери 100+ країн
Ukrtelecom SIP за тарифом SIP-trunk від Укртелекому

Підсумок: між собою — безкоштовно, назовні — копійки за хвилину.

Типова схема для компанії

                            Інтернет
                               │
                       ┌───────┴───────┐
                       │   Asterisk    │  ← VPS $5/міс або офісний ПК
                       │   (FreePBX)   │
                       └───┬───┬───┬───┘
                           │   │   │
              ┌────────────┘   │   └────────────┐
              │                │                │
        ┌─────┴─────┐   ┌─────┴─────┐   ┌──────┴──────┐
        │ Офіс Київ │   │ Віддалені │   │ SIP-trunk   │
        │ 20 ext.   │   │ працівники│   │ (Zadarma)   │
        │ браузер / │   │ з дому /  │   │    ↓        │
        │ софтфон   │   │ в дорозі  │   │ 044-XXX-XX  │
        └───────────┘   └───────────┘   │ міські/моб. │
                                        └─────────────┘

Порівняння з класичною АТС

Класична офісна АТС VoIP (Asterisk)
Обладнання $2000–10000 (залізо) $0 (програма)
Внутрішні дзвінки По мідному кабелю По інтернету
Віддалені працівники Неможливо Працює звідки завгодно
Масштабування Купувати нові плати розширення Просто додати extension в конфіг
Між офісами Платні міжміські дзвінки Безкоштовно
Запис розмов Додаткове обладнання Вбудовано
IVR / голосове меню Дорога опція Вбудовано
Інтеграція з DOP/CRM Складно або неможливо Нативно через SIP + WebSocket

Локальний тест через Docker

Для швидкого тестування можна підняти Asterisk в Docker-контейнері:

docker-compose up -d

Це дасть: - Extension 101 — твій браузер реєструється як SIP-телефон - Extension 102 — другий тестовий акаунт (другий браузер або MicroSIP) - *43 — echo test: дзвониш і чуєш свій голос із затримкою (перевірка аудіо)

Налаштування в ESWF для локального тесту:

Поле Значення
SIP Server localhost
WebSocket URL wss://localhost:8089/ws
SIP User 101
SIP Password test123

Demo-режим (без сервера взагалі)

Якщо немає SIP-сервера і Docker — в ESWF є вбудований demo-режим:

  1. Settings → Телефонія → увімкнути "Enable IP Telephony"
  2. Увімкнути "Demo Mode" (оранжевий перемикач)
  3. Натиснути "Зберегти"

В demo-режимі кнопки телефону стають зеленими і при натисканні симулюють повний цикл дзвінка (З'єднання → Виклик → Розмова) без реального підключення. Overlay показує бейдж "DEMO".


Можливі розширення (TODO)

  • [ ] Вхідні дзвінки (incoming calls) з popup-нотифікацією
  • [ ] Журнал дзвінків (CallLog) з прив'язкою до клієнта
  • [ ] Запис розмов (recording) через SIP-сервер
  • [ ] Click-to-call з будь-якого місця в DOP (не тільки картка клієнта)
  • [ ] DTMF (тонові сигнали) для IVR-меню
  • [ ] Перевод дзвінка (transfer) на іншого оператора
  • [ ] Конференц-дзвінки
  • [ ] Інтеграція з Binotel API (callback, history)
  • [ ] Статус SIP-реєстрації в хедері (зелена/червона іконка)
  • [ ] Per-user налаштування ringtone