solitaire_engine/assets/themes/classic/ was absent from the container
because only the workspace-root assets/ directory was copied. The
AssetServer serves themes/classic/ from that same root, so the classic
theme manifested as a missing-asset load failure at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename assets/themes/default/ → assets/themes/dark/; update theme.ron
id/name to "dark"/"Dark"
- Rename all DEFAULT_THEME_* constants → DARK_THEME_* and
default_theme_svg_bytes / populate_embedded_default_theme → dark_*
- Add bundled_theme_url() helper for URL resolution without needing the
registry (used by Startup systems where ordering isn't guaranteed)
- Registry now lists Classic first (new player default), Dark second
- settings.rs default_theme_id() returns "classic" so fresh installs
start on the white card theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
White/cream card faces with traditional red (hearts/diamonds) and black
(clubs/spades) colours, plus a navy diamond-pattern card back. Shipped
as a bundled AssetServer theme alongside the existing Default theme.
Registry updated to include the Classic entry; registry tests updated
to reflect the new BUNDLED_COUNT of 2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only game.html had the snippet; the other five pages were missing it,
causing the Matomo installation verification check to fail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/index.php returns 302 after tables are created (installer redirect),
which fails k8s HTTP probes. /matomo.php is the tracker endpoint and
always returns 200 regardless of installation state. Also add
timeoutSeconds: 5 since PHP startup can exceed the 1s default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bitnami/matomo was removed from Docker Hub (0 tags). Switch to the
official matomo:5.10.0 image; update port 8080→80, volume path to
/var/www/html, and env var names to match the official image schema.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Secrets committed in prior commits (matomo-secret.yaml,
secret-analytics-auth.yaml) have been scrubbed from history via
filter-branch — rotate those credentials immediately.
Going forward:
- deploy/*-secret.yaml is gitignored; apply manually with kubectl
- deploy/matomo-secret.yaml.example shows the required shape
- ArgoCD ignoreDifferences on matomo-secret prevents it pruning a
manually-applied secret
- Remove matomo-secret.yaml from kustomization.yaml so ArgoCD never
manages it again
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
check_auto_complete no longer requires the waste pile to be empty —
only the stock must be exhausted and all tableau cards face-up.
next_auto_complete_move checks the waste top card before scanning
tableau, and auto_complete_step falls back to draw() when no direct
foundation move is available so the waste drains automatically.
Fixes the end-game state where the player could see a clear win but
the auto-complete interval never fired because the waste was non-empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the hand-rolled analytics endpoint and SQLite event table in favour
of Matomo — a self-hosted, full-featured analytics platform.
k8s:
- Deploy MariaDB 11 + Bitnami Matomo 5 in the solitaire namespace
- Route analytics.aleshym.co ingress to the Matomo service
- Remove Datasette sidecar and its BasicAuth middleware/secret
- Remove the analytics port from the solitaire-server Service
Rust:
- Replace AnalyticsClient (custom HTTP endpoint) with MatomoClient (Matomo
HTTP Tracking API bulk endpoint); maps game events to Matomo categories
- Add matomo_url + matomo_site_id fields to Settings (serde default → None/1)
- Privacy toggle in Settings now activates when matomo_url is set (not tied
to SyncBackend::SolitaireServer)
- Remove POST /api/analytics route from solitaire_server
Web:
- Add Matomo JS tracking snippet to game.html (/play page)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Datasette container alongside the existing server in the same pod so
it can read the SQLite PVC without a second ReadWriteOnce mount. Protected
by a Traefik BasicAuth middleware at analytics.aleshym.co.
Also fixes the ArgoCD repoURL to point to the migrated Gitea hostname
(git.aleshym.co) instead of the old bare IP.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Content-Security-Policy, X-Content-Type-Options, and X-Frame-Options are
now injected by a single Axum middleware on the web router subtree, so
all HTML pages get consistent headers without touching each file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- leaderboard.html, replays.html: escape user-supplied display_name and
username before inserting into innerHTML to prevent stored XSS
- game.js: call POST /api/replays on win so browser-game completions are
recorded; scores were never submitted before this fix
- replays.rs: after replay insert, upsert leaderboard best_score /
best_time_secs for opted-in users when the new score beats their current
best (classic mode only); scores were never updated before this fix
- leaderboard.rs: add LIMIT 100 to GET /api/leaderboard to prevent
unbounded query growth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a card flipped face-up, the browser fetched the PNG on demand,
showing the cream fallback colour until the image arrived. Preloading
all 52 faces and the back at module load ensures they are cached before
any flip can occur.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add account.html: tabbed form for login and registration, signed-in
state with sign-out, links to leaderboard and replays
- Wire /account route in build_router_inner
- Add Account card to landing page
- Link leaderboard login prompt to /account for new users
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all display-name occurrences across web pages, Rust source,
docs, and Cargo metadata. Update localStorage token key from sq_token
to fs_token. Tagline "Klondike Solitaire" retained as genre descriptor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add leaderboard.html: JWT login form + localStorage token + table
- Add replays.html: public listing of recent replays, row click to viewer
- Wire /leaderboard and /replays routes in build_router_inner
- Fix home.html Recent Replays link from /api/replays/recent to /replays
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two runs for the same SHA racing to push the kustomization update
caused the second to fail with "failed to push some refs".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>