Files
PecHub/backend/alembic/versions/0003_virtual_boxes_notifications.py
T
2026-03-19 11:41:10 +01:00

163 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Virtual Boxes e Notifiche Multi-canale Fase 2
Revision ID: 0003
Revises: 0002
Create Date: 2026-03-19 00:00:00.000000
Aggiunge le tabelle:
- virtual_boxes
- virtual_box_rules
- virtual_box_assignments
- notification_channels (+ ENUM notification_channel_type)
- notification_rules
- notification_log (+ ENUM notification_status)
"""
from alembic import op
revision = "0003"
down_revision = "0002"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ── ENUM types ────────────────────────────────────────────────────────────
op.execute(
"CREATE TYPE notification_channel_type AS ENUM "
"('webhook', 'email', 'telegram', 'whatsapp')"
)
op.execute(
"CREATE TYPE notification_status AS ENUM "
"('pending', 'sent', 'failed', 'skipped')"
)
# ── VIRTUAL BOXES ─────────────────────────────────────────────────────────
op.execute("""
CREATE TABLE virtual_boxes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
label VARCHAR(100),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_vbox_name_tenant UNIQUE (tenant_id, name)
)
""")
op.execute("CREATE INDEX idx_vbox_tenant ON virtual_boxes (tenant_id)")
op.execute("""
CREATE TRIGGER trg_virtual_boxes_updated_at
BEFORE UPDATE ON virtual_boxes
FOR EACH ROW EXECUTE FUNCTION set_updated_at()
""")
op.execute("""
CREATE TABLE virtual_box_rules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
virtual_box_id UUID NOT NULL REFERENCES virtual_boxes(id) ON DELETE CASCADE,
field VARCHAR(50) NOT NULL,
operator VARCHAR(20) NOT NULL DEFAULT 'contains',
value TEXT NOT NULL,
date_from VARCHAR(20),
date_to VARCHAR(20),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
op.execute("CREATE INDEX idx_vbox_rule_vbox ON virtual_box_rules (virtual_box_id)")
op.execute("""
CREATE TABLE virtual_box_assignments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
virtual_box_id UUID NOT NULL REFERENCES virtual_boxes(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
assigned_by UUID REFERENCES users(id),
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_vbox_assignment UNIQUE (virtual_box_id, user_id)
)
""")
op.execute("CREATE INDEX idx_vbox_assign_user ON virtual_box_assignments (user_id)")
op.execute("CREATE INDEX idx_vbox_assign_vbox ON virtual_box_assignments (virtual_box_id)")
# ── NOTIFICATION CHANNELS ─────────────────────────────────────────────────
op.execute("""
CREATE TABLE notification_channels (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
channel_type notification_channel_type NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
config JSONB,
config_enc TEXT,
consecutive_failures INT NOT NULL DEFAULT 0,
circuit_open_until TIMESTAMPTZ,
created_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
op.execute("CREATE INDEX idx_notif_channel_tenant ON notification_channels (tenant_id)")
op.execute("""
CREATE TRIGGER trg_notification_channels_updated_at
BEFORE UPDATE ON notification_channels
FOR EACH ROW EXECUTE FUNCTION set_updated_at()
""")
op.execute("""
CREATE TABLE notification_rules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
channel_id UUID NOT NULL REFERENCES notification_channels(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
event_type VARCHAR(100) NOT NULL,
filter JSONB,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
op.execute("CREATE INDEX idx_notif_rule_tenant ON notification_rules (tenant_id)")
op.execute("CREATE INDEX idx_notif_rule_channel ON notification_rules (channel_id)")
op.execute("CREATE INDEX idx_notif_rule_event ON notification_rules (event_type)")
op.execute("""
CREATE TABLE notification_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
channel_id UUID NOT NULL REFERENCES notification_channels(id) ON DELETE CASCADE,
rule_id UUID REFERENCES notification_rules(id) ON DELETE SET NULL,
event_type VARCHAR(100) NOT NULL,
event_payload JSONB,
status notification_status NOT NULL DEFAULT 'pending',
attempt_count INT NOT NULL DEFAULT 0,
max_attempts INT NOT NULL DEFAULT 3,
next_retry_at TIMESTAMPTZ,
last_error TEXT,
http_status INT,
sent_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
op.execute("CREATE INDEX idx_notif_log_tenant ON notification_log (tenant_id)")
op.execute("CREATE INDEX idx_notif_log_channel ON notification_log (channel_id)")
op.execute(
"CREATE INDEX idx_notif_log_status ON notification_log (status, next_retry_at) "
"WHERE status IN ('pending', 'failed')"
)
def downgrade() -> None:
for table in [
"notification_log",
"notification_rules",
"notification_channels",
"virtual_box_assignments",
"virtual_box_rules",
"virtual_boxes",
]:
op.execute(f"DROP TABLE IF EXISTS {table} CASCADE")
op.execute("DROP TYPE IF EXISTS notification_status")
op.execute("DROP TYPE IF EXISTS notification_channel_type")