# Intaleq Voice Call Signaling Backend (API Key & Session Pre-creation) Production-ready WebRTC signaling server in Node.js for the Intaleq ride-hailing application. Coordinates secure peer-to-peer audio calls between driver and passenger during active rides only. Sessions are pre-created by the main application backend over an authenticated HTTP endpoint, allowing mobile clients to connect using temporary session IDs without dealing with JWTs. ## Prerequisites - **Node.js 20+** (for local development) - **Docker** and **Docker Compose** (for production deployments) - **Nginx** (acting as reverse proxy for WSS upgrade and SSL termination) - **Let's Encrypt** (for SSL certificates) ## Local Development 1. **Clone and Navigate** Ensure you are in the `voice-call-service` directory. 2. **Setup Configuration** Copy the example environment file and configure variables: ```bash cp .env.example .env ``` *Note: Ensure `API_KEY` is at least 32 characters long.* 3. **Install Dependencies** ```bash npm install ``` 4. **Run Server** Since Node.js 20.6.0+, you can load `.env` files natively using the `--env-file` flag: ```bash node --env-file=.env cmd/server/main.js ``` The signaling server will start and bind locally to `127.0.0.1:47880`. ## Docker Deployment Deploying with Docker isolates dependencies and bounds resources to 128MB. 1. **Deploy Command** Run the deployment automation script: ```bash ./deploy.sh ``` This script will verify your configuration, build a secure multi-stage container running under user `node` (UID 1000), deploy it in host networking mode, and verify the service's health. 2. **Manual Docker Compose Commands** Alternatively, you can build and run it manually: ```bash docker-compose up -d --build ``` ## Nginx Configuration An Nginx configuration block should be configured on your CloudPanel/hosting server for `calls.intaleqapp.com`. It proxies connections to localhost and processes WebSocket handshakes: ```nginx server { listen 80; server_name calls.intaleqapp.com; location / { proxy_pass http://127.0.0.1:47880; # WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Headers 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; # Read timeout management proxy_read_timeout 120s; proxy_send_timeout 120s; } } ``` ## Environment Variables | Variable | Description | Default | | :--- | :--- | :--- | | `SERVER_ADDR` | Local binding interface and port | `127.0.0.1:47880` | | `API_KEY` | Secret key for authenticating HTTP session creation | None (min 32 chars) | | `LOG_LEVEL` | Level of logging output (`debug`, `info`, `warn`, `error`) | `info` | | `MAX_MESSAGE_BYTES` | Maximum payload size allowed for WebSocket frames | `4096` | | `HEARTBEAT_INTERVAL` | Keepalive ping interval | `20s` | | `HEARTBEAT_TIMEOUT` | Connection death timeout | `30s` | | `SESSION_DURATION` | Hard limit on call length before auto-termination | `60s` | | `RATE_LIMIT_PER_MIN` | Maximum connections allowed per IP per minute | `10` | | `ENVIRONMENT` | Target environment designation | `production` | | `DB_HOST` | MySQL database host address | `127.0.0.1` | | `DB_PORT` | MySQL database connection port | `3306` | | `DB_DATABASE` | MySQL database name | `callDB` | | `DB_USERNAME` | MySQL database username | None | | `DB_PASSWORD` | MySQL database password | None | ## Database Logging The signaling server integrates with a MySQL database to log call connectivity metrics. The system automatically creates a `call_logs` table on startup (defined in `database.sql`). ### Call Logs Schema | Column | Type | Description | | :--- | :--- | :--- | | `id` | `INT` | Primary Key, Auto Increment | | `session_id` | `VARCHAR(36)` | Unique Ephemeral Session ID | | `ride_id` | `VARCHAR(255)` | Active Ride ID | | `driver_id` | `VARCHAR(255)` | Registered Driver ID | | `passenger_id` | `VARCHAR(255)` | Registered Passenger ID | | `status` | `VARCHAR(50)` | Call state (`created`, `active`, `ended`) | | `initiated_by` | `VARCHAR(255)` | User ID of the participant who started the connection (sent first offer) | | `end_reason` | `VARCHAR(255)` | Call termination reason (e.g., `user_terminated`, `max_duration_reached`, `disconnect_driver`, `disconnect_passenger`) | | `created_at` | `TIMESTAMP` | Time when session was pre-created | | `connected_at` | `TIMESTAMP` | Time when both parties connected and call started | | `ended_at` | `TIMESTAMP` | Time when call was terminated | ## HTTP Session Pre-creation API The main application backend (PHP) registers a session before allowing call access. ### Create Call Session * **Route**: `POST /sessions` * **Headers**: * `X-API-Key: YOUR_API_KEY_HERE` * `Content-Type: application/json` * **Request Body**: ```json { "ride_id": "ride_456", "driver_id": "user_123", "passenger_id": "user_789" } ``` * **Response Body (200 OK)**: ```json { "session_id": "3b2e7c4f-95a2-4a0b-99f6-fc935d0a4461", "ride_id": "ride_456", "expires_in": 60 } ``` ## WebSocket Protocol Reference Clients communicate with the backend at `ws://calls.intaleqapp.com/ws`. ### Client-to-Server Messages 1. **Authenticate (MUST be sent first)** ```json { "type": "authenticate", "session_id": "3b2e7c4f-95a2-4a0b-99f6-fc935d0a4461", "user_id": "user_123" } ``` 2. **Offer WebRTC Payload** ```json {"type": "offer", "sdp": "v=0\r\n..."} ``` 3. **Answer WebRTC Payload** ```json {"type": "answer", "sdp": "v=0\r\n..."} ``` 4. **ICE Candidate WebRTC Payload** ```json {"type": "ice_candidate", "candidate": {"candidate": "...", "sdpMid": "0", "sdpMLineIndex": 0}} ``` 5. **Heartbeat** ```json {"type": "heartbeat"} ``` 6. **End Call** ```json {"type": "end_call"} ``` ### Server-to-Client Messages 1. **Authentication Success Alert** ```json {"type": "authenticated", "user_id": "user_123"} ``` 2. **Session Joined (Active status)** ```json {"type": "session_joined", "session_id": "sess_abc", "ride_id": "ride_456"} ``` 3. **Peer Joined Notification** ```json {"type": "participant_joined", "role": "passenger"} ``` 4. **Relayed WebRTC Offer** ```json {"type": "offer", "sdp": "v=0\r\n..."} ``` 5. **Relayed WebRTC Answer** ```json {"type": "answer", "sdp": "v=0\r\n..."} ``` 6. **Relayed ICE Candidate** ```json {"type": "ice_candidate", "candidate": {"candidate": "...", "sdpMid": "0", "sdpMLineIndex": 0}} ``` 7. **Peer Disconnected Alert** ```json {"type": "participant_left", "role": "driver"} ``` 8. **Call Duration Timeout Alert** ```json {"type": "call_timeout", "reason": "max_duration_reached"} ``` 9. **Call Terminated Alert** ```json {"type": "call_ended", "reason": "user_terminated"} ``` 10. **Heartbeat Reply (Pong)** ```json {"type": "pong"} ``` 11. **Unauthorized Connection Error** ```json {"type": "unauthorized", "reason": "session_not_found"} ``` 12. **General Error Message** ```json {"type": "error", "code": "session_not_found", "message": "No active session for this ride"} ``` ## Security Notes 1. **Access Control** Only your trusted main backend can create call sessions, verified via the `X-API-Key` header. Mobile clients only receive the ephemeral `session_id` and can only authenticate under their pre-registered `user_id` context. 2. **Encryption** SSL (WSS) is terminated at Nginx. Internal network loops (from Nginx to 47880) run local loopbacks.