"""Add admin platform roles (super admin + platform admin) Revision ID: z9j0k1l2m3n4 Revises: z8i9j0k1l2m3 Create Date: 2026-01-24 Adds support for super admin and platform admin roles: - is_super_admin column on users table - admin_platforms junction table for platform admin assignments Super admins have access to all platforms. Platform admins are assigned to specific platforms via admin_platforms. Existing admins are migrated to super admins for backward compatibility. """ from datetime import UTC, datetime from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = "z9j0k1l2m3n4" down_revision = "z8i9j0k1l2m3" branch_labels = None depends_on = None def upgrade() -> None: # 1. Add is_super_admin column to users table op.add_column( "users", sa.Column( "is_super_admin", sa.Boolean(), nullable=False, server_default="false", comment="Whether this admin has access to all platforms (super admin)", ), ) # 2. Create admin_platforms junction table op.create_table( "admin_platforms", sa.Column("id", sa.Integer(), nullable=False), sa.Column( "user_id", sa.Integer(), nullable=False, comment="Reference to the admin user", ), sa.Column( "platform_id", sa.Integer(), nullable=False, comment="Reference to the platform", ), sa.Column( "is_active", sa.Boolean(), nullable=False, server_default="true", comment="Whether the admin assignment is active", ), sa.Column( "assigned_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), comment="When the admin was assigned to this platform", ), sa.Column( "assigned_by_user_id", sa.Integer(), nullable=True, comment="Super admin who made this assignment", ), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), ), sa.Column( "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), onupdate=sa.func.now(), ), sa.ForeignKeyConstraint( ["user_id"], ["users.id"], ondelete="CASCADE", ), sa.ForeignKeyConstraint( ["platform_id"], ["platforms.id"], ondelete="CASCADE", ), sa.ForeignKeyConstraint( ["assigned_by_user_id"], ["users.id"], ondelete="SET NULL", ), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("user_id", "platform_id", name="uq_admin_platform"), ) # Create indexes for performance op.create_index( "idx_admin_platforms_user_id", "admin_platforms", ["user_id"], ) op.create_index( "idx_admin_platforms_platform_id", "admin_platforms", ["platform_id"], ) op.create_index( "idx_admin_platform_active", "admin_platforms", ["user_id", "platform_id", "is_active"], ) op.create_index( "idx_admin_platform_user_active", "admin_platforms", ["user_id", "is_active"], ) # 3. Migrate existing admins to super admins for backward compatibility # All current admins get super admin access to maintain their existing permissions op.execute("UPDATE users SET is_super_admin = TRUE WHERE role = 'admin'") def downgrade() -> None: # Drop indexes op.drop_index("idx_admin_platform_user_active", table_name="admin_platforms") op.drop_index("idx_admin_platform_active", table_name="admin_platforms") op.drop_index("idx_admin_platforms_platform_id", table_name="admin_platforms") op.drop_index("idx_admin_platforms_user_id", table_name="admin_platforms") # Drop admin_platforms table op.drop_table("admin_platforms") # Drop is_super_admin column op.drop_column("users", "is_super_admin")