Closes P1 #11 of .convoys/ship-readiness.md (launch sequence step 7) — "No migration tool — scripts/add-*.js graveyard". Schema changes post-this-convoy ship as node-pg-migrate migrations under migrations/ at the repo root; the legacy 27 scripts/add-*.js / scripts/fix-*.js / scripts/seed-*.js jobs remain append-only history per the no-go-zones rule. Decisions (full record in .convoys/migration-tool.md § Decisions): D1 — Tool: node-pg-migrate@^8. Rejected drizzle-kit / prisma migrate / kysely because each forces broader TypeScript surface than AGENTS.md Gotcha #9 allows (TS is a devDep only). node-pg-migrate is JavaScript-native, raw-SQL-friendly via pgm.sql(), and ESM-clean for the post-bump-next-js "type": "module" repo. Brings pg@^8.21.0 as a peer dep (dev-only; never loaded in the Next.js bundle). D2 — Migrations directory: migrations/ at the repo root. Separates the tool-wrapped artifacts from the historical scripts/migrations/ placeholder folder (which housed the lone pre-tool 2026-05-24-rename-admin-email.js migration and remains preserved for the audit trail). Matches node-pg-migrate's default flag. D3 — Tracking table: default pgmigrations (no name collision with the existing 7-table bootstrap; zero CLI noise). D4 — Backfill strategy: hand-translate scripts/setup-neon-db.js's DDL into the initial migration verbatim. Each await sql`...` block becomes one pgm.sql(`...`) call. Each CREATE uses IF NOT EXISTS, so the migration is idempotent against fresh AND pre-existing envs — re-running setup-db on an env that already has the schema is a no-op DDL-wise (only records the pgmigrations row). Documented assumption: prod has drifted via the 27 historical add-*.js scripts; reconciling those into the migration history is the queued reconcile-historical-add-scripts follow-up convoy. D5 — Bootstrap reconciliation: split. setup-neon-db.js now (1) validates ADMIN_INITIAL_PASSWORD + POSTGRES_URL, (2) spawns `npm run migrate up` via child_process with stdio inherited, (3) seeds the admin row with ON CONFLICT (email) DO NOTHING. The seven DDL blocks are deleted from setup-neon-db.js; success/error message copy is updated to mention the migration step explicitly. D6 — CI integration: defer. Wiring a CI job that runs migrate up against a test DB needs either a dedicated Neon branch + secret OR a Postgres service container; both are real work. Surface as wire-migrate-into-ci follow-up. Risk acknowledged in .convoys/migration-tool.md § R3. D7 — Down-migration on the initial backfill: hard stub. Rolling back the initial schema would drop every user / card / collection / deck row in the DB. The stub throws with a long-form error pointing at the recommended alternative (branch the Neon database + forward-apply). Future migrations that touch one of the seven bootstrap tables write their own dated migration with a real down(). Verification (pre-PR): - npm run lint → 128 problems (baseline preserved, zero regression; migration file is lint-clean, no new ignore patterns) - npm run test:run → 21/21 pass - node --check on migrations/1779853647564_initial-schema.js + on scripts/setup-neon-db.js → exit 0 - Module load + down() throw verified via dynamic import - npm run migrate -- --help reaches the node-pg-migrate CLI through the wrapper Live verification against a Neon branch is deferred (no throwaway branch available); the operator's optional post-merge sequence is documented in .convoys/migration-tool.md § Operator runbook. See .convoys/migration-tool.md § Follow-ups for the queued wire-migrate-into-ci / reconcile-historical-add-scripts / retire-graveyard-scripts-after-audit / audit-node-pg-migrate-transitive-deps / add-migration-template follow-up convoys. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|---|---|---|
| .. | ||
| migrations | ||
| add-card-columns.js | ||
| add-collaboration-features.js | ||
| add-collection-slugs.js | ||
| add-favorites-system.js | ||
| add-image-column.js | ||
| add-system-collection-column.js | ||
| add-updated-at-column.js | ||
| add-user-profile-columns.js | ||
| add-user-profile-fields.js | ||
| bulk-import-all.js | ||
| create-sample-cards.js | ||
| create-sample-collections.js | ||
| create-test-users.js | ||
| demote-admin-to-user.js | ||
| fix-lorcana-images.js | ||
| fix-user-cards-constraints.js | ||
| import-lorcana-simple.js | ||
| import-lorcana.js | ||
| import-popular-sets.js | ||
| list-users.js | ||
| log-convoy-event.sh | ||
| promote-user-to-admin.js | ||
| README.md | ||
| reset-db.js | ||
| seed-collections-alice-bob.js | ||
| seed-collections-with-cards.js | ||
| setup-neon-db.js | ||
| wt.sh | ||
Deck Hearth Bulk Import Scripts
This directory contains scripts for bulk importing TCG card data into the database.
Available Scripts
Card Import Scripts
1. import-popular-sets.js - Popular Sets Import
Imports the most popular and recent sets from all three TCGs (Magic, Pokemon, Lorcana).
Usage:
npm run import-popular
What it imports:
- Magic: The Gathering: ~100+ sets from Alpha to recent releases
- Pokemon: ~100+ sets from Base Set to current Scarlet & Violet
- Lorcana: All 3 available sets
Estimated time: 2-4 hours (depending on API response times)
2. bulk-import-all.js - Complete Import
Imports ALL available sets from all TCGs (comprehensive import).
Usage:
npm run import-all
What it imports:
- Magic: The Gathering: 100+ sets (Alpha to current)
- Pokemon: 100+ sets (Base Set to current)
- Lorcana: All available sets
Estimated time: 4-8 hours (depending on API response times)
User Management Scripts
3. list-users.js - List All Users
Lists all users in the database with their roles and details.
Usage:
node scripts/list-users.js
Output: Shows user ID, email, role (admin/user), and creation date.
4. promote-user-to-admin.js - Promote User to Admin
Promotes a regular user to admin role.
Usage:
node scripts/promote-user-to-admin.js user@example.com
Requirements: User must be registered first.
5. demote-admin-to-user.js - Demote Admin to User
Demotes an admin back to regular user role.
Usage:
node scripts/demote-admin-to-user.js admin@example.com
Safety: Ensures at least one admin always remains in the system.
Database Management Scripts
6. setup-neon-db.js - Database Setup
Sets up the Neon PostgreSQL database with all required tables and creates the default admin user.
Usage:
node scripts/setup-neon-db.js
7. reset-db.js - Database Reset
Resets the database by dropping and recreating all tables.
Usage:
node scripts/reset-db.js
How It Works
- Sequential Import: Scripts import sets one by one to avoid overwhelming the APIs
- Error Handling: Failed imports are logged but don't stop the process
- Progress Tracking: Real-time console output shows progress
- Results Logging: Detailed results are saved to JSON files
- Rate Limiting: 1-second delays between imports to be respectful to APIs
Output Files
After running, you'll get timestamped JSON files with detailed results:
popular-sets-import-results-[timestamp].jsonbulk-import-results-[timestamp].json
These files contain:
- Success/failure status for each set
- Number of cards imported per set
- Error messages for failed imports
- Summary statistics
Prerequisites
- Server Running: Make sure your Next.js dev server is running (
npm run dev) - Database Setup: Ensure the database is initialized (
npm run setup-db) - Dependencies: All required packages are installed
Recommendations
For First-Time Setup
Start with the popular sets import:
npm run import-popular
This will give you a solid foundation with the most relevant cards.
For Complete Database
If you want everything, use the full import:
npm run import-all
For Ongoing Management
After the initial bulk import, use the admin interface at /admin/card-import for:
- Importing new sets as they release
- Selective imports of specific sets
- Monitoring import progress
Troubleshooting
Common Issues
- API Rate Limits: If you get rate limit errors, the scripts will continue but log failures
- Network Issues: Scripts will retry and continue with the next set
- Server Restart: If the server restarts, just restart the import script
Monitoring Progress
Watch the console output for:
- ✅ Successful imports with card counts
- ❌ Failed imports with error messages
- 📊 Summary statistics at the end
Stopping and Resuming
You can stop the script with Ctrl+C and restart it later. The scripts will start from the beginning, but the database will only store unique cards (no duplicates).
API Endpoints Used
POST /api/cards/import-mtg- Magic: The Gathering importsPOST /api/cards/import-pokemon- Pokemon importsPOST /api/cards/import-lorcana- Lorcana imports
Data Sources
- Magic: The Gathering: Scryfall API
- Pokemon: Pokemon TCG API
- Lorcana: Lorcana API (limited availability)
Performance Notes
- Each set typically contains 100-400 cards
- Total database size after full import: ~50,000-100,000 cards
- Import speed: ~1 set per minute (with delays)
- Database storage: ~100-200MB after full import