"""Phase 13: Operational Alerts — alert_rules and alert_instances tables. Revision ID: b041alerts Revises: b040ent Create Date: 2026-05-21 """ from alembic import op import sqlalchemy as sa revision = "b041alerts" down_revision = "b040ent" branch_labels = None depends_on = None def upgrade() -> None: conn = op.get_bind() # ── alert_rules ─────────────────────────────────────────────────────────── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS alert_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(300) NOT NULL, description TEXT, rule_type VARCHAR(50) NOT NULL, severity VARCHAR(20) NOT NULL DEFAULT 'medium', is_enabled BOOLEAN NOT NULL DEFAULT TRUE, is_system BOOLEAN NOT NULL DEFAULT FALSE, config JSONB NOT NULL DEFAULT '{}', notify_in_app BOOLEAN NOT NULL DEFAULT TRUE, notify_webhook BOOLEAN NOT NULL DEFAULT FALSE, webhook_id UUID REFERENCES webhook_configs(id) ON DELETE SET NULL, cooldown_hours INTEGER NOT NULL DEFAULT 24, created_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(), last_fired_at TIMESTAMP WITHOUT TIME ZONE ) """)) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_rules_type ON alert_rules (rule_type)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_rules_enabled ON alert_rules (is_enabled)" )) # ── alert_instances ─────────────────────────────────────────────────────── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS alert_instances ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), rule_id UUID REFERENCES alert_rules(id) ON DELETE SET NULL, rule_name VARCHAR(300) NOT NULL, rule_type VARCHAR(50) NOT NULL, severity VARCHAR(20) NOT NULL, title VARCHAR(500) NOT NULL, message TEXT NOT NULL, details JSONB, status VARCHAR(20) NOT NULL DEFAULT 'open', acknowledged_by UUID REFERENCES users(id) ON DELETE SET NULL, acknowledged_at TIMESTAMP WITHOUT TIME ZONE, resolved_at TIMESTAMP WITHOUT TIME ZONE, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now() ) """)) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_instances_rule_id ON alert_instances (rule_id)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_instances_status ON alert_instances (status)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_instances_severity ON alert_instances (severity)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_alert_instances_created ON alert_instances (created_at)" )) def downgrade() -> None: conn = op.get_bind() conn.execute(sa.text("DROP TABLE IF EXISTS alert_instances CASCADE")) conn.execute(sa.text("DROP TABLE IF EXISTS alert_rules CASCADE"))