feat: add versioned migrations and kysely query layer
This commit is contained in:
16
backend/migrations/1700000001000_create-users.cjs
Normal file
16
backend/migrations/1700000001000_create-users.cjs
Normal file
@@ -0,0 +1,16 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createExtension('pgcrypto', { ifNotExists: true });
|
||||
pgm.createTable('users', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
external_subject_id: { type: 'text', notNull: true },
|
||||
display_name: { type: 'text', notNull: true },
|
||||
email: { type: 'text', notNull: true },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint('users', 'users_external_subject_id_key', 'UNIQUE(external_subject_id)');
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('users');
|
||||
};
|
||||
24
backend/migrations/1700000002000_create-user-preferences.cjs
Normal file
24
backend/migrations/1700000002000_create-user-preferences.cjs
Normal file
@@ -0,0 +1,24 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('user_preferences', {
|
||||
user_id: {
|
||||
type: 'uuid',
|
||||
primaryKey: true,
|
||||
notNull: true,
|
||||
references: 'users(id)',
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
preferred_pace: { type: 'text' },
|
||||
preferred_budget_level: { type: 'text' },
|
||||
max_walking_distance_km: { type: 'numeric' },
|
||||
preferred_start_time: { type: 'text' },
|
||||
child_friendly_preferred: { type: 'boolean', notNull: true, default: false },
|
||||
interests: { type: 'text[]', notNull: true, default: '{}' },
|
||||
notes: { type: 'text' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('user_preferences');
|
||||
};
|
||||
26
backend/migrations/1700000003000_create-trips.cjs
Normal file
26
backend/migrations/1700000003000_create-trips.cjs
Normal file
@@ -0,0 +1,26 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('trips', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
name: { type: 'text', notNull: true },
|
||||
description: { type: 'text' },
|
||||
owner_id: { type: 'uuid', notNull: true, references: 'users(id)' },
|
||||
start_date: { type: 'date' },
|
||||
end_date: { type: 'date' },
|
||||
status: { type: 'text', notNull: true, default: 'DRAFT' },
|
||||
planning_stage: { type: 'text' },
|
||||
currency: { type: 'text', notNull: true, default: 'EUR' },
|
||||
version: { type: 'integer', notNull: true, default: 1 },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint(
|
||||
'trips',
|
||||
'trips_status_check',
|
||||
"CHECK (status IN ('DRAFT','PLANNING','BOOKING','UPCOMING','ACTIVE','COMPLETED','ARCHIVED'))",
|
||||
);
|
||||
pgm.createIndex('trips', 'owner_id');
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('trips');
|
||||
};
|
||||
23
backend/migrations/1700000004000_create-trip-settings.cjs
Normal file
23
backend/migrations/1700000004000_create-trip-settings.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('trip_settings', {
|
||||
trip_id: {
|
||||
type: 'uuid',
|
||||
primaryKey: true,
|
||||
notNull: true,
|
||||
references: 'trips(id)',
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
web_research_enabled: { type: 'boolean', notNull: true, default: false },
|
||||
periodic_agent_review_enabled: { type: 'boolean', notNull: true, default: false },
|
||||
notification_email_enabled: { type: 'boolean', notNull: true, default: true },
|
||||
notification_push_enabled: { type: 'boolean', notNull: true, default: true },
|
||||
default_research_depth: { type: 'text' },
|
||||
default_planning_style: { type: 'text' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('trip_settings');
|
||||
};
|
||||
25
backend/migrations/1700000005000_create-trip-members.cjs
Normal file
25
backend/migrations/1700000005000_create-trip-members.cjs
Normal file
@@ -0,0 +1,25 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('trip_members', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
trip_id: { type: 'uuid', notNull: true, references: 'trips(id)', onDelete: 'CASCADE' },
|
||||
user_id: { type: 'uuid', notNull: true, references: 'users(id)' },
|
||||
role: { type: 'text', notNull: true },
|
||||
status: { type: 'text', notNull: true },
|
||||
joined_at: { type: 'timestamptz' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint('trip_members', 'trip_members_trip_user_key', 'UNIQUE(trip_id, user_id)');
|
||||
pgm.addConstraint('trip_members', 'trip_members_role_check', "CHECK (role IN ('OWNER','MEMBER'))");
|
||||
pgm.addConstraint(
|
||||
'trip_members',
|
||||
'trip_members_status_check',
|
||||
"CHECK (status IN ('INVITED','ACTIVE','DECLINED'))",
|
||||
);
|
||||
pgm.createIndex('trip_members', 'trip_id');
|
||||
pgm.createIndex('trip_members', 'user_id');
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('trip_members');
|
||||
};
|
||||
20
backend/migrations/1700000006000_create-trip-invitations.cjs
Normal file
20
backend/migrations/1700000006000_create-trip-invitations.cjs
Normal file
@@ -0,0 +1,20 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('trip_invitations', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
trip_id: { type: 'uuid', notNull: true, references: 'trips(id)', onDelete: 'CASCADE' },
|
||||
email: { type: 'text', notNull: true },
|
||||
invited_by_user_id: { type: 'uuid', notNull: true, references: 'users(id)' },
|
||||
token_hash: { type: 'text', notNull: true },
|
||||
expires_at: { type: 'timestamptz', notNull: true },
|
||||
accepted_at: { type: 'timestamptz' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint('trip_invitations', 'trip_invitations_token_hash_key', 'UNIQUE(token_hash)');
|
||||
pgm.createIndex('trip_invitations', 'trip_id');
|
||||
pgm.sql('CREATE INDEX trip_invitations_lower_email_idx ON trip_invitations (lower(email))');
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('trip_invitations');
|
||||
};
|
||||
22
backend/migrations/1700000007000_create-travelers.cjs
Normal file
22
backend/migrations/1700000007000_create-travelers.cjs
Normal file
@@ -0,0 +1,22 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('travelers', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
trip_id: { type: 'uuid', notNull: true, references: 'trips(id)', onDelete: 'CASCADE' },
|
||||
linked_user_id: { type: 'uuid', references: 'users(id)' },
|
||||
display_name: { type: 'text', notNull: true },
|
||||
traveler_type: { type: 'text', notNull: true },
|
||||
created_by_user_id: { type: 'uuid', notNull: true, references: 'users(id)' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint(
|
||||
'travelers',
|
||||
'travelers_traveler_type_check',
|
||||
"CHECK (traveler_type IN ('ADULT','CHILD','INFANT'))",
|
||||
);
|
||||
pgm.createIndex('travelers', 'trip_id');
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('travelers');
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('trip_preference_overrides', {
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
trip_id: { type: 'uuid', notNull: true, references: 'trips(id)', onDelete: 'CASCADE' },
|
||||
user_id: { type: 'uuid', references: 'users(id)' },
|
||||
traveler_id: { type: 'uuid', references: 'travelers(id)', onDelete: 'CASCADE' },
|
||||
overrides: { type: 'jsonb', notNull: true, default: '{}' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
});
|
||||
pgm.addConstraint(
|
||||
'trip_preference_overrides',
|
||||
'trip_preference_overrides_subject_check',
|
||||
'CHECK (num_nonnulls(user_id, traveler_id) = 1)',
|
||||
);
|
||||
pgm.createIndex('trip_preference_overrides', ['trip_id', 'user_id'], {
|
||||
unique: true,
|
||||
where: 'user_id IS NOT NULL',
|
||||
name: 'trip_pref_overrides_trip_user_uq',
|
||||
});
|
||||
pgm.createIndex('trip_preference_overrides', ['trip_id', 'traveler_id'], {
|
||||
unique: true,
|
||||
where: 'traveler_id IS NOT NULL',
|
||||
name: 'trip_pref_overrides_trip_traveler_uq',
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.dropTable('trip_preference_overrides');
|
||||
};
|
||||
Reference in New Issue
Block a user