Implements a Pion Go B2BUA media server sidecar that sits between Meta's WhatsApp Cloud API and the agent's browser, enabling call persistence across page reloads, server-side recording, multi-participant support, audio injection, and AI integration readiness. Go Media Server (enterprise/media-server/): - Pion WebRTC v4 B2BUA with Peer A (Meta) and Peer B (Agent) connections - Real-time OGG/Opus recording with crash recovery - Audio bridge with multi-peer fan-out and AudioConsumer plugin interface - Audio injection from OGG files with loop support for hold music - Session manager with graceful shutdown and orphaned recording recovery - HTTP API with Bearer token auth, health checks, and metrics Rails Integration: - Whatsapp::MediaServerClient HTTP client for Go sidecar communication - Dual-mode CallService: legacy browser-direct and server-relay paths - Media server callback controller for agent disconnect/recording/terminate - CallRecordingFetchJob: downloads OGG from Go server to ActiveStorage/S3 - CallCleanupJob: sweeps stale ringing and in-progress calls - New endpoints: active, agent_answer, reconnect, join, play_audio - DB migration: media_session_id column with indexes Frontend: - Dual-mode composable auto-detecting legacy vs server-relay - handleAgentOffer() for receiving SDP from media server - useCallReconnection composable for page reload recovery - Removed terminateCallOnUnload in server-relay mode - Simplified outbound call flow in ConversationHeader - New ActionCable event: whatsapp_call.agent_offer Documentation: - Server-side WebRTC architecture spec (1505 lines) - Implementation plan with 119 trackable checklist items - Feature spec, PR breakdown, and relay architecture docs
147 lines
3.3 KiB
YAML
147 lines
3.3 KiB
YAML
version: '3'
|
|
|
|
services:
|
|
base: &base
|
|
build:
|
|
context: .
|
|
dockerfile: ./docker/Dockerfile
|
|
args:
|
|
BUNDLE_WITHOUT: ''
|
|
EXECJS_RUNTIME: 'Node'
|
|
RAILS_ENV: 'development'
|
|
RAILS_SERVE_STATIC_FILES: 'false'
|
|
tty: true
|
|
stdin_open: true
|
|
image: chatwoot:development
|
|
env_file: .env
|
|
|
|
rails:
|
|
<<: *base
|
|
build:
|
|
context: .
|
|
dockerfile: ./docker/dockerfiles/rails.Dockerfile
|
|
image: chatwoot-rails:development
|
|
volumes:
|
|
- ./:/app:delegated
|
|
- node_modules:/app/node_modules
|
|
- packs:/app/public/packs
|
|
- cache:/app/tmp/cache
|
|
- bundle:/usr/local/bundle
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
- vite
|
|
- mailhog
|
|
- sidekiq
|
|
ports:
|
|
- 3000:3000
|
|
env_file: .env
|
|
environment:
|
|
- VITE_DEV_SERVER_HOST=vite
|
|
- NODE_ENV=development
|
|
- RAILS_ENV=development
|
|
entrypoint: docker/entrypoints/rails.sh
|
|
command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
|
|
|
|
sidekiq:
|
|
<<: *base
|
|
image: chatwoot-rails:development
|
|
volumes:
|
|
- ./:/app:delegated
|
|
- node_modules:/app/node_modules
|
|
- packs:/app/public/packs
|
|
- cache:/app/tmp/cache
|
|
- bundle:/usr/local/bundle
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
- mailhog
|
|
environment:
|
|
- NODE_ENV=development
|
|
- RAILS_ENV=development
|
|
command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"]
|
|
|
|
vite:
|
|
<<: *base
|
|
build:
|
|
context: .
|
|
dockerfile: ./docker/dockerfiles/vite.Dockerfile
|
|
image: chatwoot-vite:development
|
|
volumes:
|
|
- ./:/app:delegated
|
|
- node_modules:/app/node_modules
|
|
- packs:/app/public/packs
|
|
- cache:/app/tmp/cache
|
|
- bundle:/usr/local/bundle
|
|
ports:
|
|
- "3036:3036" # Vite dev server
|
|
environment:
|
|
- VITE_DEV_SERVER_HOST=0.0.0.0
|
|
- NODE_ENV=development
|
|
- RAILS_ENV=development
|
|
entrypoint: docker/entrypoints/vite.sh
|
|
command: bin/vite dev
|
|
|
|
postgres:
|
|
image: pgvector/pgvector:pg16
|
|
restart: always
|
|
ports:
|
|
- '5432:5432'
|
|
volumes:
|
|
- postgres:/data/postgres
|
|
environment:
|
|
- POSTGRES_DB=chatwoot
|
|
- POSTGRES_USER=postgres
|
|
- POSTGRES_PASSWORD=
|
|
|
|
redis:
|
|
image: redis:alpine
|
|
restart: always
|
|
command: ["sh", "-c", "redis-server --requirepass \"$REDIS_PASSWORD\""]
|
|
env_file: .env
|
|
volumes:
|
|
- redis:/data/redis
|
|
ports:
|
|
- '6379:6379'
|
|
|
|
mailhog:
|
|
image: mailhog/mailhog
|
|
ports:
|
|
- 1025:1025
|
|
- 8025:8025
|
|
|
|
media-server:
|
|
image: chatwoot/media-server:latest
|
|
build:
|
|
context: ./enterprise/media-server
|
|
dockerfile: Dockerfile
|
|
ports:
|
|
- '4000:4000'
|
|
- '10000-10100:10000-10100/udp'
|
|
environment:
|
|
- AUTH_TOKEN=${MEDIA_SERVER_AUTH_TOKEN:-}
|
|
- RAILS_CALLBACK_URL=http://rails:3000
|
|
- RECORDINGS_DIR=/recordings
|
|
- LOG_LEVEL=info
|
|
- UDP_PORT_MIN=10000
|
|
- UDP_PORT_MAX=10100
|
|
- PUBLIC_IP=${MEDIA_SERVER_PUBLIC_IP:-}
|
|
- STUN_SERVERS=stun:stun.l.google.com:19302
|
|
volumes:
|
|
- media_recordings:/recordings
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:4000/health']
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
volumes:
|
|
postgres:
|
|
redis:
|
|
packs:
|
|
node_modules:
|
|
cache:
|
|
bundle:
|
|
media_recordings:
|