Spaces:
Running
Running
| # WhatsApp Service Gateway | |
| The main FastAPI application (port **7860**) is the **public API gateway**. It | |
| proxies every request under `/api/whatsapp/*` to the internal WhatsApp service | |
| (a Go/whatsmeow application), which is not exposed publicly. | |
| ```text | |
| Client | |
| | HTTP | |
| v | |
| Main FastAPI :7860 | |
| | /api/whatsapp/... | |
| v | |
| WhatsApp Service (internal, e.g. http://localhost:8231) | |
| | | |
| v | |
| Client | |
| ``` | |
| ## Route mapping | |
| The gateway forwards the **full path, query string, HTTP method and body** | |
| unchanged. All routes require the main application's API key | |
| (`Authorization: Bearer <API_KEY>`). | |
| | Client request | WhatsApp service call | | |
| |---|---| | |
| | `GET /api/whatsapp/server/ok` | `GET /server/ok` (health) | | |
| | `POST /api/whatsapp/instance/create` | `POST /instance/create` (admin) | | |
| | `GET /api/whatsapp/instance/status` | `GET /instance/status` | | |
| | `POST /api/whatsapp/send/text` | `POST /send/text` | | |
| | `DELETE /api/whatsapp/instance/delete/:id` | `DELETE /instance/delete/:id` | | |
| No path transformation is applied: after the `/api/whatsapp` prefix is stripped, | |
| the rest of the path is appended to `WHATSAPP_SERVICE_URL`. | |
| ## Deployment model | |
| Single Docker container (Hugging Face Spaces style): the Go binary is compiled | |
| in a multi-stage `whatsapp-build` stage and embedded in the image at | |
| `/app/whatsapp-service/server`. `start.sh` launches it as a sibling process and | |
| `uvicorn` is the primary process. The gateway reaches it over loopback | |
| (`http://127.0.0.1:8231`). | |
| A crash of the WhatsApp service does **not** take the main app down: the gateway | |
| returns sanitized `502`/`503` responses until it recovers (restart loop every | |
| 5s, degraded mode). | |
| ## Gateway behaviour | |
| - **Auth:** the gateway requires the main app's Bearer API key. The WhatsApp | |
| service's own `apikey` header is forwarded as-is when provided by the client | |
| (per-instance token); otherwise the configured | |
| `WHATSAPP_SERVICE_GLOBAL_API_KEY` is injected (admin routes). | |
| - **Headers:** hop-by-hop headers and the gateway's `Authorization`/`Cookie` are | |
| never forwarded. Response headers are whitelisted — internal | |
| `Server`/`content-encoding`/hop-by-hop headers never reach the client. | |
| - **Errors:** connection failures → `502`; timeouts → `504`; disabled service → | |
| `503`. Upstream 4xx/5xx responses are passed through unchanged. Error bodies | |
| never contain internal hostnames, URLs, exceptions or stack traces. | |
| - **Payments/streaming:** responses are streamed back to the client (media/QR | |
| downloads are not buffered). | |
| ## Configuration | |
| | Variable | Default | Description | | |
| |---|---|---| | |
| | `WHATSAPP_SERVICE_ENABLED` | `false` | Enable the gateway routes. `start.sh` auto-enables it when the binary exists. | | |
| | `WHATSAPP_SERVICE_URL` | `http://localhost:8231` | Base URL of the internal WhatsApp service. | | |
| | `WHATSAPP_SERVICE_TIMEOUT` | `30` | Read/write/pool timeout for upstream calls (seconds). | | |
| | `WHATSAPP_SERVICE_CONNECT_TIMEOUT` | `5` | Connect timeout for upstream calls (seconds). | | |
| | `WHATSAPP_SERVICE_GLOBAL_API_KEY` | _(empty)_ | The WhatsApp service `GLOBAL_API_KEY`, injected as the default `apikey` header. | | |
| | `WHATSAPP_SERVICE_PORT` | `8231` | Port `start.sh` uses to launch the embedded binary (`SERVER_PORT`). | | |
| | `WHATSAPP_SERVICE_BINARY` | `/app/whatsapp-service/server` | Path to the embedded binary. | | |
| ### WhatsApp service env vars (centralized in the main app) | |
| There is exactly **one** configuration source: the main application's `.env` | |
| (or the deployment platform's environment). The Go service **never reads a | |
| `.env` of its own** — `start.sh` / the Docker image inject its variables as | |
| process environment, and `--dev` only optionally loads a local `.env` if one | |
| exists. Full reference: the WhatsApp section of `.env.example`. | |
| Values shared with the main app use the same names (`SUPABASE_URL`, | |
| `REDIS_URL`, ...). A few names are mapped automatically by `start.sh` so you | |
| only configure one value: | |
| | Centralized setting | Injected into Go service as | | |
| |---|---| | |
| | `SUPABASE_SERVICE_ROLE_KEY` | `SUPABASE_SERVICE_KEY` | | |
| | `WHATSAPP_SERVICE_GLOBAL_API_KEY` | `GLOBAL_API_KEY` | | |
| | `WHATSAPP_SERVICE_PORT` | `SERVER_PORT` | | |
| Other Go-service settings configured centrally: `SUPABASE_DB_URL`, | |
| `DATABASE_SAVE_MESSAGES`, `CLIENT_NAME`, `CONNECT_ON_STARTUP`, `DEBUG_ENABLED`, | |
| `LOG_TYPE`, `WEBHOOK_FILES`, `OS_NAME`, `WHATSAPP_VERSION_*`, `MINIO_*`, | |
| `AMQP_*`, `NATS_*`, `WEBHOOK_URL`, `PROXY_*`, `API_AUDIO_CONVERTER*`, | |
| `EVENT_IGNORE_*`, `QRCODE_MAX_COUNT`, `CHECK_USER_EXISTS`, `LOG_*`. | |
| > Configure `WHATSAPP_SERVICE_GLOBAL_API_KEY` (equivalently `GLOBAL_API_KEY`) | |
| > once — it feeds both the gateway's default `apikey` header and the Go | |
| > service's authentication. | |
| ## Health checks | |
| The main app's `GET /health` now includes a non-fatal `whatsapp_service` block: | |
| ```json | |
| {"configured": true, "reachable": true, "status": "ok"} | |
| ``` | |
| The WhatsApp service's own liveness endpoint is exposed through the gateway at | |
| `GET /api/whatsapp/server/ok`. | |
| ## Testing | |
| ```bash | |
| python -m pytest tests/test_whatsapp_gateway.py -v | |
| ``` | |
| Covers: gateway auth, forwarding (method/path/query/body/headers), apikey | |
| injection vs pass-through, upstream 4xx/5xx passthrough, 502/503/504 handling, | |
| response-header sanitization and the `/health` integration. | |
| A full stack test: | |
| ```bash | |
| docker build -t agentdeck-backend:test . | |
| # run with the WhatsApp env vars above + WHATSAPP_SERVICE_ENABLED=true | |
| ``` | |