"""add admin panel tables Revision ID: 026 Revises: 025 Create Date: 2026-02-08 Creates tables for admin panel: - account_limit_overrides: Per-account plan limit overrides - feature_flags: Feature flag definitions - plan_feature_defaults: Which features each plan gets - account_feature_overrides: Per-account feature exceptions - platform_settings: Runtime configuration storage """ revision = "026" down_revision = "025" branch_labels = None depends_on = None from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql def upgrade() -> None: # Account limit overrides op.create_table( "account_limit_overrides", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")), sa.Column("account_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("accounts.id", ondelete="CASCADE"), unique=True, nullable=False), sa.Column("override_max_trees", sa.Integer(), nullable=True), sa.Column("override_max_sessions_per_month", sa.Integer(), nullable=True), sa.Column("override_max_users", sa.Integer(), nullable=True), sa.Column("note", sa.Text(), nullable=True), sa.Column("created_by_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id"), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), ) op.create_index("ix_account_limit_overrides_account_id", "account_limit_overrides", ["account_id"]) # Feature flags op.create_table( "feature_flags", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")), sa.Column("flag_key", sa.String(100), unique=True, nullable=False), sa.Column("display_name", sa.String(255), nullable=False), sa.Column("description", sa.Text(), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), ) # Plan feature defaults op.create_table( "plan_feature_defaults", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")), sa.Column("plan", sa.String(50), sa.ForeignKey("plan_limits.plan"), nullable=False), sa.Column("flag_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("feature_flags.id", ondelete="CASCADE"), nullable=False), sa.Column("enabled", sa.Boolean(), server_default=sa.text("false"), nullable=False), sa.UniqueConstraint("plan", "flag_id", name="uq_plan_feature_defaults_plan_flag"), ) op.create_index("ix_plan_feature_defaults_plan", "plan_feature_defaults", ["plan"]) # Account feature overrides op.create_table( "account_feature_overrides", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")), sa.Column("account_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False), sa.Column("flag_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("feature_flags.id", ondelete="CASCADE"), nullable=False), sa.Column("enabled", sa.Boolean(), nullable=False), sa.Column("note", sa.Text(), nullable=True), sa.Column("created_by_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id"), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.UniqueConstraint("account_id", "flag_id", name="uq_account_feature_overrides_account_flag"), ) op.create_index("ix_account_feature_overrides_account_id", "account_feature_overrides", ["account_id"]) # Platform settings op.create_table( "platform_settings", sa.Column("setting_key", sa.String(100), primary_key=True), sa.Column("setting_value", sa.Text(), nullable=True), sa.Column("data_type", sa.String(20), nullable=False, server_default="string"), sa.Column("is_sensitive", sa.Boolean(), server_default=sa.text("false"), nullable=False), sa.Column("updated_by_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("users.id"), nullable=True), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), ) # Seed default platform settings op.execute( "INSERT INTO platform_settings (setting_key, setting_value, data_type) VALUES " "('maintenance_mode', 'false', 'boolean'), " "('maintenance_message', 'We''re performing scheduled maintenance. We''ll be back soon!', 'string')" ) def downgrade() -> None: op.drop_table("platform_settings") op.drop_table("account_feature_overrides") op.drop_table("plan_feature_defaults") op.drop_table("feature_flags") op.drop_table("account_limit_overrides")