#!/usr/bin/env node /** * Reset Database Script * * This script drops and recreates all tables in your Neon database, then * seeds an admin user. DESTRUCTIVE — never run against production. * * Required env (in .env.local): * POSTGRES_URL — Neon connection string * ADMIN_INITIAL_PASSWORD — strong password for the seeded admin user * (generate with `openssl rand -base64 24`) * * Mirrors the post-`drop-public-setup` shape of `setup-neon-db.js` * (commit b63b509) — same ESM imports, same fail-loud env-var check, * same no-password-echo convention. Convoy: `fix-reset-db-script` * (2026-05-25). */ import dotenv from 'dotenv'; dotenv.config({ path: '.env.local' }); import { neon } from '@neondatabase/serverless'; import bcrypt from 'bcryptjs'; async function resetDatabase() { const adminPassword = process.env.ADMIN_INITIAL_PASSWORD; if (!adminPassword || !adminPassword.trim()) { console.error( '❌ ADMIN_INITIAL_PASSWORD environment variable is not set.\n' + '\n' + ' Set it in .env.local for local dev, or as a CI secret if you run reset from CI.\n' + ' Generate a strong password with: openssl rand -base64 24\n' + ' See README.md → "First-time admin setup" for the full flow.\n' ); process.exit(1); } const sql = neon(process.env.POSTGRES_URL); try { console.log('✅ Connecting to Neon database...'); console.log('🗑️ Dropping existing tables...'); await sql`DROP TABLE IF EXISTS deck_cards CASCADE`; await sql`DROP TABLE IF EXISTS decks CASCADE`; await sql`DROP TABLE IF EXISTS collection_cards CASCADE`; await sql`DROP TABLE IF EXISTS collections CASCADE`; await sql`DROP TABLE IF EXISTS user_cards CASCADE`; await sql`DROP TABLE IF EXISTS cards CASCADE`; await sql`DROP TABLE IF EXISTS users CASCADE`; console.log('✅ Dropped all tables'); await sql` CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, role VARCHAR(50) DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; console.log('✅ Created users table'); await sql` CREATE TABLE cards ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, set_name VARCHAR(255), set_code VARCHAR(50), card_number VARCHAR(50), rarity VARCHAR(50), game VARCHAR(50) NOT NULL, mana_cost VARCHAR(50), cmc INTEGER, card_type VARCHAR(255), colors JSONB, oracle_text TEXT, power VARCHAR(10), toughness VARCHAR(10), image_url TEXT, stock_image_url TEXT, current_price DECIMAL(10,2), market_price DECIMAL(10,2), scryfall_id VARCHAR(255) UNIQUE, verified BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; console.log('✅ Created cards table'); await sql` CREATE TABLE user_cards ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE, quantity INTEGER DEFAULT 1, condition VARCHAR(50) DEFAULT 'NM', is_foil BOOLEAN DEFAULT false, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, card_id, is_foil) ) `; console.log('✅ Created user_cards table'); await sql` CREATE TABLE collections ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, description TEXT, is_public BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; console.log('✅ Created collections table'); await sql` CREATE TABLE collection_cards ( id SERIAL PRIMARY KEY, collection_id INTEGER REFERENCES collections(id) ON DELETE CASCADE, card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE, quantity INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(collection_id, card_id) ) `; console.log('✅ Created collection_cards table'); await sql` CREATE TABLE decks ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, description TEXT, game VARCHAR(50), is_public BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; console.log('✅ Created decks table'); await sql` CREATE TABLE deck_cards ( id SERIAL PRIMARY KEY, deck_id INTEGER REFERENCES decks(id) ON DELETE CASCADE, card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE, quantity INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(deck_id, card_id) ) `; console.log('✅ Created deck_cards table'); const hashedPassword = await bcrypt.hash(adminPassword, 12); await sql` INSERT INTO users (email, password, role) VALUES (${'admin@deckhearth.com'}, ${hashedPassword}, ${'admin'}) ON CONFLICT (email) DO NOTHING `; console.log('✅ Created admin user'); console.log('🎉 Database reset completed successfully!'); console.log(''); console.log('📋 Database Details:'); console.log(' Database: Neon PostgreSQL'); console.log(' Admin user ready (email: admin@deckhearth.com)'); } catch (error) { console.error('❌ Database reset failed:', error.message); process.exit(1); } } resetDatabase();