add: persistant storage of elo ranking
All checks were successful
Deploy on push / deploy (push) Has been skipped

This commit is contained in:
2026-03-22 21:18:49 -07:00
parent 614a3d1eff
commit 9f60ab3cca
7 changed files with 643 additions and 62 deletions

View File

@@ -2,7 +2,7 @@
Static photo gallery for logging meals and food memories.
The site is based on the HTML5 UP Lens template and currently ships as a plain static site: HTML, CSS, JavaScript, and local image assets.
The site is based on the HTML5 UP Lens template. The front-end remains a mostly static HTML/CSS/JavaScript site, and the local Node server now also exposes a tiny rankings sync API for shared Elo persistence.
## Repo Layout
@@ -19,8 +19,9 @@ The site is based on the HTML5 UP Lens template and currently ships as a plain s
- `scripts/check.js`: validates data, image assets, and generated pages
- `scripts/generate-thumbnails.js`: regenerates thumbnails from the full-size images
- `scripts/ingest-meal.js`: ingests a new meal image and metadata in one command
- `scripts/serve.js`: serves the generated site locally with a small static file server
- `scripts/serve.js`: serves the generated site and the rankings sync API
- `scripts/lib/elo.js`: validates and syncs Elo data against the meal list
- `scripts/lib/rankings-state.js`: normalizes and persists the shared rankings state
- `package.json`: minimal Node build entrypoint
## Run Locally
@@ -45,6 +46,9 @@ npm run serve
Then open `http://127.0.0.1:4321`.
By default, rankings sync state is written to `.runtime/rankings-state.json`.
Override that path with `RANKINGS_STATE_PATH=/absolute/path/to/rankings-state.json`.
If you want a single command that builds and serves, run:
```sh
@@ -97,9 +101,31 @@ npm run build:thumbs:force
`data/elo.json` stores the seed rating, Elo `kFactor`, and a win-loss record for each meal.
The page build keeps this file aligned with `data/meals.json`, so new meals automatically appear in `rankings.html` with the default seed rating.
The interactive voting flow on `rankings.html` uses browser `localStorage` for persistence.
That means Elo votes persist across reloads on the same browser and device, but they do not sync automatically across devices.
Use the reset button on the rankings page if you want to clear the local vote history and go back to the seeded board.
The interactive voting flow on `rankings.html` now prefers the same-origin API exposed by `scripts/serve.js`:
- `GET /api/rankings`: load the shared rankings state
- `POST /api/rankings/vote`: apply one head-to-head result on the server
- `POST /api/rankings/reset`: reset the shared board back to the seeded state
The server persists the shared board to `.runtime/rankings-state.json` by default, or to `RANKINGS_STATE_PATH` if you set it.
That makes rankings persist across reloads, sessions, browsers, and devices as long as they are hitting the same deployed site.
If the API is unavailable, the page falls back to browser `localStorage`.
In that fallback mode, votes still persist across reloads in the same browser profile, but they do not sync across browsers or devices.
Use the reset button on the rankings page if you want to clear the current saved board and go back to the seeded state.
## Deployment Notes
For Docker/VPS deployment, mount a persistent volume and point `RANKINGS_STATE_PATH` at it so rankings survive container rebuilds and restarts.
Example:
```sh
RANKINGS_STATE_PATH=/data/rankings-state.json npm start
```
In a containerized setup, mount `/data` as a named volume or bind mount.
If you reverse-proxy the app through Caddy on the same domain, the rankings page will use the shared API automatically with no extra CORS setup.
## Image Conventions
@@ -131,7 +157,3 @@ For images that should crop away from the center, add optional thumbnail focus m
```
The `x` and `y` values are normalized from `0` to `1`, where `0.5, 0.5` is the center of the image.
## Planned Features
1. Optional shared sync or export/import for rankings if browser-local persistence becomes too limiting.