mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 20:55:41 +02:00
163 lines
7.3 KiB
Python
163 lines
7.3 KiB
Python
"""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")
|