"""Add Phase 2 tables Revision ID: a1b2c3d4e5f6 Revises: f82b3092d056 Create Date: 2025-12-09 17:28:20.000000 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision: str = 'a1b2c3d4e5f6' down_revision: Union[str, Sequence[str], None] = 'f82b3092d056' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: """Upgrade schema for Phase 2.""" # Add new fields to users table op.add_column('users', sa.Column('email', sa.String(), nullable=True)) op.add_column('users', sa.Column('email_verified', sa.Boolean(), nullable=False, server_default='false')) op.add_column('users', sa.Column('totp_secret', sa.String(), nullable=True)) op.add_column('users', sa.Column('totp_enabled', sa.Boolean(), nullable=False, server_default='false')) op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) # Create refresh_tokens table op.create_table( 'refresh_tokens', sa.Column('id', sa.Integer(), nullable=False), sa.Column('token', sa.String(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=False), sa.Column('expires_at', sa.DateTime(), nullable=False), sa.Column('is_revoked', sa.Boolean(), nullable=False), sa.Column('created_at', sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), sa.PrimaryKeyConstraint('id') ) 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) # Create password_reset_tokens table op.create_table( 'password_reset_tokens', sa.Column('id', sa.Integer(), nullable=False), sa.Column('token', sa.String(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=False), sa.Column('expires_at', sa.DateTime(), nullable=False), sa.Column('is_used', sa.Boolean(), nullable=False), sa.Column('created_at', sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), sa.PrimaryKeyConstraint('id') ) 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) # Create audit_logs table op.create_table( 'audit_logs', sa.Column('id', sa.Integer(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=True), sa.Column('tenant_id', sa.Integer(), nullable=False), sa.Column('action', sa.String(), nullable=False), sa.Column('resource_type', sa.String(), nullable=False), sa.Column('resource_id', sa.Integer(), nullable=True), sa.Column('details', sa.JSON(), nullable=True), sa.Column('ip_address', sa.String(), nullable=True), sa.Column('user_agent', sa.String(), nullable=True), sa.Column('created_at', sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ), sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), sa.PrimaryKeyConstraint('id') ) op.create_index(op.f('ix_audit_logs_id'), 'audit_logs', ['id'], unique=False) op.create_index(op.f('ix_audit_logs_created_at'), 'audit_logs', ['created_at'], unique=False) def downgrade() -> None: """Downgrade schema for Phase 2.""" # Drop audit_logs table op.drop_index(op.f('ix_audit_logs_created_at'), table_name='audit_logs') op.drop_index(op.f('ix_audit_logs_id'), table_name='audit_logs') op.drop_table('audit_logs') # Drop password_reset_tokens table op.drop_index(op.f('ix_password_reset_tokens_token'), table_name='password_reset_tokens') op.drop_index(op.f('ix_password_reset_tokens_id'), table_name='password_reset_tokens') op.drop_table('password_reset_tokens') # Drop refresh_tokens table op.drop_index(op.f('ix_refresh_tokens_token'), table_name='refresh_tokens') op.drop_index(op.f('ix_refresh_tokens_id'), table_name='refresh_tokens') op.drop_table('refresh_tokens') # Remove new fields from users table op.drop_index(op.f('ix_users_email'), table_name='users') op.drop_column('users', 'totp_enabled') op.drop_column('users', 'totp_secret') op.drop_column('users', 'email_verified') op.drop_column('users', 'email')