Samuel AmarandClaude Fable 5 b90ecaf61c Add two-level tag hierarchy to the docs portal
Operations keep their upstream tags as the second grouping level, and
each service becomes an x-tagGroups entry (first level). Tag names
shared by several services are disambiguated with the service name.
The intro renders one section per service with the upstream spec's
title and info.description (the admin-entered description overrides).

The /docs renderer switches from Swagger UI to Scalar, which renders
x-tagGroups as a nested sidebar, keeps try-it-out, and gets the API
key pre-filled into its authentication panel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 12:27:32 +02:00

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.

  • Consumer docsGET /openapi.json (authenticated with X-API-Key) returns a merged OpenAPI document scoped to that key: every operation the key holds a grant on, across all services, rewritten to the gateway's routes. GET /docs serves a Swagger UI portal around it — paste a key, browse and try exactly what that key can call.

  • 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

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

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 weatherhttps://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:
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).
S
Description
No description provided
Readme
123 KiB
Languages
Python 59.3%
HTML 32.5%
CSS 7.7%
Dockerfile 0.5%