/** * @type {import('node-pg-migrate').ColumnDefinitions | undefined} */ export const shorthands = undefined; /** * @param pgm {import('node-pg-migrate').MigrationBuilder} * @param run {() => void | undefined} * @returns {Promise | void} */ export const up = (pgm) => { // Создание расширения для генерации UUID pgm.createExtension('pgcrypto', { ifNotExists: true }); // Таблица пользователей pgm.createTable('users', { id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') }, telegram_id: { type: 'bigint', notNull: true, unique: true }, username: { type: 'varchar(255)' }, first_name: { type: 'varchar(255)' }, last_name: { type: 'varchar(255)' }, language_code: { type: 'varchar(10)', default: 'en' }, is_active: { type: 'boolean', default: true }, created_at: { type: 'timestamp', default: pgm.func('NOW()') }, last_active_at: { type: 'timestamp', default: pgm.func('NOW()') }, updated_at: { type: 'timestamp', default: pgm.func('NOW()') }, premium: { type: 'boolean', default: false }, premium_expires_at: { type: 'timestamp' } }); // Таблица профилей pgm.createTable('profiles', { id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') }, user_id: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, name: { type: 'varchar(255)', notNull: true }, age: { type: 'integer', notNull: true, check: 'age >= 18 AND age <= 100' }, gender: { type: 'varchar(10)', notNull: true, check: "gender IN ('male', 'female', 'other')" }, interested_in: { type: 'varchar(10)', notNull: true, check: "interested_in IN ('male', 'female', 'both')" }, looking_for: { type: 'varchar(20)', default: 'both', check: "looking_for IN ('male', 'female', 'both')" }, bio: { type: 'text' }, photos: { type: 'jsonb', default: '[]' }, interests: { type: 'jsonb', default: '[]' }, city: { type: 'varchar(255)' }, education: { type: 'varchar(255)' }, job: { type: 'varchar(255)' }, height: { type: 'integer' }, religion: { type: 'varchar(255)' }, dating_goal: { type: 'varchar(255)' }, smoking: { type: 'boolean' }, drinking: { type: 'boolean' }, has_kids: { type: 'boolean' }, location_lat: { type: 'decimal(10,8)' }, location_lon: { type: 'decimal(11,8)' }, search_min_age: { type: 'integer', default: 18 }, search_max_age: { type: 'integer', default: 50 }, search_max_distance: { type: 'integer', default: 50 }, is_verified: { type: 'boolean', default: false }, is_visible: { type: 'boolean', default: true }, created_at: { type: 'timestamp', default: pgm.func('NOW()') }, updated_at: { type: 'timestamp', default: pgm.func('NOW()') } }); // Таблица свайпов pgm.createTable('swipes', { id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') }, user_id: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, target_user_id: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, type: { type: 'varchar(20)', notNull: true, check: "type IN ('like', 'pass', 'superlike')" }, created_at: { type: 'timestamp', default: pgm.func('NOW()') }, is_match: { type: 'boolean', default: false } }); pgm.addConstraint('swipes', 'unique_swipe', { unique: ['user_id', 'target_user_id'] }); // Таблица матчей pgm.createTable('matches', { id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') }, user_id_1: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, user_id_2: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, created_at: { type: 'timestamp', default: pgm.func('NOW()') }, last_message_at: { type: 'timestamp' }, is_active: { type: 'boolean', default: true }, is_super_match: { type: 'boolean', default: false }, unread_count_1: { type: 'integer', default: 0 }, unread_count_2: { type: 'integer', default: 0 } }); pgm.addConstraint('matches', 'unique_match', { unique: ['user_id_1', 'user_id_2'] }); // Таблица сообщений pgm.createTable('messages', { id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') }, match_id: { type: 'uuid', references: 'matches(id)', onDelete: 'CASCADE' }, sender_id: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, receiver_id: { type: 'uuid', references: 'users(id)', onDelete: 'CASCADE' }, content: { type: 'text', notNull: true }, message_type: { type: 'varchar(20)', default: 'text', check: "message_type IN ('text', 'photo', 'gif', 'sticker')" }, created_at: { type: 'timestamp', default: pgm.func('NOW()') }, is_read: { type: 'boolean', default: false } }); // Создание индексов pgm.createIndex('users', 'telegram_id'); pgm.createIndex('profiles', 'user_id'); pgm.createIndex('profiles', ['location_lat', 'location_lon'], { where: 'location_lat IS NOT NULL AND location_lon IS NOT NULL' }); pgm.createIndex('profiles', ['age', 'gender', 'interested_in']); pgm.createIndex('swipes', ['user_id', 'target_user_id']); pgm.createIndex('matches', ['user_id_1', 'user_id_2']); pgm.createIndex('messages', ['match_id', 'created_at']); }; /** * @param pgm {import('node-pg-migrate').MigrationBuilder} * @param run {() => void | undefined} * @returns {Promise | void} */ export const down = (pgm) => { pgm.dropTable('messages'); pgm.dropTable('matches'); pgm.dropTable('swipes'); pgm.dropTable('profiles'); pgm.dropTable('users'); pgm.dropExtension('pgcrypto'); };