75 lines
3.0 KiB
Python
75 lines
3.0 KiB
Python
"""Persist Message Safety v2 evidence and recovery locations.
|
|
|
|
Revision ID: 0012_safety_v2_checkpoint
|
|
Revises: 0011_module07_contract
|
|
Create Date: 2026-08-06
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0012_safety_v2_checkpoint"
|
|
down_revision: str | None = "0011_module07_contract"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE han_app.messages
|
|
ADD COLUMN IF NOT EXISTS safety_processing_mode varchar(16),
|
|
ADD COLUMN IF NOT EXISTS safety_config_version bigint,
|
|
ADD COLUMN IF NOT EXISTS safety_rules_version varchar(128);
|
|
|
|
ALTER TABLE han_app.message_attachments
|
|
ADD COLUMN IF NOT EXISTS quarantine_version_id varchar(1024),
|
|
ADD COLUMN IF NOT EXISTS quarantine_etag varchar(1024);
|
|
ALTER TABLE han_app.message_attachments
|
|
DROP CONSTRAINT IF EXISTS message_attachments_scan_status_check;
|
|
ALTER TABLE han_app.message_attachments
|
|
ADD CONSTRAINT message_attachments_scan_status_check
|
|
CHECK (scan_status IN ('pending','clean','bypassed','infected','failed')) NOT VALID;
|
|
ALTER TABLE han_app.message_attachments
|
|
VALIDATE CONSTRAINT message_attachments_scan_status_check;
|
|
|
|
ALTER TABLE han_app.safety_tasks
|
|
ADD COLUMN IF NOT EXISTS poll_location varchar(1024),
|
|
ADD COLUMN IF NOT EXISTS processing_mode varchar(16),
|
|
ADD COLUMN IF NOT EXISTS config_version bigint,
|
|
ADD COLUMN IF NOT EXISTS rules_version varchar(128),
|
|
ADD COLUMN IF NOT EXISTS expires_at timestamptz;
|
|
UPDATE han_app.safety_tasks
|
|
SET poll_location = '/internal/safety/v2/messages/tasks/' || task_id,
|
|
expires_at = deadline_at
|
|
WHERE poll_location IS NULL OR expires_at IS NULL;
|
|
ALTER TABLE han_app.safety_tasks
|
|
ALTER COLUMN poll_location SET NOT NULL,
|
|
ALTER COLUMN expires_at SET NOT NULL;
|
|
"""
|
|
)
|
|
|
|
# Notification uploads use the same safety contract.
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE han_app.client_upload_drafts
|
|
ADD COLUMN IF NOT EXISTS quarantine_version_id varchar(1024),
|
|
ADD COLUMN IF NOT EXISTS quarantine_etag varchar(1024),
|
|
ADD COLUMN IF NOT EXISTS safety_processing_mode varchar(16),
|
|
ADD COLUMN IF NOT EXISTS safety_config_version bigint,
|
|
ADD COLUMN IF NOT EXISTS safety_rules_version varchar(128);
|
|
ALTER TABLE han_app.client_upload_drafts
|
|
DROP CONSTRAINT IF EXISTS client_upload_drafts_scan_status_check;
|
|
ALTER TABLE han_app.client_upload_drafts
|
|
ADD CONSTRAINT client_upload_drafts_scan_status_check
|
|
CHECK (scan_status IN ('pending','clean','bypassed','infected','failed')) NOT VALID;
|
|
ALTER TABLE han_app.client_upload_drafts
|
|
VALIDATE CONSTRAINT client_upload_drafts_scan_status_check;
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
raise RuntimeError("Safety v2 checkpoint migration is forward-only")
|