Spaces:
Running
Running
File size: 5,396 Bytes
83d6851 6cd5bf4 83d6851 6cd5bf4 83d6851 6cd5bf4 83d6851 6cd5bf4 83d6851 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # 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
```
|