Commit graph

21 commits

Author SHA1 Message Date
Randall Stillwell
1cc2e28423 Migrate Deck Hearth off Vercel/Neon to homelab Dokploy stack.
Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object
storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy,
homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the
homelab URL instead of Vercel previews.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 09:32:13 -05:00
varutasu
8f09ed1ef6
feat(scanner): Layer-0 visual catalog search (Phase 3) (#160)
Add pgvector embeddings on cards, server-side cohere/embed-v4.0 via AI
Gateway, kNN identify route, and L0→L1→L2 client orchestration with
empty-index fast escalate and id-cursor backfill job.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:46:39 -05:00
varutasu
c52891a6b2
feat(scanner): perspective warp card detection (Phase 2) (#158)
Add quad corner refinement and homography warp for rectified identify
crops; wire warped capture through identifyTrackedCardCapture with
validation and axis-aligned fallback; add detection frame a11y labels.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:31:45 -05:00
varutasu
0b4f419f49
Scanner identify upgrade — Phase 1 hot path (#156)
* docs(convoy): seed scanner identify upgrade epic and sub-convoys

Baseline scan_attempts telemetry and three-phase plan for faster, more
accurate card identification without touching scanner chrome.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(scanner): tighten Layer-1 identify hot path (Phase 1)

Cut verify hold-still gates, OCR collector numbers on Layer 1, request
structured Gemini JSON, and skip automatic L2 refine when L1 opens the
printing picker. Includes convoy UX/architecture briefs and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:21:20 -05:00
varutasu
73424aae59
Mobile scanner checkout: scan first, commit later (#157)
* Start scanner-mobile-checkout convoy for the cart-then-commit phone flow.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Ship a cart-then-commit mobile scanner so phone sessions stay on the camera.

Scan matches enqueue locally instead of auto-writing ownership, checkout happens in a sheet, and audit fixes cover stale commit detection, returnUrl open redirects, nested Escape, and ember detection chrome.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:20:43 -05:00
Randall Stillwell
ec9bb2b93e feat(catalog): unified multi-game bulk sync + schema map update
Ship Pokemon and Lorcana bulk import libs/scripts, unified weekly cron
sync across MTG/Pokemon/Lorcana with per-game error isolation and
catalog_sync_log telemetry. Admin UI adds Unified/Incremental/Bulk MTG modes.

- Rename reconcile migrations to 1781442330* timestamps so they apply
  after bulk-data migrations without node-pg-migrate ordering conflicts
- Add Lorcana set-code normalization + orphan cleanup migrations
- Drop stricter user_cards_user_card_unique (keep 3-column foil unique)
- Update docs/SCHEMA_MAP.md for tags, card_tags, catalog_sync_log, bulk columns

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:22:19 -05:00
varutasu
4f6467f077
refactor(deck): extract grouping lib and stats sidebar (Brief 1) (#137)
Reuse computeDeckStats from deck-builder-stats; add groupDeckCards lib,
DeckDetailStatsSidebar component, and vitest coverage. Fixes stray semicolon
after useEffect. Page drops ~205 lines.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-13 00:44:16 -05:00
varutasu
cc1598962e
refactor(deck-builder): extract stats lib and stats bar (Brief 1) (#89)
Move Commander basic-land checks and deck aggregate metrics into
lib/deck-builder-stats.js with unit tests; render the summary row via
DeckBuilderStatsBar to shrink the page god-component.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 17:24:40 -05:00
varutasu
83d73eecaf
refactor(scanner): extract session and route API libs (page Brief 1) (#74)
Move scanner session persistence, queue merge helpers, and destination
routing fetch calls into lib/scanner-session.js and lib/scanner-route-api.js.
Load collections/decks on mount (were defined but never invoked).
Remove unused mana-symbol imports and dead select-all helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 16:15:27 -05:00
varutasu
986daaa1f2
refactor(scanner): extract camera lifecycle hook (Brief 4) (#71)
Move stream start/stop, detection intervals, and tracked-card polling
into lib/use-camera-scanner.js. CameraScanner keeps identification UI
and disambiguation wiring only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 15:44:56 -05:00
varutasu
7f3cf62344
refactor(scanner): extract card identification pipeline (Brief 3) (#70)
Move Layer-1/Layer-2 identify flow, outcome resolution, disambiguation
refine helpers, and scan-for-review API calls into lib/scanner-card-identify.js.
Remove unused manaSymbolSettings state from CameraScanner.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 15:34:13 -05:00
varutasu
a6813ef764
refactor(scanner): extract card detection and tracking lib (Brief 2) (#69)
Move OpenCV shape detection, coordinate conversion, overlap checks,
and tracked-card merge logic from CameraScanner into lib/scanner-card-detection.js
with unit tests for the pure helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:52:40 -05:00
varutasu
b615fac865
refactor(auth): add withAdmin() wrapper for admin API routes. (#68)
Extract shared 401/403 gate into permission-middleware and sweep the
four inline admin checks (import MTG/Pokemon, sync-catalog, card-submissions).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:42:22 -05:00
varutasu
c197dc61ed
Vocabulary cleanup follow-up (#55)
Correct dashboard title (My Collection overview, not Lists), sweep
remaining marketing/auth copy, update system-list seed description,
add vocabulary unit tests, and close the convoy record.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 10:01:03 -05:00
varutasu
c51ec6a04c
Normalize collector numbers in catalog match and harden scanner adds. (#53)
Share card-number normalization across reconcile and identify paths, retry set/name matches when OCR uses leading-zero collector numbers, and extend in-flight locks to all scanner destination actions with disabled Mark Owned feedback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 14:45:55 -05:00
varutasu
174a370fc3
Switch Pokémon catalog import to pokemon-tcg-data on GitHub. (#52)
Replace pokemontcg.io API discovery and import with raw JSON from PokemonTCG/pokemon-tcg-data; format collector numbers as number/printedTotal and drop the API key dependency for catalog sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 11:56:01 -05:00
varutasu
cf5c0558f1
Auto-link pending scan submissions after catalog sync imports. (#51)
When a set lands via runCatalogSync, match pending card_submissions by set/name/number to catalog rows and approve them with promoted_card_id instead of leaving them in the admin queue.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 09:47:59 -05:00
varutasu
bec0a7abbd
Prioritize newest missing sets in catalog sync queue. (#49)
The cron was importing oldest MTG sets first and never reaching recent Pokémon releases like Perfect Order; merge MTG and Pokémon by release date descending instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 21:49:58 -05:00
varutasu
0a47362103
feat(catalog): weekly Vercel Cron sync for MTG and Pokémon sets (#48)
Extract shared import logic into lib/card-import, discover missing sets via
Scryfall/Pokémon TCG APIs, and expose GET /api/cron/sync-catalog protected
by CRON_SECRET (max 3 sets/run, paced imports).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 14:59:59 -05:00
varutasu
9abbab6c21
feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision)
Resolves the launch-blocking 'TCG Vault vs Deck Hearth' inconsistency called out in AGENTS.md line 5 since project setup. Operator gate-0 decision: Deck Hearth wins. Two briefs applied serially. B1 (mechanical): 7-file display + comment sweep. B2 (infrastructure): Redis prefix rename in lib/rate-limit.js (5 prefixes, accept one-time counter reset), package.json + lockfile regen (STOP-on-churn confirmed only name lines changed), admin/alice/bob email rename in seed scripts + login pre-fill + NEW idempotent migration script scripts/migrations/2026-05-24-rename-admin-email.js. Risk 4 PRESERVE applied: test/lib/permission-middleware.test.js retains admin@tcgvault.com literal with 7-line architect-authored why comment (documents pre-fix-auth-bypass bug shape; preserves historical truth per project's gotcha-documentation convention). All 5 D-decisions ratified at gate-1 (Deck Hearth / deck-hearth / deckhearth / admin@deckhearth.com / full deckhearth Redis prefix). Local: lint 128 baseline (B1 + B2), vitest 21/21 (B1 + B2). CI all green: Playwright smoke 3/3 against rebranded preview in 1m4s, forbidden-cors-headers pass, forbidden-endpoints pass, Screenshot diff pass, Vercel deployment complete. Cross-validation lineage: 4th convoy where the same 3-test smoke spec defends auth surface through sweeping change (after PR #15 Layout default-user, PR #19 CORS, PR #20 rate-limit, now this PR #21 brand rename). OPERATOR POST-MERGE ACTION REQUIRED: run 'node scripts/migrations/2026-05-24-rename-admin-email.js' against prod Neon DB before next admin login (ordering: migration FIRST, then any subsequent setup-db invocation). Migration is ESM, idempotent, UNIQUE-collision-safe. PR #21 architect-commit 50ce9ab, B1 ac8c998, B2 1c18d21.
2026-05-25 02:28:29 -05:00
Randall Stillwell
1629afbb76 test(auth): add vitest harness + 16 auth-focused unit tests (Brief 5 of fix-auth-bypass)
Closes AGENTS.md gotcha #11 (well, the relevant half of it — "Testing:
None yet" line in §6 is now stale).

Installs vitest@^3.2.4 (single devDep, no UI / coverage / jsdom) and
adds 16 unit tests across 3 files that lock in post-Brief-1/2/4
behavior:

  test/lib/auth-secret.test.js (3 tests)
    - JWT_SECRET exports the env value
    - JWT_TOKEN_TTL is canonical 24h
    - Module throws at load when JWT_SECRET is empty

  test/lib/permission-middleware.test.js (8 tests)
    - getUserFromRequest returns null for: missing header, non-Bearer
      scheme, malformed token, wrong-secret token, expired token,
      valid-token-no-user-row
    - Returns user object for valid token + user row
    - Brief 2 regression lock: does NOT return the synthetic admin
      shape { userId: 1, email: 'admin@tcgvault.com', role: 'admin' }
      when no Authorization header is present

  test/api/auth-utils.test.js (5 tests)
    - generateToken issues 24h JWT (exp - iat === 86400)
    - Payload includes userId, email, role
    - verifyToken round-trips valid tokens
    - Returns null for malformed / wrong-secret tokens

CI: re-enabled the previously commented-out test: job in
.github/workflows/ci.yml. Blocking (no || true wrapper) — vitest is
the first runner in this repo and we want CI red on test regression.
JWT_SECRET is set via a CI-only fake; production secret is unaffected.

Rate-limit (Brief 4) coverage deferred to a future expand-auth-tests
convoy per architect's call (R11). package.json has "type": "module"
so vitest's default Vite-based transform handles .js ESM out of the
box — no transform config needed.

Convoy: fix-auth-bypass / Brief 5 (last brief)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 11:12:15 -05:00