vbox funzionanti

This commit is contained in:
2026-03-19 11:41:10 +01:00
parent 538d6a6bec
commit b7f7c1f7c0
32 changed files with 6043 additions and 262 deletions
@@ -0,0 +1,162 @@
"""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")