"""IRM: алерты отдельно от инцидентов (ack/resolve), связь N:M инцидент↔алерт Revision ID: 005_irm_alerts Revises: 004_grafana_catalog Create Date: 2026-04-03 """ from typing import Sequence, Union from alembic import op revision: str = "005_irm_alerts" down_revision: Union[str, None] = "004_grafana_catalog" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.execute( """ CREATE TABLE IF NOT EXISTS irm_alerts ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), ingress_event_id uuid NOT NULL UNIQUE REFERENCES ingress_events(id) ON DELETE CASCADE, status text NOT NULL DEFAULT 'firing' CHECK (status IN ('firing', 'acknowledged', 'resolved', 'silenced')), title text NOT NULL DEFAULT '', severity text NOT NULL DEFAULT 'warning', source text NOT NULL DEFAULT 'grafana', grafana_org_slug text, service_name text, labels jsonb NOT NULL DEFAULT '{}'::jsonb, fingerprint text, acknowledged_at timestamptz, acknowledged_by text, resolved_at timestamptz, resolved_by text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); """ ) op.execute( """ CREATE INDEX IF NOT EXISTS irm_alerts_status_created_idx ON irm_alerts (status, created_at DESC); """ ) op.execute( """ CREATE INDEX IF NOT EXISTS irm_alerts_ingress_event_id_idx ON irm_alerts (ingress_event_id); """ ) op.execute( """ CREATE TABLE IF NOT EXISTS incident_alert_links ( incident_id uuid NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, alert_id uuid NOT NULL REFERENCES irm_alerts(id) ON DELETE CASCADE, PRIMARY KEY (incident_id, alert_id) ); """ ) op.execute( """ CREATE INDEX IF NOT EXISTS incident_alert_links_alert_idx ON incident_alert_links (alert_id); """ ) def downgrade() -> None: op.execute("DROP TABLE IF EXISTS incident_alert_links;") op.execute("DROP TABLE IF EXISTS irm_alerts;")