n64-tracker/CLAUDE.md
mattie726 e0e68bfba8
All checks were successful
Deploy / deploy (push) Successful in 39s
Add CLAUDE.md documenting architecture and deploy pipeline
2026-08-25 23:36:05 -07:00

4 KiB

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

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.