/** * Initial schema backfill migration. * * Reproduces the 7-table DDL that `scripts/setup-neon-db.js` has been the * documented source-of-truth bootstrap for since the project's first commit. * Every statement uses `CREATE TABLE IF NOT EXISTS` so the migration is * idempotent against: * * 1. A brand-new Neon branch (creates all tables fresh). * 2. An existing env where `npm run setup-db` has already run pre-convoy * (every CREATE is a no-op; the `pgmigrations` row is the only change). * 3. An existing env where the legacy `scripts/add-*.js` / `scripts/fix-*.js` * jobs added columns beyond the bootstrap shape — those columns are * preserved (CREATE TABLE IF NOT EXISTS does not touch existing tables). * * The shape is byte-equivalent to `scripts/setup-neon-db.js` HEAD as of * `convoy/migration-tool` branch. If those scripts diverge again in the * future, ship a new dated migration alongside the script edit — do NOT * edit this file in place (this file is now itself an append-only artifact; * mutating it would silently corrupt any env that has it recorded as run). * * Down-migration is intentionally a hard stub. See `down()` below. * * @type {import('node-pg-migrate').ColumnDefinitions | undefined} */ export const shorthands = undefined; /** * @param {import('node-pg-migrate').MigrationBuilder} pgm * @returns {void} */ export const up = (pgm) => { pgm.sql(` CREATE TABLE IF NOT EXISTS 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 ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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 ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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) ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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 ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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) ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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 ) `); pgm.sql(` CREATE TABLE IF NOT EXISTS 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) ) `); }; /** * Down-migration is a hard stub. Rolling back the initial schema would drop * every user, card, collection, and deck row in the database — and the * `pgmigrations` row itself — leaving nothing to migrate forward from. If * you genuinely need a clean schema for testing, branch the Neon database * (instant + cheap) and run `npm run migrate up` against the branch instead * of rolling this migration back. * * If a future schema correction needs to mutate one of the seven bootstrap * tables, write a NEW dated migration (`npm run migrate create `) * with a real `down` — do NOT remove this stub. * * @returns {void} */ export const down = () => { throw new Error( '[migration:1779853647564_initial-schema] Refusing to drop the initial schema. ' + 'Rolling back this migration would erase every users / cards / collections / decks row ' + 'in the database. If you need a clean schema for testing, branch the Neon database and ' + 'run `npm run migrate up` against the branch instead. See migrations/1779853647564_initial-schema.js ' + "down()'s docstring for the long form." ); };