From b0c78d8dcac55e49a91030222b43859025299b1e Mon Sep 17 00:00:00 2001 From: lobo Date: Sat, 26 Sep 2026 07:37:44 +0200 Subject: [PATCH] docs(readme): add the architecture overview --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index cc76fd1..8ba200c 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,33 @@ To Be Played is split into three repositories: and security headers - CI: Forgejo Actions (`npm ci`, `npm test`, `npm run build`, Docker image) +## Architecture + +```mermaid +flowchart LR + Browser["Browser"] --> Proxy["Traefik (TLS)"] + Proxy --> Frontend["ToBePlayed-Frontend
nginx, static files"] + Proxy --> Auth["ToBePlayed-Auth"] + Proxy --> Core["ToBePlayed-Core"] + Core -->|"JWKS"| Auth + Auth --> Db[("PostgreSQL")] + Core --> Db + Core --> Steam["Steam Web API"] + Core --> Igdb["IGDB API"] + Core -->|"OAuth token"| Twitch["Twitch"] + Browser -.->|"game art"| Images["Steam and IGDB image hosts"] +``` + +- nginx serves the built app. It has no backend of its own: the browser calls Auth and Core directly + (`VITE_API_URL`, `VITE_CORE_API_URL`). The Content Security Policy allows only these two hosts and the image hosts. +- Auth and Core each have their own database. Core verifies Auth's tokens with Auth's public keys (JWKS). +- Calls for the logged-in user go through `src/utils/api.js`. `clientFetch` sends the HttpOnly cookies and the + `X-TBP-Client` header. `apiFetch` answers a `401` with one `POST /api/auth/refresh` and sends the request again. + Parallel requests and other tabs share this refresh (`navigator.locks`). When the second answer is still a + `401`, the page calls `useSessionExpired`, which opens the login page. Public pages (landing page, public + profile) use plain `fetch`. +- `useLibrarySync` polls Core's `/api/library/sync-status` while a Steam sync runs, then loads the data again. + ## Project structure ```text