3.1 KiB
3.1 KiB
analytics — overview
A stateless multi-tenant API proxy exposing two upstream services under one FastAPI app:
- Google Analytics 4 — Data API (reporting) + Admin API (read).
- Sklik (Seznam) — Drak JSON-RPC API.
The structure mirrors the sibling idoklad / csob services (config→env,
credentials→headers, client per upstream, routers, central exception handling,
Swagger at /docs), adapted to Python/FastAPI.
Design principles
- Stateless / no stored secrets. Credentials arrive per request in
X-headers and are used only to call the upstream. Nothing is persisted; the only in-memory state is a short-lived GA access-token cache (see below). - Thin passthrough. GA request/response bodies and most Sklik calls are forwarded as-is, so callers keep the full upstream API surface. Only authentication, base URL and error mapping are added.
- No silent failures. Every error is logged (never the secret values) and surfaced as JSON. Upstream errors preserve the upstream status and body.
Layout
app/
config.py env-driven config (base URLs, scope, timeout) — no secrets
logging_config.py get_logger(); secrets are never logged
errors.py MissingCredentialsError, UpstreamError + handlers
credentials.py X- header dependencies (GA + Sklik)
clients/
ga_client.py GA Data/Admin HTTP client + service-account token minting
sklik_client.py Sklik JSON-RPC client (login + session + report paging)
routers/
meta.py /health, /version
ga_data.py /ga/data/...
ga_admin.py /ga/admin/...
sklik.py /sklik/...
main.py app factory, root_path, router + handler registration
Reverse proxy
ROOT_PATH (e.g. /apps/analytics) is passed to FastAPI's root_path, so the
OpenAPI servers entry and Swagger "Try it out" use the public prefix. Internal
routes are unprefixed (Caddy handle_path strips the prefix).
Authentication summary
| Upstream | Header(s) | Behaviour |
|---|---|---|
| Google Analytics | X-GA-Access-Token or X-GA-Credentials (+ X-GA-Quota-Project) |
Token used directly; else a token is minted from the base64 service-account JSON (scope analytics.readonly) and cached in memory until ~60 s before expiry. |
| Sklik | X-Sklik-Token (+ X-Sklik-User-Id) |
client.loginByToken per request → session injected into the call. |
Deliberately not wired
- GA Admin write operations (create/update/delete properties, streams). The
requested scope is read-only (
analytics.readonly); addanalytics.editand endpoints if management is needed later. - Sklik header-credential encryption. Same deferral as
idoklad/csob: header values are plaintext over TLS for now. - Sklik session reuse across requests — the chosen model logs in per
request; a future
X-Sklik-Sessionpassthrough could save the login call.
Verification checklist (per AGENTS.md)
/healthreturns 200./docsloads;/openapi.jsonserverscontains the proxy prefix.- New endpoints appear in Swagger with their
X-headers in "Try it out". - Secrets never appear in logs or source.