Files
gatehouse-api/migrations/versions/c8d2e4f6a1b3_consolidate_sessions.py
T

123 lines
4.3 KiB
Python

"""Consolidate user and superadmin sessions into unified sessions table.
Revision ID: c8d2e4f6a1b3
Revises: b7e3f1a92c4d
Create Date: 2026-04-28 00:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = 'c8d2e4f6a1b3'
down_revision = 'b7e3f1a92c4d'
branch_labels = None
depends_on = None
def upgrade():
# 1. Add new columns (nullable initially for data migration)
op.add_column('sessions', sa.Column('owner_type', sa.String(20), nullable=True))
op.add_column('sessions', sa.Column('owner_id', sa.String(36), nullable=True))
# 2. Backfill existing user sessions: owner_type = 'user', owner_id = user_id
op.execute("""
UPDATE sessions
SET owner_type = 'user',
owner_id = user_id
WHERE owner_type IS NULL
""")
# 3. Migrate superadmin sessions into the sessions table
op.execute("""
INSERT INTO sessions (
id, owner_type, owner_id, token, status,
ip_address, user_agent, device_info,
expires_at, last_activity_at, revoked_at, revoked_reason,
is_compliance_only, created_at, updated_at, deleted_at
)
SELECT
id, 'superadmin', superadmin_id, token, 'ACTIVE',
ip_address, user_agent, NULL,
expires_at, last_activity_at, revoked_at, revoked_reason,
FALSE, created_at, updated_at, deleted_at
FROM superadmin_sessions
""")
# 4. Make owner_type and owner_id NOT NULL
op.alter_column('sessions', 'owner_type', nullable=False)
op.alter_column('sessions', 'owner_id', nullable=False)
# 5. Make user_id nullable (no longer the sole owner reference)
op.alter_column('sessions', 'user_id', nullable=True)
# 6. Create indexes for efficient owner-scoped queries
op.create_index(
'ix_sessions_owner_type_owner_id',
'sessions',
['owner_type', 'owner_id']
)
op.create_index(
'ix_sessions_owner_type',
'sessions',
['owner_type']
)
op.create_index(
'ix_sessions_owner_id',
'sessions',
['owner_id']
)
# 7. Drop the now-redundant superadmin_sessions table
op.drop_table('superadmin_sessions')
def downgrade():
# 1. Recreate superadmin_sessions table
op.create_table(
'superadmin_sessions',
sa.Column('id', sa.String(36), primary_key=True),
sa.Column('superadmin_id', sa.String(36), sa.ForeignKey('superadmins.id'), nullable=False, index=True),
sa.Column('token', sa.String(255), unique=True, nullable=False, index=True),
sa.Column('expires_at', sa.DateTime, nullable=False),
sa.Column('last_activity_at', sa.DateTime, nullable=False),
sa.Column('ip_address', sa.String(45), nullable=True),
sa.Column('user_agent', sa.Text, nullable=True),
sa.Column('revoked_at', sa.DateTime, nullable=True),
sa.Column('revoked_reason', sa.String(255), nullable=True),
sa.Column('created_at', sa.DateTime, nullable=False),
sa.Column('updated_at', sa.DateTime, nullable=False),
sa.Column('deleted_at', sa.DateTime, nullable=True),
)
# 2. Move superadmin sessions back to superadmin_sessions
op.execute("""
INSERT INTO superadmin_sessions (
id, superadmin_id, token, expires_at, last_activity_at,
ip_address, user_agent, revoked_at, revoked_reason,
created_at, updated_at, deleted_at
)
SELECT
id, owner_id, token, expires_at, last_activity_at,
ip_address, user_agent, revoked_at, revoked_reason,
created_at, updated_at, deleted_at
FROM sessions
WHERE owner_type = 'superadmin'
""")
# 3. Remove superadmin sessions from sessions table
op.execute("DELETE FROM sessions WHERE owner_type = 'superadmin'")
# 4. Drop indexes
op.drop_index('ix_sessions_owner_id', table_name='sessions')
op.drop_index('ix_sessions_owner_type', table_name='sessions')
op.drop_index('ix_sessions_owner_type_owner_id', table_name='sessions')
# 5. Remove new columns
op.drop_column('sessions', 'owner_id')
op.drop_column('sessions', 'owner_type')
# 6. Make user_id NOT NULL again
op.alter_column('sessions', 'user_id', nullable=False)