# To Be Played – Core Service Business service of **To Be Played**, a Steam backlog tracker. It syncs the user's Steam library, stores the backlog (status, rating, notes, priority), tracks playtime per day, syncs the wishlist and adds time-to-beat data from IGDB. To Be Played is split into three repositories: | Repository | Role | |---|---| | ToBePlayed-Auth | Users, login, settings, signs JWTs | | **ToBePlayed-Core** (this one) | Steam library, backlog, wishlist, stats; verifies JWTs | | ToBePlayed-Frontend | React single-page app | ## Features - **Steam sync**: owned games, playtime and achievements through the Steam Web API. Manual sync plus a nightly sync. Resilience4j retries and rate-limits every Steam call; Caffeine caches game counts and player summaries. - **Backlog**: status (`BACKLOG`, `PLAYING`, `FINISHED`, `COMPLETED` = 100 %, `DROPPED`), rating 1–5, notes, priority, filters and sorting. "Pile of Shame" summary with a random suggestion of what to play. - **Playtime history**: one row per game and day, for the activity view and the "most played in the last two weeks" dashboard banner. - **Wishlist**: synced from Steam with store price and discount. - **Time to beat**: story, extras and completionist hours from the IGDB API. Optional: without credentials the feature is off and the rest works. - **Public profile data**: totals, status counts, games in progress and finished games with rating. Readable only with a profile ticket issued by Auth. Never returns notes, priority or the Steam ID. ## Authentication Core never issues tokens. It verifies Auth's EdDSA (Ed25519) JWTs with the public key only (`JWT_PUBLIC_KEY`) and reads `userId` and `steamId` from the claims. A token with a `typ` claim (for example a profile ticket) is rejected on normal endpoints; the profile ticket is accepted only by `GET /api/public/profiles/library` in the `X-Profile-Ticket` header. ## API | Method | Path | Auth | |---|---|---| | `GET` | `/api/library/games`, `/api/library/games/{appid}` | JWT | | `PUT` | `/api/library/games/{appid}/backlog` | JWT | | `POST` | `/api/library/sync` | JWT | | `GET` | `/api/library/sync-status`, `/summary`, `/activity`, `/wishlist`, `/steam-profile`, `/games-count` | JWT | | `GET` | `/api/public/profiles/library` | profile ticket | | `GET` | `/api/stats/public` | public | ## Tech stack - Java 25, Spring Boot 4.1 (Web MVC, Security, Data JPA, Validation, Cache with Caffeine), virtual threads - PostgreSQL 17, Flyway migrations - Resilience4j: retry and rate limiter for Steam, rate limiter for IGDB - JJWT with Ed25519 public key - Spring Boot Actuator + Micrometer (Prometheus) - Tests: JUnit 5, Testcontainers (PostgreSQL), ArchUnit - CI: Forgejo Actions, multi-stage Docker image ## Architecture A modular monolith. Each module under `core_service.modules` is a vertical slice that could move to its own service later: | Module | Content | |---|---| | `library` | Sync, backlog, playtime history, wishlist, public profile data | | `stats` | Public numbers for the landing page | | `steam_adapter` | Steam Web API client | | `igdb_adapter` | IGDB client (Twitch OAuth) | Inside a module: `api` (controllers, DTOs), `service` (business logic), `domain` (JPA entities) and `repo` (Spring Data repositories). ArchUnit enforces cycle freedom, controller-to-repository direction, naming and module isolation in `src/test/java/core_service/architecture/ArchitectureTest.java`. ## Run locally Needs Java 25 and Docker. ```bash ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev ``` Spring Boot's Docker Compose support starts PostgreSQL from `compose.yaml`, Flyway applies the migrations, and the service listens on `http://localhost:8090`. Health: `/actuator/health`, metrics: `/actuator/prometheus`. In the `dev` profile a request without an `Authorization` header gets a dummy user, so the API is usable without a running Auth service. Configuration comes from environment variables: | Variable | Purpose | |---|---| | `JWT_PUBLIC_KEY` | Auth's Ed25519 public key, Base64 of the X.509 DER bytes | | `STEAM_API_KEY` | Steam Web API key | | `IGDB_CLIENT_ID`, `IGDB_CLIENT_SECRET` | Twitch app for IGDB (optional) | | `DB_PASSWORD` | Local PostgreSQL password | ## Tests ```bash ./mvnw clean verify ``` Runs unit, WebMvc, persistence and integration tests plus the ArchUnit rules. Needs Docker for Testcontainers. ## Roadmap - "What to play next" picker: time budget and mood on top of the current random suggestion. - Achievement summary per game and library-wide, "closest to 100 %". - Sale alerts for wishlist games. - Move `steam_adapter` DTOs out of `api`, then turn on strict module independence in ArchUnit. - Fetch the public key from a JWKS endpoint in Auth instead of an environment variable. ## License All rights reserved. The source is public for reading only.