Initial commit: API gateway with admin console

- FastAPI async gateway with httpx proxying to multiple upstreams
- SQLite database with SQLAlchemy ORM
- Admin console: manage services, users, API keys, endpoint access
- Per-key, per-endpoint granular access control
- OpenAPI document sync and caching (5-minute TTL)
- Request/response logging with full transaction inspection
- In-memory rate limiting (per-key, fixed-window)
- Tiered log retention (7d payloads, 90d rows, incremental vacuum)
- TLS verification toggle per service (for self-signed certificates)
- Service connectivity validation with automatic endpoint refresh
- Request browser with filters and deep-link inspection
- Docker setup with persistent volume
- Modal forms for create/edit flows

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Samuel Amar
2026-07-29 14:00:22 +02:00
co-authored by Claude Haiku 4.5
commit 77d7a50fa9
31 changed files with 2927 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
# API Gateway
A FastAPI-based gateway that exposes a single entrypoint for multiple upstream APIs,
with a built-in web management console.
## Features
- **Single entrypoint** — consumers call `/<service-slug>/<path>` and the gateway
proxies to the registered upstream (all HTTP methods, query strings, bodies,
headers). `/admin`, `/static`, `/docs` and `/health` are reserved.
- **Endpoint catalog, auto-synced** — each service's callable endpoints are
mirrored from its OpenAPI document (`/openapi.json`, `/swagger.json` or
`/api-docs`) whenever the API Keys page loads: new operations appear, removed
ones are deleted (their grants with them). Services without a reachable spec
keep their existing catalog. Patterns support `{param}` (one segment), `*`
(within a segment) and `**` (any depth).
- **Per-key access rights, hierarchical picker** — every API key is granted a
specific set of endpoints, chosen in a path tree (service → path segments →
operations) where a parent checkbox selects everything beneath it. A request
must match a listed endpoint AND the key must hold a grant on it; otherwise
404 (`unknown_endpoint`) or 403 (`access_denied`). Keys are hashed at rest,
shown once at creation, revocable, with per-key rate limits.
- **Web management console** (`/admin`) — manage connected APIs, users, and API
keys; usage dashboards with charts. Every console user has full management
access.
- **Transaction capture & request browser** — every gateway request is recorded
with service, endpoint, key, status, latency, client IP, query string, and
the request/response payloads (textual bodies up to 64 KB; binary summarized).
The Requests page filters by service / user / key / status class / path and
links to a per-request detail view showing the complete transaction.
- **Usage monitoring** — charts: traffic and average latency over time (adaptive
buckets: 5 min / 1 h / 1 day), volume by service / user / endpoint, status
codes, latency by service, top keys — filterable by time range (1 h 30 d),
service, user and key.
- **Retention** — a background purge runs every 6 hours: request/response
payloads are blanked after 7 days (the log row stays inspectable), whole log
rows are deleted after 90 days, and freed pages are returned to the
filesystem via incremental vacuum. Both windows are configurable; `0`
disables that tier.
## Quick start
```bash
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000
```
Open http://localhost:8000/admin — first startup seeds an admin account
(`admin` / `admin` by default — **change it immediately**).
## Docker
```bash
docker compose up -d
```
The console is served on http://localhost:8000/admin; the SQLite database
persists in the `gateway-data` volume. Set `GATEWAY_SECRET_KEY` (and the admin
credentials) in `docker-compose.yml` before deploying anywhere real. The image
runs a single uvicorn worker on purpose — the rate limiter and the OpenAPI
sync cache are in-memory.
## Configuration (environment variables)
| Variable | Default | Purpose |
|---|---|---|
| `GATEWAY_DATABASE_URL` | `sqlite:///./gateway.db` | SQLAlchemy database URL |
| `GATEWAY_SECRET_KEY` | dev value | Session cookie signing key — set in production |
| `GATEWAY_ADMIN_USER` / `GATEWAY_ADMIN_PASSWORD` | `admin` / `admin` | Seeded admin credentials |
| `GATEWAY_PAYLOAD_RETENTION_DAYS` | `7` | Blank stored payloads after N days (0 = keep forever) |
| `GATEWAY_LOG_RETENTION_DAYS` | `90` | Delete log rows after N days (0 = keep forever) |
## Using the gateway as a consumer
1. Register a service (e.g. slug `weather``https://api.example.com/v1`) and
list its endpoints (import from OpenAPI or add manually).
2. Issue an API key and tick the endpoints it may call.
3. The consumer calls:
```bash
curl -H "X-API-Key: gw_..." "http://localhost:8000/weather/forecast?city=Paris"
```
The gateway authenticates the key, matches the request against the service's
endpoint list, checks the key's grant and the rate limit, proxies the request to
`https://api.example.com/v1/forecast?city=Paris`, logs it, and returns the
upstream response. Gateway-generated errors are JSON with an `error` code:
`missing_api_key` (401), `invalid_api_key` / `access_denied` (403),
`unknown_service` / `unknown_endpoint` (404), `rate_limited` (429),
`upstream_unreachable` (502), `upstream_timeout` (504).
## Notes & limits
- The stats queries use SQLite date functions; if you point `GATEWAY_DATABASE_URL`
at Postgres/MySQL, adapt `app/admin/stats.py` (`strftime`, `iif`).
- The rate limiter is in-memory (single process). Run one worker, or swap in Redis
for multi-worker deployments.
- Streaming responses are buffered (fine for JSON APIs; not for large file proxying).