Files
games-scores/.gitignore
T
jcoffey-dev f31e3b0e12 One leaderboard for every game on the site
The board started inside the lemonade stand because when it was written there
was one game. games.jcoffey.dev already treated it as shared -- the compose
file calls it "the shared leaderboard" and nginx proxies it at a site-wide
/api/ rather than under one game's path -- but it was still one game's code,
and a second game would have meant a second container, a second volume and a
second thing to back up for every game after that.

It is its own repository rather than living in either game, because a shared
board belongs to no game. Putting it in the lemonade stand would have made
every future game depend on the lemonade stand for its leaderboard, and the
games repository's own rule is that what belongs to a game lives in that
game's repository. This belongs to none of them.

AGPL for the same reason the games are: section 13 is about network services,
and this is the network service.

One table, and it does not know what a glass of lemonade is:

  scores(id, game, name, m1, m2, m3, extra, created_at)

Three ranked integer columns and a JSON bag, because every board here is sort
by a couple of numbers and show a couple more. The registry decides what m1
means for a given game, so adding a game is one file in src/games/ and an
import -- never a migration.

The generic half is deliberately generic and the knowledge deliberately is
not. Generic storage with generic validation would be a wall anybody can write
anything on. Each game says what is impossible in its own terms: a stand that
earned more than its trading days allow, a hunter carrying six arrows when
nobody starts with more than five, a hunter who ran out of arrows and kept
some. That is the most a board with no accounts has ever been able to offer,
and the README says so rather than implying more.

The migration keeps the old table. The volume this mounts holds the only copy
of a board real people are on, so on first boot against the old shape the rows
are copied across and the original is renamed to scores_lemonade_v1 rather
than dropped. A few kilobytes is the difference between a bad migration being
an afternoon and being an apology. It runs in one transaction, it is
idempotent, and it says what it did in the log.

A request with no game is treated as lemonade. That is a compatibility shim
rather than a default worth keeping: the deployed lemonade bundle posts no
game at all and copies of it are sitting in browsers. It goes once that bundle
has been rebuilt long enough ago to be forgotten.

18 tests, including the migration run against a database built to the old
schema column for column -- checking the thing that matters, which is that the
board comes back out ranked the way it went in.
2026-09-08 22:32:47 -07:00

6 lines
41 B
Plaintext

node_modules
data
*.db
*.db-shm
*.db-wal