imn@dev:~/oss/spidey-shelf$ cat scheme.md
friend's phone
types the number on the box
next.js on vercel
fts + pg_trgm · server components
catalog ×247
reference_figures
owned shelf
verdict view
price cache
nightly cron
// deploy: Vercel (fra1) · Railway Postgres · one nightly cron at 06:00 UTC
imn@dev:~/oss/spidey-shelf$ cat services.md
- Vercel
- hosts the next.js app, the image cdn, the admin server actions, and the one 06:00 cron that refreshes prices.
- Railway Postgres
- the single database — catalog, collection, price cache, plus a hand-written tsvector and pg_trgm search layer drizzle-kit cannot generate.
- UploadThing
- stores owner-uploaded box art, normalized to 800x800 webp in the browser; its cdn host is pinned exactly, never wildcarded.
- UPCitemdb
- keyless 100-a-day barcode fallback for a scan the catalog misses — exactly one attempt, 5s timeout, no retries.
- eBay Browse API
- optional market-signal pricing — median, min, listing count — cached in postgres; without credentials the panel renders and calls nothing.
- Nominatim
- geocodes a newly seen acquisition city exactly once, inside the server action that saves the sighting, never at render time.
- GitHub Actions
- lint, prettier, typecheck, vitest and a full next build on every pr — with no DATABASE_URL, which is why db routes are force-dynamic.
imn@dev:~/oss/spidey-shelf$ cat stack.md
imn@dev:~/oss/spidey-shelf$ cat highlights.md
- SEARCH
- digits take an exact pop-number match across the whole catalog, not just the owned half; text goes through websearch_to_tsquery OR'd with pg_trgm similarity. owned rows always sort first, so a 60-row limit can never hide the gift answer.
- SCAN
- the catalog shipped with zero barcodes, so a scan is an enrichment write rather than a lookup: miss → one upcitemdb call → a fuzzy match against the catalog → the owner confirms → the code lands on that row. the second scan of the same box is free.
- BUDGET
- external quotas are treated as load-bearing constraints. upcitemdb gets one attempt behind a 5s abort signal, and all bulk pricing is routed through a single nightly cron so every visitor-facing page only ever reads the cache.
- AUTH
- src/proxy.ts does an optimistic cookie redirect and says outright in its own comments that it is not auth, citing CVE-2025-29927. requireAdmin() re-verifies the jose-signed session inside every admin page, server action and route handler.
- MAP
- the sightings map is 27kb of vendored natural earth land data drawn through a hand-written equirectangular projection with antimeridian splitting — no map library, no tile server, no runtime dependency at all.
- TESTS
- around 60 vitest files run with zero database access: pure logic (slugging, search parsing, verdicts, stats math, upc check digits, map geometry) plus a mocked-db seam for the server actions.