exports.up = async (knex) => { await knex.schema.createTable('scenes_revisions', (table) => { table.increments('id'); table.integer('scene_id') .notNullable() .references('id') .inTable('releases') .onDelete('set null'); table.integer('user_id') .references('id') .inTable('users') .onDelete('set null'); table.json('base') .notNullable(); table.json('deltas') .notNullable(); table.text('hash') .notNullable(); table.text('comment'); table.boolean('approved'); table.integer('reviewed_by') .references('id') .inTable('users') .onDelete('set null'); table.datetime('reviewed_at'); table.text('feedback'); table.datetime('applied_at'); table.datetime('created_at') .notNullable() .defaultTo(knex.fn.now()); }); await knex.schema.createTable('bans', (table) => { table.increments('id'); table.integer('user_id') .references('id') .inTable('users') .onDelete('set null'); table.string('username'); table.specificType('ip', 'cidr'); table.boolean('match_all') .notNullable() .defaultTo(false); table.string('scope'); table.boolean('shadow'); table.integer('banned_by') .references('id') .inTable('users') .onDelete('set null'); table.datetime('expires_at') .notNullable(); table.datetime('created_at') .notNullable() .defaultTo(knex.fn.now()); }); await knex.schema.alterTable('users', (table) => { table.specificType('last_ip', 'cidr'); }); }; exports.down = async (knex) => { await knex.schema.dropTable('scenes_revisions'); await knex.schema.dropTable('bans'); await knex.schema.alterTable('users', (table) => { table.dropColumn('last_ip'); }); };