"""Add missing tables: brands, competitors, api_keys, usage_records, platform_rule_versions, detection_tasks Revision ID: g1h2i3j4kl56 Revises: f7a8b9c0de56 Create Date: 2026-05-26 10:00:00.000000 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision: str = "g1h2i3j4kl56" down_revision: Union[str, Sequence[str], None] = "f7a8b9c0de56" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( "brands", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("name", sa.String(50), nullable=False), sa.Column("aliases", postgresql.JSONB(), server_default="[]", nullable=False), sa.Column("website", sa.String(500), nullable=True), sa.Column("industry", sa.String(50), nullable=True), sa.Column("platforms", postgresql.JSONB(), server_default="[]", nullable=False), sa.Column("frequency", sa.String(20), server_default="weekly", nullable=False), sa.Column("status", sa.String(20), server_default="active", nullable=False), sa.Column("last_queried_at", sa.DateTime(timezone=True), nullable=True), sa.Column("next_query_at", sa.DateTime(timezone=True), 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.PrimaryKeyConstraint("id"), ) op.create_index("idx_brands_user_id", "brands", ["user_id"]) op.create_table( "competitors", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("brand_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("name", sa.String(50), nullable=False), sa.Column("aliases", postgresql.JSONB(), server_default="[]", nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["brand_id"], ["brands.id"], ondelete="CASCADE"), ) op.create_index("idx_competitors_brand_id", "competitors", ["brand_id"]) op.create_table( "api_keys", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("engine_type", sa.String(20), nullable=False), sa.Column("encrypted_key", sa.String(500), nullable=False), sa.Column("key_hint", sa.String(50), nullable=False), sa.Column("key_source", sa.String(10), server_default="user", nullable=True), sa.Column("status", sa.String(20), server_default="active", nullable=True), sa.Column("priority", sa.Integer(), server_default="0", nullable=True), sa.Column("last_verified_at", sa.DateTime(timezone=True), 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.PrimaryKeyConstraint("id"), ) op.create_index("idx_api_keys_user_id", "api_keys", ["user_id"]) op.create_index("idx_api_keys_user_engine", "api_keys", ["user_id", "engine_type"]) op.create_index("idx_api_keys_engine_status", "api_keys", ["engine_type", "status"]) op.create_table( "usage_records", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("brand_id", postgresql.UUID(as_uuid=True), nullable=True), sa.Column("engine_type", sa.String(20), nullable=False), sa.Column("query", sa.String(500), nullable=False), sa.Column("input_tokens", sa.Integer(), server_default="0", nullable=True), sa.Column("output_tokens", sa.Integer(), server_default="0", nullable=True), sa.Column("cost", sa.Float(), server_default="0.0", nullable=True), sa.Column("extra_data", postgresql.JSONB(), server_default="{}", nullable=True), sa.Column("timestamp", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["brand_id"], ["brands.id"], ondelete="SET NULL"), ) op.create_index("idx_usage_records_user_id", "usage_records", ["user_id"]) op.create_index("idx_usage_records_timestamp", "usage_records", ["timestamp"]) op.create_index("idx_usage_records_user_engine", "usage_records", ["user_id", "engine_type"]) op.create_index("idx_usage_records_user_timestamp", "usage_records", ["user_id", "timestamp"]) op.create_index("idx_usage_records_engine_timestamp", "usage_records", ["engine_type", "timestamp"]) op.create_table( "platform_rule_versions", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("rule_id", sa.String(100), nullable=False), sa.Column("platform", sa.String(50), nullable=False), sa.Column("version", sa.Integer(), nullable=False), sa.Column("rule_data", postgresql.JSONB(), nullable=False), sa.Column("change_summary", sa.String(500), nullable=True), sa.Column("created_by", sa.String(100), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True), sa.PrimaryKeyConstraint("id"), ) op.create_index("idx_rule_versions_rule_id", "platform_rule_versions", ["rule_id"]) op.create_index("idx_rule_versions_platform", "platform_rule_versions", ["platform"]) op.create_table( "detection_tasks", sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False), sa.Column("brand_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("name", sa.String(200), nullable=False), sa.Column("frequency", sa.String(20), nullable=False), sa.Column("engines", postgresql.JSONB(), server_default="[]", nullable=False), sa.Column("queries", postgresql.JSONB(), server_default="[]", nullable=False), sa.Column("competitor_names", postgresql.JSONB(), nullable=True), sa.Column("is_active", sa.Boolean(), server_default="true", nullable=False), sa.Column("last_run_at", sa.DateTime(timezone=True), nullable=True), sa.Column("next_run_at", sa.DateTime(timezone=True), 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.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["brand_id"], ["brands.id"], ondelete="CASCADE"), ) op.create_index("idx_detection_tasks_brand_id", "detection_tasks", ["brand_id"]) op.create_index("idx_detection_tasks_user_id", "detection_tasks", ["user_id"]) op.create_index("idx_detection_tasks_is_active", "detection_tasks", ["is_active"]) def downgrade() -> None: op.drop_index("idx_detection_tasks_is_active", table_name="detection_tasks") op.drop_index("idx_detection_tasks_user_id", table_name="detection_tasks") op.drop_index("idx_detection_tasks_brand_id", table_name="detection_tasks") op.drop_table("detection_tasks") op.drop_index("idx_rule_versions_platform", table_name="platform_rule_versions") op.drop_index("idx_rule_versions_rule_id", table_name="platform_rule_versions") op.drop_table("platform_rule_versions") op.drop_index("idx_usage_records_engine_timestamp", table_name="usage_records") op.drop_index("idx_usage_records_user_timestamp", table_name="usage_records") op.drop_index("idx_usage_records_user_engine", table_name="usage_records") op.drop_index("idx_usage_records_timestamp", table_name="usage_records") op.drop_index("idx_usage_records_user_id", table_name="usage_records") op.drop_table("usage_records") op.drop_index("idx_api_keys_engine_status", table_name="api_keys") op.drop_index("idx_api_keys_user_engine", table_name="api_keys") op.drop_index("idx_api_keys_user_id", table_name="api_keys") op.drop_table("api_keys") op.drop_index("idx_competitors_brand_id", table_name="competitors") op.drop_table("competitors") op.drop_index("idx_brands_user_id", table_name="brands") op.drop_table("brands")