geo detection
This commit is contained in:
152
temp_migrations/1758144488937_initial-schema.js
Normal file
152
temp_migrations/1758144488937_initial-schema.js
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @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> | 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> | void}
|
||||
*/
|
||||
export const down = (pgm) => {
|
||||
pgm.dropTable('messages');
|
||||
pgm.dropTable('matches');
|
||||
pgm.dropTable('swipes');
|
||||
pgm.dropTable('profiles');
|
||||
pgm.dropTable('users');
|
||||
pgm.dropExtension('pgcrypto');
|
||||
};
|
||||
25
temp_migrations/1758144618548_add-missing-profile-columns.js
Normal file
25
temp_migrations/1758144618548_add-missing-profile-columns.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
// Добавляем колонки, которые могли быть пропущены в схеме
|
||||
pgm.addColumns('profiles', {
|
||||
hobbies: { type: 'text' }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {
|
||||
pgm.dropColumns('profiles', ['hobbies']);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {};
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
// Добавляем отсутствующие колонки в таблицу profiles
|
||||
pgm.addColumns('profiles', {
|
||||
religion: { type: 'varchar(255)' },
|
||||
dating_goal: { type: 'varchar(255)' },
|
||||
smoking: { type: 'boolean' },
|
||||
drinking: { type: 'boolean' },
|
||||
has_kids: { type: 'boolean' }
|
||||
}, { ifNotExists: true });
|
||||
};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {
|
||||
pgm.dropColumns('profiles', ['religion', 'dating_goal', 'smoking', 'drinking', 'has_kids'], { ifExists: true });
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {};
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
// Изменяем тип столбцов с boolean на varchar для хранения строковых значений
|
||||
pgm.alterColumn('profiles', 'smoking', {
|
||||
type: 'varchar(50)',
|
||||
using: 'smoking::text'
|
||||
});
|
||||
|
||||
pgm.alterColumn('profiles', 'drinking', {
|
||||
type: 'varchar(50)',
|
||||
using: 'drinking::text'
|
||||
});
|
||||
|
||||
// has_kids оставляем boolean, так как у него всего два состояния
|
||||
};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {
|
||||
// Возвращаем столбцы к типу boolean
|
||||
pgm.alterColumn('profiles', 'smoking', {
|
||||
type: 'boolean',
|
||||
using: "CASE WHEN smoking = 'regularly' OR smoking = 'sometimes' THEN true ELSE false END"
|
||||
});
|
||||
|
||||
pgm.alterColumn('profiles', 'drinking', {
|
||||
type: 'boolean',
|
||||
using: "CASE WHEN drinking = 'regularly' OR drinking = 'sometimes' THEN true ELSE false END"
|
||||
});
|
||||
};
|
||||
50
temp_migrations/1758149087361_add-column-synonyms.js
Normal file
50
temp_migrations/1758149087361_add-column-synonyms.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @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> | void}
|
||||
*/
|
||||
export const up = (pgm) => {
|
||||
// Создание представления для совместимости со старым кодом (swipes)
|
||||
pgm.sql(`
|
||||
CREATE OR REPLACE VIEW swipes_view AS
|
||||
SELECT
|
||||
id,
|
||||
user_id AS swiper_id,
|
||||
target_user_id AS swiped_id,
|
||||
type AS direction,
|
||||
created_at,
|
||||
is_match
|
||||
FROM swipes;
|
||||
`);
|
||||
|
||||
// Создание представления для совместимости со старым кодом (matches)
|
||||
pgm.sql(`
|
||||
CREATE OR REPLACE VIEW matches_view AS
|
||||
SELECT
|
||||
id,
|
||||
user_id_1 AS user1_id,
|
||||
user_id_2 AS user2_id,
|
||||
created_at AS matched_at,
|
||||
is_active AS status,
|
||||
last_message_at,
|
||||
is_super_match,
|
||||
unread_count_1,
|
||||
unread_count_2
|
||||
FROM matches;
|
||||
`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param pgm {import('node-pg-migrate').MigrationBuilder}
|
||||
* @param run {() => void | undefined}
|
||||
* @returns {Promise<void> | void}
|
||||
*/
|
||||
export const down = (pgm) => {
|
||||
pgm.sql(`DROP VIEW IF EXISTS swipes_view;`);
|
||||
pgm.sql(`DROP VIEW IF EXISTS matches_view;`);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
exports.shorthands = undefined;
|
||||
|
||||
exports.up = pgm => {
|
||||
// Проверяем существование таблицы scheduled_notifications
|
||||
pgm.sql(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.tables
|
||||
WHERE table_name = 'scheduled_notifications'
|
||||
) THEN
|
||||
-- Проверяем, нет ли уже столбца processed
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'scheduled_notifications' AND column_name = 'processed'
|
||||
) THEN
|
||||
-- Добавляем столбец processed
|
||||
ALTER TABLE scheduled_notifications ADD COLUMN processed BOOLEAN DEFAULT FALSE;
|
||||
END IF;
|
||||
ELSE
|
||||
-- Создаем таблицу, если она не существует
|
||||
CREATE TABLE scheduled_notifications (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES users(id),
|
||||
type VARCHAR(50) NOT NULL,
|
||||
data JSONB,
|
||||
scheduled_at TIMESTAMP NOT NULL,
|
||||
processed BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = pgm => {
|
||||
pgm.sql(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'scheduled_notifications' AND column_name = 'processed'
|
||||
) THEN
|
||||
ALTER TABLE scheduled_notifications DROP COLUMN processed;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
`);
|
||||
};
|
||||
Reference in New Issue
Block a user