updates
This commit is contained in:
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
from logging.config import fileConfig
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy import pool
|
||||
from alembic import context
|
||||
import os
|
||||
@@ -15,17 +15,19 @@ config = context.config
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
database_url = settings.database_url
|
||||
config.set_main_option('sqlalchemy.url', database_url)
|
||||
target_metadata = Base.metadata
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
url = config.get_main_option('sqlalchemy.url')
|
||||
"""Run migrations in 'offline' mode."""
|
||||
url = database_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:
|
||||
connectable = engine_from_config(config.get_section(config.config_ini_section, {}), prefix='sqlalchemy.', poolclass=pool.NullPool)
|
||||
"""Run migrations in 'online' mode."""
|
||||
# Create engine directly from URL to avoid ConfigParser interpolation issues
|
||||
connectable = create_engine(database_url, poolclass=pool.NullPool)
|
||||
with connectable.connect() as connection:
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
with context.begin_transaction():
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
"""create_user_sessions_table
|
||||
|
||||
Revision ID: 54e4d0db31a3
|
||||
Revises: d709b14aa24a
|
||||
Create Date: 2025-12-06 01:12:15.123456
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '54e4d0db31a3'
|
||||
down_revision = 'd709b14aa24a'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Check if table exists before creating
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
if 'user_sessions' not in tables:
|
||||
# Create user sessions table
|
||||
op.create_table(
|
||||
'user_sessions',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('session_token', sa.String(length=255), nullable=False),
|
||||
sa.Column('refresh_token', sa.String(length=255), nullable=True),
|
||||
sa.Column('ip_address', sa.String(length=45), nullable=True),
|
||||
sa.Column('user_agent', sa.String(length=500), nullable=True),
|
||||
sa.Column('device_info', sa.Text(), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='1'),
|
||||
sa.Column('last_activity', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
# Create indexes
|
||||
op.create_index(op.f('ix_user_sessions_id'), 'user_sessions', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_user_sessions_user_id'), 'user_sessions', ['user_id'], unique=False)
|
||||
op.create_index(op.f('ix_user_sessions_session_token'), 'user_sessions', ['session_token'], unique=True)
|
||||
op.create_index(op.f('ix_user_sessions_refresh_token'), 'user_sessions', ['refresh_token'], unique=True)
|
||||
op.create_index(op.f('ix_user_sessions_is_active'), 'user_sessions', ['is_active'], unique=False)
|
||||
op.create_index(op.f('ix_user_sessions_last_activity'), 'user_sessions', ['last_activity'], unique=False)
|
||||
op.create_index(op.f('ix_user_sessions_expires_at'), 'user_sessions', ['expires_at'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop indexes
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
if 'user_sessions' in tables:
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_expires_at'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_last_activity'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_is_active'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_refresh_token'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_session_token'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_user_id'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_user_sessions_id'), table_name='user_sessions')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Drop table
|
||||
op.drop_table('user_sessions')
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,114 @@
|
||||
"""create_gdpr_requests_table
|
||||
|
||||
Revision ID: d709b14aa24a
|
||||
Revises: b1c4d7c154ec
|
||||
Create Date: 2025-12-06 01:10:15.233886
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd709b14aa24a'
|
||||
down_revision = 'b1c4d7c154ec'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Check if table exists before creating
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
if 'gdpr_requests' not in tables:
|
||||
# Create GDPR requests table
|
||||
op.create_table(
|
||||
'gdpr_requests',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('request_type', sa.Enum('data_export', 'data_deletion', 'data_rectification', 'consent_withdrawal', name='gdprrequesttype'), nullable=False),
|
||||
sa.Column('status', sa.Enum('pending', 'processing', 'completed', 'rejected', 'cancelled', name='gdprrequeststatus'), nullable=False, server_default='pending'),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('user_email', sa.String(length=255), nullable=False),
|
||||
sa.Column('is_anonymous', sa.Boolean(), nullable=False, server_default='0'),
|
||||
sa.Column('request_data', sa.JSON(), nullable=True),
|
||||
sa.Column('verification_token', sa.String(length=255), nullable=True),
|
||||
sa.Column('verified_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('processed_by', sa.Integer(), nullable=True),
|
||||
sa.Column('processed_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('processing_notes', sa.Text(), nullable=True),
|
||||
sa.Column('export_file_path', sa.String(length=500), nullable=True),
|
||||
sa.Column('deletion_log', sa.JSON(), nullable=True),
|
||||
sa.Column('ip_address', sa.String(length=45), nullable=True),
|
||||
sa.Column('user_agent', sa.String(length=255), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['processed_by'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
# Create indexes
|
||||
op.create_index(op.f('ix_gdpr_requests_id'), 'gdpr_requests', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_request_type'), 'gdpr_requests', ['request_type'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_status'), 'gdpr_requests', ['status'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_user_id'), 'gdpr_requests', ['user_id'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_is_anonymous'), 'gdpr_requests', ['is_anonymous'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_created_at'), 'gdpr_requests', ['created_at'], unique=False)
|
||||
op.create_index(op.f('ix_gdpr_requests_verification_token'), 'gdpr_requests', ['verification_token'], unique=True)
|
||||
else:
|
||||
# Table already exists, ensure it has the is_anonymous column if missing
|
||||
columns = [col['name'] for col in inspector.get_columns('gdpr_requests')]
|
||||
if 'is_anonymous' not in columns:
|
||||
op.add_column('gdpr_requests', sa.Column('is_anonymous', sa.Boolean(), nullable=False, server_default='0'))
|
||||
op.create_index(op.f('ix_gdpr_requests_is_anonymous'), 'gdpr_requests', ['is_anonymous'], unique=False)
|
||||
|
||||
# Ensure user_id is nullable
|
||||
# Note: This might fail if there are existing non-null values, but for a new table it should be fine
|
||||
try:
|
||||
op.alter_column('gdpr_requests', 'user_id', existing_type=sa.Integer(), nullable=True)
|
||||
except Exception:
|
||||
pass # Column might already be nullable
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Drop indexes
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
if 'gdpr_requests' in tables:
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_verification_token'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_created_at'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_is_anonymous'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_user_id'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_status'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_request_type'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
op.drop_index(op.f('ix_gdpr_requests_id'), table_name='gdpr_requests')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Drop table
|
||||
op.drop_table('gdpr_requests')
|
||||
@@ -0,0 +1,110 @@
|
||||
"""add_email_verification_and_enhance_user_security
|
||||
|
||||
Revision ID: fe519abcefe7
|
||||
Revises: 54e4d0db31a3
|
||||
Create Date: 2025-12-06 01:53:10.797944
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fe519abcefe7'
|
||||
down_revision = '54e4d0db31a3'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
# Add email_verified column to users table
|
||||
if 'users' in tables:
|
||||
columns = [col['name'] for col in inspector.get_columns('users')]
|
||||
if 'email_verified' not in columns:
|
||||
# Use MySQL-compatible boolean default
|
||||
op.add_column('users', sa.Column('email_verified', sa.Boolean(), nullable=False, server_default=sa.text('0')))
|
||||
# Check if index exists before creating
|
||||
try:
|
||||
op.create_index('ix_users_email_verified', 'users', ['email_verified'], unique=False)
|
||||
except Exception:
|
||||
pass # Index might already exist
|
||||
|
||||
# Add password expiry fields
|
||||
if 'password_changed_at' not in columns:
|
||||
op.add_column('users', sa.Column('password_changed_at', sa.DateTime(), nullable=True))
|
||||
# Check if index exists before creating
|
||||
try:
|
||||
op.create_index('ix_users_password_changed_at', 'users', ['password_changed_at'], unique=False)
|
||||
except Exception:
|
||||
pass # Index might already exist
|
||||
|
||||
# Create password_history table
|
||||
if 'password_history' not in tables:
|
||||
op.create_table(
|
||||
'password_history',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('password_hash', sa.String(length=255), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_password_history_id'), 'password_history', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_password_history_user_id'), 'password_history', ['user_id'], unique=False)
|
||||
op.create_index(op.f('ix_password_history_created_at'), 'password_history', ['created_at'], unique=False)
|
||||
|
||||
# Create email_verification_tokens table
|
||||
if 'email_verification_tokens' not in tables:
|
||||
op.create_table(
|
||||
'email_verification_tokens',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('token', sa.String(length=255), nullable=False),
|
||||
sa.Column('email', sa.String(length=100), nullable=False),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('used', sa.Boolean(), nullable=False, server_default=sa.text('0')),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_email_verification_tokens_id'), 'email_verification_tokens', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_email_verification_tokens_user_id'), 'email_verification_tokens', ['user_id'], unique=False)
|
||||
op.create_index(op.f('ix_email_verification_tokens_token'), 'email_verification_tokens', ['token'], unique=True)
|
||||
op.create_index(op.f('ix_email_verification_tokens_expires_at'), 'email_verification_tokens', ['expires_at'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
# Drop email_verification_tokens table
|
||||
if 'email_verification_tokens' in tables:
|
||||
op.drop_index(op.f('ix_email_verification_tokens_expires_at'), table_name='email_verification_tokens')
|
||||
op.drop_index(op.f('ix_email_verification_tokens_token'), table_name='email_verification_tokens')
|
||||
op.drop_index(op.f('ix_email_verification_tokens_user_id'), table_name='email_verification_tokens')
|
||||
op.drop_index(op.f('ix_email_verification_tokens_id'), table_name='email_verification_tokens')
|
||||
op.drop_table('email_verification_tokens')
|
||||
|
||||
# Remove password_history table
|
||||
if 'password_history' in tables:
|
||||
op.drop_index(op.f('ix_password_history_created_at'), table_name='password_history')
|
||||
op.drop_index(op.f('ix_password_history_user_id'), table_name='password_history')
|
||||
op.drop_index(op.f('ix_password_history_id'), table_name='password_history')
|
||||
op.drop_table('password_history')
|
||||
|
||||
# Remove email_verified and password_changed_at columns from users table
|
||||
if 'users' in tables:
|
||||
columns = [col['name'] for col in inspector.get_columns('users')]
|
||||
if 'password_changed_at' in columns:
|
||||
op.drop_index('ix_users_password_changed_at', table_name='users')
|
||||
op.drop_column('users', 'password_changed_at')
|
||||
if 'email_verified' in columns:
|
||||
op.drop_index('ix_users_email_verified', table_name='users')
|
||||
op.drop_column('users', 'email_verified')
|
||||
Reference in New Issue
Block a user