No description
  • Kotlin 72.6%
  • Svelte 16.8%
  • TypeScript 9.1%
  • HTML 0.6%
  • JavaScript 0.4%
  • Other 0.4%
Find a file
2026-06-09 00:03:20 +02:00
.forgejo/workflows ci(backend): pin upload-artifact to v3 for Forgejo compatibility 2026-05-09 12:48:57 +02:00
backend Merge pull request 'chore(deps): update maven:3-eclipse-temurin-25 docker digest to 01ef98a' (#31) from renovate/maven-3-eclipse-temurin-25 into develop 2026-06-09 00:03:20 +02:00
deploy feat(infra): add flaresolverr service to the compose stack 2026-05-09 13:24:01 +02:00
docs first commit 2026-04-24 21:49:35 +02:00
frontend Merge pull request 'chore(deps): update tailwind to ^4.3.0' (#29) from renovate/tailwind into develop 2026-06-09 00:03:19 +02:00
review-findings docs(review): log second-sweep R-findings (2026-05-09) in planning summary 2026-05-09 08:57:58 +02:00
.gitignore build(backend): scaffold Spring Boot + Kotlin skeleton 2026-04-24 22:06:43 +02:00
CLAUDE.md docs(claude): commit CLAUDE.md so contributors share the same project guide 2026-05-09 12:51:09 +02:00
CLAUDE_CODE_PROMPT.md chore(backend): rename root package nl.nimi → nl.midasvo 2026-04-25 17:42:39 +02:00
docker-compose.yml chore(deps): update postgres:16-alpine docker digest to 16bc17c 2026-05-16 22:01:33 +00:00
PLAN.md chore(backend): rename root package nl.nimi → nl.midasvo 2026-04-25 17:42:39 +02:00
README.md feat(api,frontend): browse a benefit catalog like you'd browse a tier 2026-04-25 18:29:17 +02:00
renovate.json chore(renovate): block major Postgres bumps until we have an upgrade path 2026-05-10 09:18:05 +02:00

gptiers

If I pay for Ultimate instead of Premium, what games do I actually get?

A self-hosted web app that answers exactly that question. Compares Xbox Game Pass tiers (Essential / Premium / Ultimate / PC Game Pass) against the live catalogs at catalog.gamepass.com and displaycatalog.mp.microsoft.com, keeps a history per (game, tier, market), and exposes the diff via REST and a Svelte UI.

For 2026-04 NL, the headline answer is: upgrading from Premium to Ultimate unlocks 139 additional games. That's the kind of thing this app exists for.

Quickstart (local)

You need Docker, Java 21+, Node 20+, Maven 3.9+.

# 1. Postgres
docker run -d --name gptiers-pg \
  -e POSTGRES_PASSWORD=devpw -e POSTGRES_USER=gptiers -e POSTGRES_DB=gptiers \
  -p 127.0.0.1:5433:5432 postgres:16-alpine

# 2. Backend
DB_URL=jdbc:postgresql://localhost:5433/gptiers \
DB_USERNAME=gptiers DB_PASSWORD=devpw \
GPTIERS_ADMIN_USERNAME=admin \
GPTIERS_ADMIN_PASSWORD=local-dev-password-1234 \
mvn -B -f backend/pom.xml spring-boot:run

# 3. Frontend (separate terminal — Vite proxies /api to localhost:8080)
cd frontend && npm install && npm run dev

# 4. Visit http://localhost:5173, navigate to /login, and sign in
#    with username 'admin' + the GPTIERS_ADMIN_PASSWORD you set above.
#    The Admin page has buttons to trigger syncs.

Layout

backend/                 Spring Boot 3.4 + Kotlin 2.1 + Spring Security 6 + JPA + Liquibase
frontend/                Svelte 5 + Vite + Tailwind 4 (SPA, login + admin pages included)
deploy/                  docker-compose.yml runbook + Caddy snippet (see deploy/README.md)
docs/                    Cowork-produced API discovery notes
.forgejo/workflows/      CI: build and push both images on develop / main
docker-compose.yml       Production stack at the repo root
renovate.json            Dependency updater config
PLAN.md                  Phase-by-phase project plan
CLAUDE_CODE_PROMPT.md    Kickoff prompt for the agent that built this

Endpoints

Public read API

GET  /api/tiers                                   tier+benefit catalog with current counts
GET  /api/tiers/{tier}/games                      paginated, filter by category/publisher/first-party
GET  /api/tiers/{tier}/categories                 distinct categories for a tier
GET  /api/tiers/{tier}/publishers                 publisher groups for a tier
GET  /api/benefits/{benefit}/games                same shape as tier games, for EA Play / Ubisoft+
GET  /api/benefits/{benefit}/categories           distinct categories for a benefit
GET  /api/benefits/{benefit}/publishers           publisher groups for a benefit
GET  /api/diff?from=premium&to=ultimate           games in `to` but not in `from`
GET  /api/games/{bigId}                           detail + full membership history
GET  /api/recent?tier=ultimate&days=30            recently added to a tier or benefit (use ?benefit=)
GET  /api/stats/history?tier=ultimate&days=90    snapshot count over time (or ?benefit=)
GET  /api/version                                 git sha + build time of the running image
GET  /actuator/health                             uptime check
GET  /v3/api-docs                                 OpenAPI 3.1 spec
GET  /swagger-ui.html                             Swagger UI

Admin (Spring Security form-login)

POST /admin/login                                 username + password, sets JSESSIONID
POST /admin/logout                                clears the session
GET  /admin/me                                    current authentication
GET  /admin/sync-runs                             paginated audit log
POST /admin/sync?tier=…                           sync one tier
POST /admin/sync/benefit?benefit=…               sync one benefit (unions console + PC)
POST /admin/sync/run                              the full daily job, on demand

Easiest path is the SPA admin page at /admin — sign in at /login with the configured username/password and the page exposes one-click triggers for all of the above plus a recent-runs table. The deploy doc has a curl fallback if you really need it.

Tests

mvn -B -f backend/pom.xml test           # full backend suite (Testcontainers)
cd frontend && npx svelte-check          # frontend type-check

JaCoCo report after mvn test is at backend/target/site/jacoco/index.html.

Deploy

The runbook for putting this on an Oracle VPS behind Caddy is in deploy/README.md. Short version: pull the two images from the Forgejo registry, set .env (DB password + admin password + optional username override), docker compose up -d, append the Caddy snippet (one reverse_proxy line — the frontend nginx handles the /api and /admin split internally), point DNS, done.

Project plan

See PLAN.md for the phase-by-phase plan and the conventions the code follows. All six phases are complete and the stack runs on the VPS; ongoing work is incremental polish on top.