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:
@@ -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).
|
||||
Reference in New Issue
Block a user