Lists

2 min read

Add entry ordering, completion, pinning and local JWT verification, then trim over-engineering

Summary

Added manual ordering of entries with up/down controls, a completed state that greys out and strikes through an entry and sorts it after the open ones, and pinning of up to five top-level lists to the home page. Replaced per-request token checks against Goauth with in-process HS256 verification using the shared JWT secret, and made air restart on .env changes. Ran an over-engineering audit and applied all eight findings, removing the Goauth fallback verifier, the separate healthcheck binary, hand-rolled cookie rewriting and a duplicated edit-in-place field, for a net cut of 112 lines. Backend behaviour was tested against a stand-in auth server and the Docker image was built and reported healthy; frontend changes were build-checked but not exercised in a browser, and acceptance of a real Goauth-issued token was not confirmed. Also seeded ten sample lists into the dev database.

Decisions

  • Rejected a Redis cache: list reads took about 0.04 ms in Postgres, while the slow hop was the round trip to Goauth
  • Verified access tokens locally with the Goauth JWT secret and made JWT_SECRET required, dropping the /auth/me fallback
  • Stored entry order as an integer position set by sending the full ordered id list for a list
  • Stored completion as completed_at and sorted completed entries last while keeping their position, so reopening restores the original place
  • Stored pins as pinned_at on top-level lists only, capped at five per user in the backend, and excluded pinned lists from recently edited
  • Put complete, move and delete actions in the per-entry menu instead of showing a checkbox on every row
  • Kept the real secret only in the gitignored backend/.env and left placeholders in the committed example file

Learnings

  • A migration that re-runs on every start can add a NOT NULL column idempotently by adding it nullable, backfilling WHERE it IS NULL, then setting the default and NOT NULL
  • air ignores .env unless it is listed in include_file, and a bare touch does not trigger a rebuild; a content write does
  • React StrictMode double-mounts components in development, so data-loading effects fire twice there but not in production builds
  • pkill -f ‘^air$’ and docker container prune are broader than a test cleanup needs; the first stopped the user’s own dev server
  • A generic decode helper and an embed.FS migration loader made the code more uniform but not shorter