From e0e68bfba83f50531b39c5b5b11e126af3049b19 Mon Sep 17 00:00:00 2001 From: mattie726 Date: Tue, 25 Aug 2026 23:36:05 -0700 Subject: [PATCH] Add CLAUDE.md documenting architecture and deploy pipeline --- CLAUDE.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2ffa037 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# N64 Tracker + +Flask app for tracking a personal N64 game collection: ownership status, +loose cartridge prices (scraped from PriceCharting), rarity percentages +(scraped from RarityGuide), and cover art (LaunchBox games DB, cached +locally as WebP thumbnails). Runs as one Docker container on the user's +Unraid home server. + +## Architecture + +- **`app.py`** — Flask routes for the game list/stats API, session-based + password auth (single shared password, SHA-256 hashed in + `/app/data/settings.json`), and three scrapers: + - `scrape_pricecharting()` — loose cartridge prices, paginated via a + cursor-based form POST loop. + - `scrape_rarityguide()` — rarity % + label, fuzzy-matched to game titles + via `difflib` (handles minor title spelling differences between + sources). + - `scrape_game_info()` — cover art + metadata from LaunchBox's games DB, + only fetched lazily on first view/ownership toggle of a game, not + upfront for the whole collection. + - Thumbnails: `generate_thumbnails()` runs in a background thread at + startup, converting `Coverart/*.png` to 300px-wide WebP in + `/app/data/thumbs` — serving thumbnails instead of full PNGs is what + keeps the game grid fast. + - `BASE_PREFIX = '/n64'` — the app expects to run under this subpath + behind a reverse proxy; all routes are prefixed with it. +- **`static/`** — frontend (vanilla JS/HTML/CSS), served by Flask directly. +- **`games.json`** at the repo root is a **seed/reference copy**, not the + live database — the actual live data is `/app/data/games.json` on the + bind-mounted `data/` volume (gitignored, server-only). +- **`Coverart/`** — ~1,600 PNG cover art images (~1.6GB). Deliberately + **excluded from git** (`.gitignore`) — this is static reference data + that doesn't change, and 1.6GB in git history would make every clone of + this repo download all of it. It lives only on the server; the deploy + workflow explicitly excludes it from the rsync `--delete` so a deploy + never wipes it (see below). + +## Local development + +```bash +pip install -r requirements.txt +python3 app.py +``` + +Runs on port 5000. Needs `Coverart/` populated locally to see cover art +(not in git — copy it from the server if working on cover-art-related +features, or work without it since it degrades gracefully). + +## Deployment + +**Target:** Unraid server, container path `/mnt/user/appdata/n64-tracker`, +built from `Dockerfile`/`docker-compose.yml` in this repo. Container name +`n64-tracker-n64-tracker-1`, host port `8585` → container port `5000`. + +**Deploy mechanism: Forgejo push-to-deploy**, same pattern as the other +three projects on this box — see `card-grader`'s `CLAUDE.md` for the full +pipeline mechanics. Push to `main` on the `forgejo` remote +(`https://git.hippofam.com/ninja_hippo/n64-tracker.git`) triggers +`.forgejo/workflows/deploy.yml`, which syncs into +`/mnt/user/appdata/n64-tracker` (excluding `.git`, `.env`, `data/`, and +**`Coverart/`** — that last exclusion is load-bearing, since `Coverart/` +isn't tracked in git at all and a `--delete` sync without excluding it +would wipe the images on every deploy) and runs `docker compose up -d --build`. + +**Persistent state, never touched by deploys:** `data/` (games.json, +settings.json, thumbnail cache) and `Coverart/` are both server-only. +`.env` holds `ADMIN_PASSWORD`/`SECRET_KEY`. + +**Migration notes (2026-08-25):** +- This container originally used a Docker **named volume** + (`n64-tracker_game-data`) instead of a bind mount, which meant Unraid's + Appdata Backup plugin (only knows about `/mnt/user/appdata`) silently + never backed up the live game database. Migrated to a bind mount + (`./data:/app/data`) with existing data copied over and verified intact + before the named volume was deleted. +- `ADMIN_PASSWORD` and `SECRET_KEY` were previously hardcoded in plain text + directly in `docker-compose.yml`, which would have landed in git history + the moment this repo was created. Moved to a gitignored `.env` file + referenced via `env_file: .env` before this repo was ever initialized — + neither secret is in git history.