This commit is contained in:
Iliyan Angelov
2025-11-21 01:20:51 +02:00
parent a38ab4fa82
commit 6f85b8cf17
242 changed files with 7154 additions and 14492 deletions

View File

@@ -6,65 +6,31 @@ import os
import sys
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
# Import models and Base
from src.config.database import Base
from src.config.settings import settings
from src.models import * # Import all models
# this is the Alembic Config object
from src.models import *
config = context.config
# Interpret the config file for Python logging.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# Get database URL from settings
database_url = settings.database_url
config.set_main_option("sqlalchemy.url", database_url)
# add your model's MetaData object here
config.set_main_option('sqlalchemy.url', database_url)
target_metadata = Base.metadata
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode."""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
url = config.get_main_option('sqlalchemy.url')
context.configure(url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={'paramstyle': 'named'})
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
connectable = engine_from_config(config.get_section(config.config_ini_section, {}), prefix='sqlalchemy.', poolclass=pool.NullPool)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
run_migrations_online()

View File

@@ -1,31 +1,16 @@
"""add_mfa_fields_to_users
Revision ID: 08e2f866e131
Revises: add_badges_to_page_content
Create Date: 2025-11-19 11:13:30.376194
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '08e2f866e131'
down_revision = 'add_badges_to_page_content'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add MFA fields to users table
op.add_column('users', sa.Column('mfa_enabled', sa.Boolean(), nullable=False, server_default='0'))
op.add_column('users', sa.Column('mfa_secret', sa.String(255), nullable=True))
op.add_column('users', sa.Column('mfa_backup_codes', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove MFA fields from users table
op.drop_column('users', 'mfa_backup_codes')
op.drop_column('users', 'mfa_secret')
op.drop_column('users', 'mfa_enabled')
op.drop_column('users', 'mfa_enabled')

View File

@@ -1,23 +1,12 @@
"""add_section_title_fields_to_page_content
Revision ID: 1444eb61188e
Revises: ff515d77abbe
Create Date: 2025-11-20 15:51:29.671843
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '1444eb61188e'
down_revision = 'ff515d77abbe'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index('bookings_room_id', table_name='bookings')
op.drop_index('bookings_status', table_name='bookings')
op.drop_index('bookings_user_id', table_name='bookings')
@@ -57,10 +46,7 @@ def upgrade() -> None:
op.add_column('page_contents', sa.Column('awards_section_subtitle', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('partners_section_title', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('partners_section_subtitle', sa.Text(), nullable=True))
op.alter_column('password_reset_tokens', 'used',
existing_type=mysql.TINYINT(display_width=1),
nullable=False,
existing_server_default=sa.text("'0'"))
op.alter_column('password_reset_tokens', 'used', existing_type=mysql.TINYINT(display_width=1), nullable=False, existing_server_default=sa.text("'0'"))
op.drop_index('password_reset_tokens_token', table_name='password_reset_tokens')
op.drop_index('password_reset_tokens_user_id', table_name='password_reset_tokens')
op.drop_index('token', table_name='password_reset_tokens')
@@ -68,11 +54,7 @@ def upgrade() -> None:
op.create_index(op.f('ix_password_reset_tokens_token'), 'password_reset_tokens', ['token'], unique=True)
op.drop_constraint('password_reset_tokens_ibfk_1', 'password_reset_tokens', type_='foreignkey')
op.create_foreign_key(None, 'password_reset_tokens', 'users', ['user_id'], ['id'])
op.alter_column('payments', 'deposit_percentage',
existing_type=mysql.INTEGER(),
comment=None,
existing_comment='Percentage of deposit (e.g., 20, 30, 50)',
existing_nullable=True)
op.alter_column('payments', 'deposit_percentage', existing_type=mysql.INTEGER(), comment=None, existing_comment='Percentage of deposit (e.g., 20, 30, 50)', existing_nullable=True)
op.drop_index('payments_booking_id', table_name='payments')
op.drop_index('payments_payment_status', table_name='payments')
op.create_index(op.f('ix_payments_id'), 'payments', ['id'], unique=False)
@@ -128,11 +110,8 @@ def upgrade() -> None:
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
op.drop_constraint('users_ibfk_1', 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'roles', ['role_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'users', type_='foreignkey')
op.create_foreign_key('users_ibfk_1', 'users', 'roles', ['role_id'], ['id'], onupdate='CASCADE', ondelete='RESTRICT')
op.drop_index(op.f('ix_users_id'), table_name='users')
@@ -188,10 +167,7 @@ def downgrade() -> None:
op.drop_index(op.f('ix_payments_id'), table_name='payments')
op.create_index('payments_payment_status', 'payments', ['payment_status'], unique=False)
op.create_index('payments_booking_id', 'payments', ['booking_id'], unique=False)
op.alter_column('payments', 'deposit_percentage',
existing_type=mysql.INTEGER(),
comment='Percentage of deposit (e.g., 20, 30, 50)',
existing_nullable=True)
op.alter_column('payments', 'deposit_percentage', existing_type=mysql.INTEGER(), comment='Percentage of deposit (e.g., 20, 30, 50)', existing_nullable=True)
op.drop_constraint(None, 'password_reset_tokens', type_='foreignkey')
op.create_foreign_key('password_reset_tokens_ibfk_1', 'password_reset_tokens', 'users', ['user_id'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
op.drop_index(op.f('ix_password_reset_tokens_token'), table_name='password_reset_tokens')
@@ -199,10 +175,7 @@ def downgrade() -> None:
op.create_index('token', 'password_reset_tokens', ['token'], unique=False)
op.create_index('password_reset_tokens_user_id', 'password_reset_tokens', ['user_id'], unique=False)
op.create_index('password_reset_tokens_token', 'password_reset_tokens', ['token'], unique=False)
op.alter_column('password_reset_tokens', 'used',
existing_type=mysql.TINYINT(display_width=1),
nullable=True,
existing_server_default=sa.text("'0'"))
op.alter_column('password_reset_tokens', 'used', existing_type=mysql.TINYINT(display_width=1), nullable=True, existing_server_default=sa.text("'0'"))
op.drop_column('page_contents', 'partners_section_subtitle')
op.drop_column('page_contents', 'partners_section_title')
op.drop_column('page_contents', 'awards_section_subtitle')
@@ -241,6 +214,4 @@ def downgrade() -> None:
op.create_index('ix_bookings_promotion_code', 'bookings', ['promotion_code'], unique=False)
op.create_index('bookings_user_id', 'bookings', ['user_id'], unique=False)
op.create_index('bookings_status', 'bookings', ['status'], unique=False)
op.create_index('bookings_room_id', 'bookings', ['room_id'], unique=False)
# ### end Alembic commands ###
op.create_index('bookings_room_id', 'bookings', ['room_id'], unique=False)

View File

@@ -1,62 +1,18 @@
"""add_page_content_table
Revision ID: 163657e72e93
Revises: 6a126cc5b23c
Create Date: 2025-11-18 18:02:03.480951
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '163657e72e93'
down_revision = '6a126cc5b23c'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# Only create the page_contents table, skip other schema changes
op.create_table('page_contents',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('page_type', sa.Enum('home', 'contact', 'about', 'footer', 'seo', name='pagetype'), nullable=False),
sa.Column('title', sa.String(length=500), nullable=True),
sa.Column('subtitle', sa.String(length=1000), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('content', sa.Text(), nullable=True),
sa.Column('meta_title', sa.String(length=500), nullable=True),
sa.Column('meta_description', sa.Text(), nullable=True),
sa.Column('meta_keywords', sa.String(length=1000), nullable=True),
sa.Column('og_title', sa.String(length=500), nullable=True),
sa.Column('og_description', sa.Text(), nullable=True),
sa.Column('og_image', sa.String(length=1000), nullable=True),
sa.Column('canonical_url', sa.String(length=1000), nullable=True),
sa.Column('contact_info', sa.Text(), nullable=True),
sa.Column('social_links', sa.Text(), nullable=True),
sa.Column('footer_links', sa.Text(), nullable=True),
sa.Column('hero_title', sa.String(length=500), nullable=True),
sa.Column('hero_subtitle', sa.String(length=1000), nullable=True),
sa.Column('hero_image', sa.String(length=1000), nullable=True),
sa.Column('story_content', sa.Text(), nullable=True),
sa.Column('values', sa.Text(), nullable=True),
sa.Column('features', sa.Text(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_table('page_contents', sa.Column('id', sa.Integer(), nullable=False), sa.Column('page_type', sa.Enum('home', 'contact', 'about', 'footer', 'seo', name='pagetype'), nullable=False), sa.Column('title', sa.String(length=500), nullable=True), sa.Column('subtitle', sa.String(length=1000), nullable=True), sa.Column('description', sa.Text(), nullable=True), sa.Column('content', sa.Text(), nullable=True), sa.Column('meta_title', sa.String(length=500), nullable=True), sa.Column('meta_description', sa.Text(), nullable=True), sa.Column('meta_keywords', sa.String(length=1000), nullable=True), sa.Column('og_title', sa.String(length=500), nullable=True), sa.Column('og_description', sa.Text(), nullable=True), sa.Column('og_image', sa.String(length=1000), nullable=True), sa.Column('canonical_url', sa.String(length=1000), nullable=True), sa.Column('contact_info', sa.Text(), nullable=True), sa.Column('social_links', sa.Text(), nullable=True), sa.Column('footer_links', sa.Text(), nullable=True), sa.Column('hero_title', sa.String(length=500), nullable=True), sa.Column('hero_subtitle', sa.String(length=1000), nullable=True), sa.Column('hero_image', sa.String(length=1000), nullable=True), sa.Column('story_content', sa.Text(), nullable=True), sa.Column('values', sa.Text(), nullable=True), sa.Column('features', sa.Text(), nullable=True), sa.Column('is_active', sa.Boolean(), nullable=False), sa.Column('created_at', sa.DateTime(), nullable=False), sa.Column('updated_at', sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint('id'))
op.create_index(op.f('ix_page_contents_id'), 'page_contents', ['id'], unique=False)
op.create_index(op.f('ix_page_contents_page_type'), 'page_contents', ['page_type'], unique=True)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_page_contents_page_type'), table_name='page_contents')
op.drop_index(op.f('ix_page_contents_id'), table_name='page_contents')
op.drop_table('page_contents')
op.execute("DROP TYPE IF EXISTS pagetype")
# ### end Alembic commands ###
op.execute('DROP TYPE IF EXISTS pagetype')

View File

@@ -1,38 +1,22 @@
"""add_luxury_section_fields_to_page_content
Revision ID: 17efc6439cc3
Revises: bfa74be4b256
Create Date: 2025-11-20 13:37:20.015422
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '17efc6439cc3'
down_revision = 'bfa74be4b256'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add luxury section fields to page_contents table
# Use TEXT instead of VARCHAR to avoid MySQL row size limits
op.add_column('page_contents', sa.Column('luxury_section_title', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_section_subtitle', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_section_image', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_features', sa.Text(), nullable=True)) # JSON array
op.add_column('page_contents', sa.Column('luxury_gallery', sa.Text(), nullable=True)) # JSON array of image URLs
op.add_column('page_contents', sa.Column('luxury_testimonials', sa.Text(), nullable=True)) # JSON array of testimonials
op.add_column('page_contents', sa.Column('luxury_features', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_gallery', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_testimonials', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove luxury section fields
op.drop_column('page_contents', 'luxury_testimonials')
op.drop_column('page_contents', 'luxury_gallery')
op.drop_column('page_contents', 'luxury_features')
op.drop_column('page_contents', 'luxury_section_image')
op.drop_column('page_contents', 'luxury_section_subtitle')
op.drop_column('page_contents', 'luxury_section_title')
op.drop_column('page_contents', 'luxury_section_title')

View File

@@ -1,39 +1,13 @@
"""Initial migration: create all tables with indexes
Revision ID: 59baf2338f8a
Revises:
Create Date: 2025-11-16 16:03:26.313117
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '59baf2338f8a'
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('audit_logs',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('action', sa.String(length=100), nullable=False),
sa.Column('resource_type', sa.String(length=50), nullable=False),
sa.Column('resource_id', sa.Integer(), nullable=True),
sa.Column('ip_address', sa.String(length=45), nullable=True),
sa.Column('user_agent', sa.String(length=255), nullable=True),
sa.Column('request_id', sa.String(length=36), nullable=True),
sa.Column('details', sa.JSON(), nullable=True),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('error_message', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('audit_logs', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('user_id', sa.Integer(), nullable=True), sa.Column('action', sa.String(length=100), nullable=False), sa.Column('resource_type', sa.String(length=50), nullable=False), sa.Column('resource_id', sa.Integer(), nullable=True), sa.Column('ip_address', sa.String(length=45), nullable=True), sa.Column('user_agent', sa.String(length=255), nullable=True), sa.Column('request_id', sa.String(length=36), nullable=True), sa.Column('details', sa.JSON(), nullable=True), sa.Column('status', sa.String(length=20), nullable=False), sa.Column('error_message', sa.Text(), nullable=True), sa.Column('created_at', sa.DateTime(), nullable=False), sa.ForeignKeyConstraint(['user_id'], ['users.id']), sa.PrimaryKeyConstraint('id'))
op.create_index(op.f('ix_audit_logs_action'), 'audit_logs', ['action'], unique=False)
op.create_index(op.f('ix_audit_logs_created_at'), 'audit_logs', ['created_at'], unique=False)
op.create_index(op.f('ix_audit_logs_id'), 'audit_logs', ['id'], unique=False)
@@ -46,7 +20,6 @@ def upgrade() -> None:
op.drop_index('banners_is_active', table_name='banners')
op.drop_index('banners_position', table_name='banners')
op.create_index(op.f('ix_banners_id'), 'banners', ['id'], unique=False)
# Drop foreign keys first, then indexes
op.drop_constraint('bookings_ibfk_2', 'bookings', type_='foreignkey')
op.drop_constraint('bookings_ibfk_1', 'bookings', type_='foreignkey')
op.drop_index('booking_number', table_name='bookings')
@@ -60,7 +33,6 @@ def upgrade() -> None:
op.create_index(op.f('ix_bookings_id'), 'bookings', ['id'], unique=False)
op.create_foreign_key(None, 'bookings', 'users', ['user_id'], ['id'])
op.create_foreign_key(None, 'bookings', 'rooms', ['room_id'], ['id'])
# Drop foreign keys first, then indexes
op.drop_constraint('checkin_checkout_ibfk_1', 'checkin_checkout', type_='foreignkey')
op.drop_constraint('checkin_checkout_ibfk_2', 'checkin_checkout', type_='foreignkey')
op.drop_constraint('checkin_checkout_ibfk_3', 'checkin_checkout', type_='foreignkey')
@@ -70,7 +42,6 @@ def upgrade() -> None:
op.create_foreign_key(None, 'checkin_checkout', 'bookings', ['booking_id'], ['id'])
op.create_foreign_key(None, 'checkin_checkout', 'users', ['checkout_by'], ['id'])
op.create_foreign_key(None, 'checkin_checkout', 'users', ['checkin_by'], ['id'])
# Drop foreign keys first, then indexes
op.drop_constraint('favorites_ibfk_2', 'favorites', type_='foreignkey')
op.drop_constraint('favorites_ibfk_1', 'favorites', type_='foreignkey')
op.drop_index('favorites_room_id', table_name='favorites')
@@ -79,11 +50,7 @@ def upgrade() -> None:
op.create_index(op.f('ix_favorites_id'), 'favorites', ['id'], unique=False)
op.create_foreign_key(None, 'favorites', 'users', ['user_id'], ['id'])
op.create_foreign_key(None, 'favorites', 'rooms', ['room_id'], ['id'])
op.alter_column('password_reset_tokens', 'used',
existing_type=mysql.TINYINT(display_width=1),
nullable=False,
existing_server_default=sa.text("'0'"))
# Drop foreign key first, then indexes
op.alter_column('password_reset_tokens', 'used', existing_type=mysql.TINYINT(display_width=1), nullable=False, existing_server_default=sa.text("'0'"))
op.drop_constraint('password_reset_tokens_ibfk_1', 'password_reset_tokens', type_='foreignkey')
op.drop_index('password_reset_tokens_token', table_name='password_reset_tokens')
op.drop_index('password_reset_tokens_user_id', table_name='password_reset_tokens')
@@ -91,12 +58,7 @@ def upgrade() -> None:
op.create_index(op.f('ix_password_reset_tokens_id'), 'password_reset_tokens', ['id'], unique=False)
op.create_index(op.f('ix_password_reset_tokens_token'), 'password_reset_tokens', ['token'], unique=True)
op.create_foreign_key(None, 'password_reset_tokens', 'users', ['user_id'], ['id'])
op.alter_column('payments', 'deposit_percentage',
existing_type=mysql.INTEGER(),
comment=None,
existing_comment='Percentage of deposit (e.g., 20, 30, 50)',
existing_nullable=True)
# Drop foreign keys first, then indexes
op.alter_column('payments', 'deposit_percentage', existing_type=mysql.INTEGER(), comment=None, existing_comment='Percentage of deposit (e.g., 20, 30, 50)', existing_nullable=True)
op.drop_constraint('payments_related_payment_id_foreign_idx', 'payments', type_='foreignkey')
op.drop_constraint('payments_ibfk_1', 'payments', type_='foreignkey')
op.drop_index('payments_booking_id', table_name='payments')
@@ -109,7 +71,6 @@ def upgrade() -> None:
op.drop_index('promotions_is_active', table_name='promotions')
op.create_index(op.f('ix_promotions_code'), 'promotions', ['code'], unique=True)
op.create_index(op.f('ix_promotions_id'), 'promotions', ['id'], unique=False)
# Drop foreign key first, then indexes
op.drop_constraint('refresh_tokens_ibfk_1', 'refresh_tokens', type_='foreignkey')
op.drop_index('refresh_tokens_token', table_name='refresh_tokens')
op.drop_index('refresh_tokens_user_id', table_name='refresh_tokens')
@@ -117,7 +78,6 @@ def upgrade() -> None:
op.create_index(op.f('ix_refresh_tokens_id'), 'refresh_tokens', ['id'], unique=False)
op.create_index(op.f('ix_refresh_tokens_token'), 'refresh_tokens', ['token'], unique=True)
op.create_foreign_key(None, 'refresh_tokens', 'users', ['user_id'], ['id'])
# Drop foreign keys first, then indexes
op.drop_constraint('reviews_ibfk_2', 'reviews', type_='foreignkey')
op.drop_constraint('reviews_ibfk_1', 'reviews', type_='foreignkey')
op.drop_index('reviews_room_id', table_name='reviews')
@@ -130,7 +90,6 @@ def upgrade() -> None:
op.create_index(op.f('ix_roles_id'), 'roles', ['id'], unique=False)
op.create_index(op.f('ix_roles_name'), 'roles', ['name'], unique=True)
op.create_index(op.f('ix_room_types_id'), 'room_types', ['id'], unique=False)
# Drop foreign key first, then indexes
op.drop_constraint('rooms_ibfk_1', 'rooms', type_='foreignkey')
op.drop_index('room_number', table_name='rooms')
op.drop_index('rooms_featured', table_name='rooms')
@@ -139,7 +98,6 @@ def upgrade() -> None:
op.create_index(op.f('ix_rooms_id'), 'rooms', ['id'], unique=False)
op.create_index(op.f('ix_rooms_room_number'), 'rooms', ['room_number'], unique=True)
op.create_foreign_key(None, 'rooms', 'room_types', ['room_type_id'], ['id'])
# Drop foreign keys first, then indexes
op.drop_constraint('service_usages_ibfk_1', 'service_usages', type_='foreignkey')
op.drop_constraint('service_usages_ibfk_2', 'service_usages', type_='foreignkey')
op.drop_index('service_usages_booking_id', table_name='service_usages')
@@ -149,7 +107,6 @@ def upgrade() -> None:
op.create_foreign_key(None, 'service_usages', 'services', ['service_id'], ['id'])
op.drop_index('services_category', table_name='services')
op.create_index(op.f('ix_services_id'), 'services', ['id'], unique=False)
# Drop foreign key first, then indexes
op.drop_constraint('users_ibfk_1', 'users', type_='foreignkey')
op.drop_index('email', table_name='users')
op.drop_index('users_email', table_name='users')
@@ -157,11 +114,8 @@ def upgrade() -> None:
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
op.create_foreign_key(None, 'users', 'roles', ['role_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'users', type_='foreignkey')
op.create_foreign_key('users_ibfk_1', 'users', 'roles', ['role_id'], ['id'], onupdate='CASCADE', ondelete='RESTRICT')
op.drop_index(op.f('ix_users_id'), table_name='users')
@@ -217,10 +171,7 @@ def downgrade() -> None:
op.drop_index(op.f('ix_payments_id'), table_name='payments')
op.create_index('payments_payment_status', 'payments', ['payment_status'], unique=False)
op.create_index('payments_booking_id', 'payments', ['booking_id'], unique=False)
op.alter_column('payments', 'deposit_percentage',
existing_type=mysql.INTEGER(),
comment='Percentage of deposit (e.g., 20, 30, 50)',
existing_nullable=True)
op.alter_column('payments', 'deposit_percentage', existing_type=mysql.INTEGER(), comment='Percentage of deposit (e.g., 20, 30, 50)', existing_nullable=True)
op.drop_constraint(None, 'password_reset_tokens', type_='foreignkey')
op.create_foreign_key('password_reset_tokens_ibfk_1', 'password_reset_tokens', 'users', ['user_id'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
op.drop_index(op.f('ix_password_reset_tokens_token'), table_name='password_reset_tokens')
@@ -228,10 +179,7 @@ def downgrade() -> None:
op.create_index('token', 'password_reset_tokens', ['token'], unique=False)
op.create_index('password_reset_tokens_user_id', 'password_reset_tokens', ['user_id'], unique=False)
op.create_index('password_reset_tokens_token', 'password_reset_tokens', ['token'], unique=False)
op.alter_column('password_reset_tokens', 'used',
existing_type=mysql.TINYINT(display_width=1),
nullable=True,
existing_server_default=sa.text("'0'"))
op.alter_column('password_reset_tokens', 'used', existing_type=mysql.TINYINT(display_width=1), nullable=True, existing_server_default=sa.text("'0'"))
op.drop_constraint(None, 'favorites', type_='foreignkey')
op.drop_constraint(None, 'favorites', type_='foreignkey')
op.create_foreign_key('favorites_ibfk_1', 'favorites', 'users', ['user_id'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
@@ -265,13 +213,7 @@ def downgrade() -> None:
op.drop_index(op.f('ix_banners_id'), table_name='banners')
op.create_index('banners_position', 'banners', ['position'], unique=False)
op.create_index('banners_is_active', 'banners', ['is_active'], unique=False)
op.create_table('SequelizeMeta',
sa.Column('name', mysql.VARCHAR(collation='utf8mb3_unicode_ci', length=255), nullable=False),
sa.PrimaryKeyConstraint('name'),
mysql_collate='utf8mb3_unicode_ci',
mysql_default_charset='utf8mb3',
mysql_engine='InnoDB'
)
op.create_table('SequelizeMeta', sa.Column('name', mysql.VARCHAR(collation='utf8mb3_unicode_ci', length=255), nullable=False), sa.PrimaryKeyConstraint('name'), mysql_collate='utf8mb3_unicode_ci', mysql_default_charset='utf8mb3', mysql_engine='InnoDB')
op.create_index('name', 'SequelizeMeta', ['name'], unique=False)
op.drop_index(op.f('ix_audit_logs_user_id'), table_name='audit_logs')
op.drop_index(op.f('ix_audit_logs_resource_type'), table_name='audit_logs')
@@ -280,6 +222,4 @@ def downgrade() -> None:
op.drop_index(op.f('ix_audit_logs_id'), table_name='audit_logs')
op.drop_index(op.f('ix_audit_logs_created_at'), table_name='audit_logs')
op.drop_index(op.f('ix_audit_logs_action'), table_name='audit_logs')
op.drop_table('audit_logs')
# ### end Alembic commands ###
op.drop_table('audit_logs')

View File

@@ -1,29 +1,16 @@
"""add_capacity_room_size_view_to_rooms
Revision ID: 6a126cc5b23c
Revises: add_stripe_payment_method
Create Date: 2025-11-17 16:25:09.581786
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6a126cc5b23c'
down_revision = 'add_stripe_payment_method'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add the three new columns to rooms table
op.add_column('rooms', sa.Column('capacity', sa.Integer(), nullable=True))
op.add_column('rooms', sa.Column('room_size', sa.String(length=50), nullable=True))
op.add_column('rooms', sa.Column('view', sa.String(length=100), nullable=True))
def downgrade() -> None:
# Remove the three columns from rooms table
op.drop_column('rooms', 'view')
op.drop_column('rooms', 'room_size')
op.drop_column('rooms', 'capacity')
op.drop_column('rooms', 'capacity')

View File

@@ -1,60 +1,29 @@
"""add_system_settings_table
Revision ID: 96c23dad405d
Revises: 59baf2338f8a
Create Date: 2025-11-17 11:51:28.369031
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '96c23dad405d'
down_revision = '59baf2338f8a'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Create system_settings table (if it doesn't exist)
from sqlalchemy import inspect
bind = op.get_bind()
inspector = inspect(bind)
tables = inspector.get_table_names()
if 'system_settings' not in tables:
op.create_table('system_settings',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('key', sa.String(length=100), nullable=False),
sa.Column('value', sa.Text(), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('updated_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['updated_by_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('system_settings', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('key', sa.String(length=100), nullable=False), sa.Column('value', sa.Text(), nullable=False), sa.Column('description', sa.Text(), nullable=True), sa.Column('updated_at', sa.DateTime(), nullable=False), sa.Column('updated_by_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['updated_by_id'], ['users.id']), sa.PrimaryKeyConstraint('id'))
op.create_index(op.f('ix_system_settings_id'), 'system_settings', ['id'], unique=False)
op.create_index(op.f('ix_system_settings_key'), 'system_settings', ['key'], unique=True)
# Add currency column to users table (if it doesn't exist)
columns = [col['name'] for col in inspector.get_columns('users')]
if 'currency' not in columns:
op.add_column('users', sa.Column('currency', sa.String(length=3), nullable=False, server_default='VND'))
# ### end Alembic commands ###
def downgrade() -> None:
# Drop currency column from users table
try:
op.drop_column('users', 'currency')
except Exception:
# Column might not exist, skip
pass
# Drop system_settings table
op.drop_index(op.f('ix_system_settings_key'), table_name='system_settings')
op.drop_index(op.f('ix_system_settings_id'), table_name='system_settings')
op.drop_table('system_settings')
# ### end Alembic commands ###
op.drop_table('system_settings')

View File

@@ -1,22 +1,11 @@
"""add_about_page_fields
Revision ID: f2a3b4c5d6e7
Revises: a1b2c3d4e5f6
Create Date: 2025-11-20 17:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f2a3b4c5d6e7'
down_revision = 'a1b2c3d4e5f6'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add about page specific fields (all as TEXT to avoid row size issues)
op.add_column('page_contents', sa.Column('about_hero_image', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('mission', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('vision', sa.Text(), nullable=True))
@@ -24,13 +13,10 @@ def upgrade() -> None:
op.add_column('page_contents', sa.Column('timeline', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('achievements', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove about page specific fields
op.drop_column('page_contents', 'achievements')
op.drop_column('page_contents', 'timeline')
op.drop_column('page_contents', 'team')
op.drop_column('page_contents', 'vision')
op.drop_column('page_contents', 'mission')
op.drop_column('page_contents', 'about_hero_image')
op.drop_column('page_contents', 'about_hero_image')

View File

@@ -1,26 +1,12 @@
"""add_badges_to_page_content
Revision ID: add_badges_to_page_content
Revises: cce764ef7a50
Create Date: 2025-01-14 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'add_badges_to_page_content'
down_revision = 'cce764ef7a50'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add badges column to page_contents table
op.add_column('page_contents', sa.Column('badges', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove badges column from page_contents table
op.drop_column('page_contents', 'badges')
op.drop_column('page_contents', 'badges')

View File

@@ -1,26 +1,12 @@
"""add_copyright_text_to_page_content
Revision ID: a1b2c3d4e5f6
Revises: ff515d77abbe
Create Date: 2025-11-20 16:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a1b2c3d4e5f6'
down_revision = '1444eb61188e'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add copyright_text column to page_contents table
op.add_column('page_contents', sa.Column('copyright_text', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove copyright_text column from page_contents table
op.drop_column('page_contents', 'copyright_text')
op.drop_column('page_contents', 'copyright_text')

View File

@@ -1,50 +1,19 @@
"""add_stripe_payment_method
Revision ID: add_stripe_payment_method
Revises: 96c23dad405d
Create Date: 2025-01-17 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'add_stripe_payment_method'
down_revision = '96c23dad405d'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Note: MySQL ENUM modifications can be tricky.
# If payments table already has data with existing enum values,
# we need to preserve them when adding 'stripe'
# For MySQL, we need to alter the ENUM column to include the new value
# Check if we're using MySQL
bind = op.get_bind()
if bind.dialect.name == 'mysql':
# Alter the ENUM column to include 'stripe'
# This preserves existing values and adds 'stripe'
op.execute(
"ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe') NOT NULL"
)
op.execute("ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe') NOT NULL")
else:
# For other databases (PostgreSQL, SQLite), enum changes are handled differently
# For SQLite, this might not be needed as it doesn't enforce enum constraints
pass
# ### end Alembic commands ###
def downgrade() -> None:
# Remove 'stripe' from the ENUM (be careful if there are existing stripe payments)
bind = op.get_bind()
if bind.dialect.name == 'mysql':
# First, check if there are any stripe payments - if so, this will fail
# In production, you'd want to migrate existing stripe payments first
op.execute(
"ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet') NOT NULL"
)
# ### end Alembic commands ###
op.execute("ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet') NOT NULL")

View File

@@ -1,34 +1,18 @@
"""add_promotion_fields_to_bookings
Revision ID: bd309b0742c1
Revises: f1a2b3c4d5e6
Create Date: 2025-11-20 02:16:09.496685
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bd309b0742c1'
down_revision = 'f1a2b3c4d5e6'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add promotion-related columns to bookings table
op.add_column('bookings', sa.Column('original_price', sa.Numeric(10, 2), nullable=True))
op.add_column('bookings', sa.Column('discount_amount', sa.Numeric(10, 2), nullable=True, server_default='0'))
op.add_column('bookings', sa.Column('promotion_code', sa.String(50), nullable=True))
# Add index on promotion_code for faster lookups
op.create_index(op.f('ix_bookings_promotion_code'), 'bookings', ['promotion_code'], unique=False)
def downgrade() -> None:
# Remove promotion-related columns
op.drop_index(op.f('ix_bookings_promotion_code'), table_name='bookings')
op.drop_column('bookings', 'promotion_code')
op.drop_column('bookings', 'discount_amount')
op.drop_column('bookings', 'original_price')
op.drop_column('bookings', 'original_price')

View File

@@ -1,23 +1,11 @@
"""add_luxury_content_fields_to_page_content
Revision ID: bfa74be4b256
Revises: bd309b0742c1
Create Date: 2025-11-20 13:27:52.106013
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bfa74be4b256'
down_revision = 'bd309b0742c1'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add luxury content fields to page_contents table
op.add_column('page_contents', sa.Column('amenities_section_title', sa.String(500), nullable=True))
op.add_column('page_contents', sa.Column('amenities_section_subtitle', sa.String(1000), nullable=True))
op.add_column('page_contents', sa.Column('amenities', sa.Text(), nullable=True))
@@ -33,9 +21,7 @@ def upgrade() -> None:
op.add_column('page_contents', sa.Column('about_preview_image', sa.String(1000), nullable=True))
op.add_column('page_contents', sa.Column('stats', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove luxury content fields
op.drop_column('page_contents', 'stats')
op.drop_column('page_contents', 'about_preview_image')
op.drop_column('page_contents', 'about_preview_content')
@@ -49,5 +35,4 @@ def downgrade() -> None:
op.drop_column('page_contents', 'testimonials_section_title')
op.drop_column('page_contents', 'amenities')
op.drop_column('page_contents', 'amenities_section_subtitle')
op.drop_column('page_contents', 'amenities_section_title')
op.drop_column('page_contents', 'amenities_section_title')

View File

@@ -1,28 +1,12 @@
"""add_map_url_to_page_content
Revision ID: cce764ef7a50
Revises: 163657e72e93
Create Date: 2025-11-18 18:11:41.071053
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cce764ef7a50'
down_revision = '163657e72e93'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# Only add the map_url column to page_contents table
op.add_column('page_contents', sa.Column('map_url', sa.String(length=1000), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('page_contents', 'map_url')
# ### end Alembic commands ###
op.drop_column('page_contents', 'map_url')

View File

@@ -1,51 +1,19 @@
"""add_paypal_payment_method
Revision ID: d9aff6c5f0d4
Revises: 08e2f866e131
Create Date: 2025-11-19 12:07:50.703320
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'd9aff6c5f0d4'
down_revision = '08e2f866e131'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Note: MySQL ENUM modifications can be tricky.
# If payments table already has data with existing enum values,
# we need to preserve them when adding 'paypal'
# For MySQL, we need to alter the ENUM column to include the new value
# Check if we're using MySQL
bind = op.get_bind()
if bind.dialect.name == 'mysql':
# Alter the ENUM column to include 'paypal'
# This preserves existing values and adds 'paypal'
op.execute(
"ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe', 'paypal') NOT NULL"
)
op.execute("ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe', 'paypal') NOT NULL")
else:
# For other databases (PostgreSQL, SQLite), enum changes are handled differently
# For SQLite, this might not be needed as it doesn't enforce enum constraints
pass
# ### end Alembic commands ###
def downgrade() -> None:
# Remove 'paypal' from the ENUM (be careful if there are existing paypal payments)
bind = op.get_bind()
if bind.dialect.name == 'mysql':
# First, check if there are any paypal payments - if so, this will fail
# In production, you'd want to migrate existing paypal payments first
op.execute(
"ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe') NOT NULL"
)
# ### end Alembic commands ###
op.execute("ALTER TABLE payments MODIFY COLUMN payment_method ENUM('cash', 'credit_card', 'debit_card', 'bank_transfer', 'e_wallet', 'stripe') NOT NULL")

View File

@@ -1,27 +1,12 @@
"""add_is_proforma_to_invoices
Revision ID: f1a2b3c4d5e6
Revises: d9aff6c5f0d4
Create Date: 2025-11-20 00:20:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f1a2b3c4d5e6'
down_revision = 'd9aff6c5f0d4'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add is_proforma column to invoices table
op.add_column('invoices', sa.Column('is_proforma', sa.Boolean(), nullable=False, server_default='0'))
def downgrade() -> None:
# Remove is_proforma column
op.drop_column('invoices', 'is_proforma')
op.drop_column('invoices', 'is_proforma')

View File

@@ -1,36 +1,22 @@
"""add_more_luxury_sections_to_page_content
Revision ID: ff515d77abbe
Revises: 17efc6439cc3
Create Date: 2025-11-20 15:17:50.977961
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ff515d77abbe'
down_revision = '17efc6439cc3'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add more luxury sections to page_contents table
op.add_column('page_contents', sa.Column('luxury_services', sa.Text(), nullable=True)) # JSON array of services
op.add_column('page_contents', sa.Column('luxury_experiences', sa.Text(), nullable=True)) # JSON array of experiences
op.add_column('page_contents', sa.Column('awards', sa.Text(), nullable=True)) # JSON array of awards
op.add_column('page_contents', sa.Column('luxury_services', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('luxury_experiences', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('awards', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('cta_title', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('cta_subtitle', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('cta_button_text', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('cta_button_link', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('cta_image', sa.Text(), nullable=True))
op.add_column('page_contents', sa.Column('partners', sa.Text(), nullable=True)) # JSON array of partners
op.add_column('page_contents', sa.Column('partners', sa.Text(), nullable=True))
def downgrade() -> None:
# Remove luxury sections fields
op.drop_column('page_contents', 'partners')
op.drop_column('page_contents', 'cta_image')
op.drop_column('page_contents', 'cta_button_link')
@@ -39,5 +25,4 @@ def downgrade() -> None:
op.drop_column('page_contents', 'cta_title')
op.drop_column('page_contents', 'awards')
op.drop_column('page_contents', 'luxury_experiences')
op.drop_column('page_contents', 'luxury_services')
op.drop_column('page_contents', 'luxury_services')