Closes the pick-a-name convoy. Applies D1-D5 + Risk 4 PRESERVE per
operator gate-1 ratification.
Infrastructure renames:
- lib/rate-limit.js: 5 Redis key prefixes tcgvault:* → deckhearth:* (D5).
One-time per-15-min / per-1-hour counter reset accepted; no user impact
because counter windows are short anyway. Existing rate-limit state in
Upstash will accumulate at the new prefix on first request.
- package.json: name field tcg-vault → deck-hearth (D2)
- package-lock.json: regenerated for the name change; STOP-on-churn
protocol confirmed only the two name lines changed (no dep churn)
- All three test users (admin/alice/bob) renamed to @deckhearth.com (D4)
- One-off migration script scripts/migrations/2026-05-24-rename-admin-
email.js (NEW): ESM, idempotent, UNIQUE-collision-safe. Per the
no-go-zones rule for new migrations. Operator MUST run post-deploy.
- README.md + TESTING_GUIDE.md operator-caveat blockquotes flagged
- pages/login.js demo-credential pre-fill updated
PRESERVED per Risk 4:
- test/lib/permission-middleware.test.js literal admin@tcgvault.com
with 7-line architect-authored "why" comment block. This is the
documented pre-fix-auth-bypass bug shape; the regression-lock
literal stays as historical truth.
Verification:
- npm run lint: 128 problems (baseline preserved)
- npm run test:run: 21/21 pass (preserved literal keeps green)
- Grep across full repo: 0 hits for TCG Vault / tcgvault / tcg-vault
except the explicit preserve in the test file + .convoys/ historical
- lib/rate-limit.js: 5 deckhearth: prefixes, 0 tcgvault: prefixes
- node --check on the new migration script: exit 0
- git diff package-lock.json: only the 2 "name": lines changed (no churn)
Operator post-merge action:
- Run `node scripts/migrations/2026-05-24-rename-admin-email.js` against
the production Neon DB. Order matters: migration FIRST, then any
subsequent `npm run setup-db` invocation. Migration script will refuse
to run if collision detected (means setup-db already ran post-rename).
Architect brief: .convoys/pick-a-name/brief-2-infrastructure-and-email-migration.md
Architect commit: 50ce9ab
Operator gate-1: D1-D5 + Risk 4 PRESERVE ratified.
Co-authored-by: Cursor <cursoragent@cursor.com>
163 lines
No EOL
6.1 KiB
Markdown
163 lines
No EOL
6.1 KiB
Markdown
# Deck Hearth
|
|
|
|
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.md` Gotcha #9)
|
|
- **Backend**: Next.js API Routes
|
|
- **Database**: Neon PostgreSQL (serverless)
|
|
- **Authentication**: JWT with bcrypt (24-hour expiry; `lib/auth-secret.js` is the single source of truth for `JWT_SECRET`)
|
|
- **Rate limiting**: `@upstash/ratelimit` on `/api/auth/login` + `/api/auth/register` (5 attempts / 15 min per IP)
|
|
- **Testing**: Vitest (unit); Playwright queued
|
|
- **Styling**: Tailwind CSS
|
|
- **Deployment**: Vercel
|
|
|
|
## 📦 Installation
|
|
|
|
1. **Clone the repository**
|
|
```bash
|
|
git clone <repository-url>
|
|
cd tcg-vault
|
|
```
|
|
|
|
2. **Install dependencies**
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
3. **Set up environment variables**
|
|
```bash
|
|
cp .env.example .env.local
|
|
```
|
|
|
|
Update `.env.local` with your Neon database URL and a real JWT secret:
|
|
```env
|
|
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_SECRET` is **required** — `lib/auth-secret.js` throws at import time if it's unset.
|
|
`ADMIN_INITIAL_PASSWORD` is **required** for `npm run setup-db` — the script exits with code 1 if it's unset.
|
|
|
|
4. **Set up the database**
|
|
```bash
|
|
npm run setup-db
|
|
```
|
|
|
|
5. **Start development server**
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
## 🗄️ Database Schema
|
|
|
|
The application uses the following tables:
|
|
- `users` - User accounts and authentication
|
|
- `cards` - Card information and metadata
|
|
- `user_cards` - User's card collections
|
|
- `collections` - Named card collections
|
|
- `collection_cards` - Cards in collections
|
|
- `decks` - Deck definitions
|
|
- `deck_cards` - Cards in decks
|
|
|
|
## 🔧 API Endpoints
|
|
|
|
### Authentication
|
|
- `POST /api/auth/register` - User registration
|
|
- `POST /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 in `fix-auth-bypass` Brief 3 (commit `fc0dd73`) and CI now blocks their reintroduction. Don't recreate them.
|
|
|
|
## 🚀 Deployment
|
|
|
|
This app is configured for deployment on Vercel:
|
|
|
|
1. **Connect your repository** to Vercel
|
|
2. **Set environment variables** in Vercel dashboard
|
|
3. **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_PASSWORD` in `.env.local` before running
|
|
`npm run setup-db`. Use `openssl rand -base64 24` (or any other strong
|
|
source) to generate the value.
|
|
- **CI / Vercel:** set `ADMIN_INITIAL_PASSWORD` as 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@deckhearth.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-db` is
|
|
> idempotent (`ON CONFLICT (email) DO NOTHING`) — re-running it with
|
|
> `ADMIN_INITIAL_PASSWORD` set 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 queued `rotate-default-admin` follow-up
|
|
> convoy.
|
|
|
|
> **Operators of envs that pre-date the `pick-a-name` convoy (2026-05-24):**
|
|
> the admin row was renamed from `admin@tcgvault.com` to
|
|
> `admin@deckhearth.com`. Run
|
|
> `node scripts/migrations/2026-05-24-rename-admin-email.js` once after
|
|
> deploy to UPDATE any existing `@tcgvault.com` user rows (the admin row,
|
|
> plus alice/bob if `npm run create-test-users` was ever run). Re-running
|
|
> the migration after the first run is idempotent and prints "Nothing to
|
|
> migrate." Verify post-migration with
|
|
> `psql $POSTGRES_URL -c "SELECT email FROM users WHERE email LIKE '%@tcgvault.com'"`
|
|
> — expect zero rows.
|
|
|
|
## 🤝 Contributing
|
|
|
|
1. Fork the repository
|
|
2. Create a feature branch
|
|
3. Make your changes
|
|
4. Submit a pull request
|
|
|
|
## 📄 License
|
|
|
|
MIT License - see LICENSE file for details |