Closes P1 #10 step 2 / launch sequence step 10. PR #17 already plumbed VERCEL_AUTOMATION_BYPASS_SECRET into both workflows' env; this brief wires the actual @playwright/test dep, the playwright.config.js that bridges env -> use.extraHTTPHeaders, and a single visual spec so the screenshot workflow has something to discover. Per architect Decision 1 - tests/smoke/app.smoke.spec.ts stays as .ts (npx eslint exits 0 against the current config); new visual spec also .ts. Per Decision 2 - playwright.config.js fail-loud in CI when VERCEL_AUTOMATION_BYPASS_SECRET is missing (throw at config load with actionable message naming the env var, rotation command, AGENTS.md reference); warn-and-no-op in dev. Mirrors lib/rate-limit.js fail-closed pattern (AGENTS.md Gotcha #12). Per Decision 3 - two projects partitioned by testMatch: - smoke -> tests/smoke/**/*.spec.{ts,js} - visual -> tests/visual/**/*.spec.{ts,js} Shared use: block. Matches the workflows' --project=smoke|visual invocations. Per Decision 4 - NO baselines committed. The Mac-vs-Linux platform- suffix issue (architect Boot-the-brief Finding 7: Playwright's custom snapshotPathTemplate has no {platform} token) means a Mac-generated baseline would silently overwrite a Linux CI baseline. Queued as seed-visual-baselines-on-linux follow-up convoy. First CI run of Screenshot diff will fail at the test step; existing continue-on-error: true swallows it and the comment-on-PR step posts the run link. Per Decision 5 - no eslint.config.mjs change. Per Decision 6 - three simple scripts (test:smoke, test:visual, test:visual:update). No auto-boot wrapper. Verification: - npm ci --dry-run exits 0 (lockfile in sync) - npm run lint: 128 problems (baseline match - 81 errors, 47 warnings) - npm run test:run: 21/21 vitest pass (no regression) - npm run test:smoke (no BASE_URL): dev-path warn-and-continue fires "[playwright.config] VERCEL_AUTOMATION_BYPASS_SECRET unset" warning, enumerates 3 smoke tests, attempts to run (fails on missing local chromium binary - workflows install browsers via npx playwright install --with-deps chromium) - CI=true npm run test:smoke: CI-path throws at config load with "VERCEL_AUTOMATION_BYPASS_SECRET is required in CI to reach Vercel-Protection-protected preview deployments. Reseed via: gh secret set VERCEL_AUTOMATION_BYPASS_SECRET --body \"<value>\". See AGENTS.md \xc2\xa7 7 for the full plumbing context." - npx playwright --version: Version 1.60.0 Implementation note: removed the architect's defensive eslint-disable- next-line no-console directive in the dev-warn branch because the active eslint config does not enable no-console for repo-root config files; ESLint reported the directive as unused, which would have regressed the lint baseline by +1. Behavior unchanged; comment-only edit per the brief's explicit "comment wording can be tightened" allowance. Replaced with an explanatory comment noting why the directive is intentionally absent. Implementation note 2: alphabetized @playwright/test as the FIRST entry in devDependencies (before @testing-library/dom) because "@p" < "@t". The brief's prose said "between @testing-library/react and autoprefixer" which would have broken alphabetization; followed the brief's overarching principle ("alphabetical position") instead. No code paths in pages/** or lib/** touched. No workflow YAML edited. No AGENTS.md edits (doc-writer pass at convoy close handles the seed-on-Linux runbook). Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|---|---|---|
| .convoys | ||
| .cursor | ||
| .github | ||
| components | ||
| docs | ||
| lib | ||
| pages | ||
| public | ||
| scripts | ||
| styles | ||
| test | ||
| tests | ||
| .agent-context-manifest.yml | ||
| .gitignore | ||
| .npmrc | ||
| AGENTS.md | ||
| eslint.config.mjs | ||
| next.config.js | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.js | ||
| postcss.config.js | ||
| README.md | ||
| tailwind.config.js | ||
| TESTING_GUIDE.md | ||
| vercel.json | ||
| vitest.config.js | ||
TCG Vault
A modern trading card game collection manager built with Next.js and Neon Database.
🚀 Features
- Card Management: Track your MTG, Pokémon, and Lorcana cards
- Collection Organization: Create and manage card collections
- Deck Building: Build and share decks
- Authentication: Secure user accounts with JWT
- Admin Panel: Manage cards and users
- Real-time Pricing: Track card values
🛠️ Tech Stack
- Frontend: Next.js 16 (Pages router), React 18, JavaScript (TypeScript is a devDep only — see
AGENTS.mdGotcha #9) - Backend: Next.js API Routes
- Database: Neon PostgreSQL (serverless)
- Authentication: JWT with bcrypt (24-hour expiry;
lib/auth-secret.jsis the single source of truth forJWT_SECRET) - Rate limiting:
@upstash/ratelimiton/api/auth/login+/api/auth/register(5 attempts / 15 min per IP) - Testing: Vitest (unit); Playwright queued
- Styling: Tailwind CSS
- Deployment: Vercel
📦 Installation
-
Clone the repository
git clone <repository-url> cd tcg-vault -
Install dependencies
npm install -
Set up environment variables
cp .env.example .env.localUpdate
.env.localwith your Neon database URL and a real JWT secret:POSTGRES_URL="postgresql://your-username:your-password@your-host/your-database" JWT_SECRET="<generate with: openssl rand -hex 32>" # Required for `npm run setup-db` — used once to hash the initial admin password. # Set in .env.local for local dev, or as a CI secret if you run setup from CI. ADMIN_INITIAL_PASSWORD="<generate with: openssl rand -base64 24>" # Optional — exercise the rate limiter locally. Without them, `lib/rate-limit.js` # warn-and-no-ops in dev. In production these are auto-provisioned by the # Vercel Upstash Marketplace integration. KV_REST_API_URL="https://<your-upstash-host>.upstash.io" KV_REST_API_TOKEN="<your-upstash-rest-token>"JWT_SECRETis required —lib/auth-secret.jsthrows at import time if it's unset.ADMIN_INITIAL_PASSWORDis required fornpm run setup-db— the script exits with code 1 if it's unset. -
Set up the database
npm run setup-db -
Start development server
npm run dev
🗄️ Database Schema
The application uses the following tables:
users- User accounts and authenticationcards- Card information and metadatauser_cards- User's card collectionscollections- Named card collectionscollection_cards- Cards in collectionsdecks- Deck definitionsdeck_cards- Cards in decks
🔧 API Endpoints
Authentication
POST /api/auth/register- User registrationPOST /api/auth/login- User login
Admin
GET /api/admin- Admin panel data
Health Check
GET /api/health- Application health
Note: Earlier revisions of this README also listed
GET /api/test-db(and three other unauthenticated dev endpoints:/api/simple,/api/test-auth,/api/setup-database). All four were deleted infix-auth-bypassBrief 3 (commitfc0dd73) and CI now blocks their reintroduction. Don't recreate them.
🚀 Deployment
This app is configured for deployment on Vercel:
- Connect your repository to Vercel
- Set environment variables in Vercel dashboard
- Deploy automatically on push to main branch
📁 Project Structure
tcg-vault/
├── pages/ # Next.js pages and API routes
│ ├── api/ # API endpoints
│ │ ├── auth/ # Authentication routes
│ │ └── admin/ # Admin routes
│ ├── _app.js # App wrapper
│ └── index.js # Home page
├── lib/ # Utility libraries
│ └── database.js # Database adapter
├── scripts/ # Database setup scripts
├── public/ # Static assets
└── .env.local # Environment variables
🔐 First-time admin setup
npm run setup-db creates a single admin user the first time it runs. The
password is read from the ADMIN_INITIAL_PASSWORD environment variable; the
script exits with code 1 (and does not open a database connection) if the
variable is unset or empty.
- Local dev: set
ADMIN_INITIAL_PASSWORDin.env.localbefore runningnpm run setup-db. Useopenssl rand -base64 24(or any other strong source) to generate the value. - CI / Vercel: set
ADMIN_INITIAL_PASSWORDas a project secret if setup ever runs from CI. The env var is only read by the seed script; runtime auth uses the per-user password stored in the database. - Admin email: the seed creates
admin@tcgvault.com. Change the password immediately after first login via the app's profile settings.
Operators of envs that pre-date this change:
npm run setup-dbis idempotent (ON CONFLICT (email) DO NOTHING) — re-running it withADMIN_INITIAL_PASSWORDset will not rotate an existing admin row's password. If your environment was set up before this change and still has the prior weak default, rotate the password manually via the app after logging in, or wait for the queuedrotate-default-adminfollow-up convoy.
🤝 Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
📄 License
MIT License - see LICENSE file for details