Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
321
docs-src/ai-compliance-sdk/migrations/001_rbac_schema.sql
Normal file
321
docs-src/ai-compliance-sdk/migrations/001_rbac_schema.sql
Normal file
@@ -0,0 +1,321 @@
|
||||
-- AI Compliance SDK - RBAC Schema
|
||||
-- Migration 001: Multi-Tenant RBAC with Namespace Isolation
|
||||
|
||||
-- Enable UUID extension
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ============================================================================
|
||||
-- Tenants (Mandanten)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_tenants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
settings JSONB DEFAULT '{}',
|
||||
max_users INT DEFAULT 100,
|
||||
llm_quota_monthly INT DEFAULT 10000,
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_compliance_tenants_slug ON compliance_tenants(slug);
|
||||
CREATE INDEX idx_compliance_tenants_status ON compliance_tenants(status);
|
||||
|
||||
-- ============================================================================
|
||||
-- Namespaces (Abteilungen - z.B. CFO Use-Case)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_namespaces (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(100) NOT NULL,
|
||||
parent_namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
isolation_level VARCHAR(50) DEFAULT 'strict', -- 'strict', 'shared', 'public'
|
||||
data_classification VARCHAR(50) DEFAULT 'internal', -- 'public', 'internal', 'confidential', 'restricted'
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, slug)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_compliance_namespaces_tenant ON compliance_namespaces(tenant_id);
|
||||
CREATE INDEX idx_compliance_namespaces_parent ON compliance_namespaces(parent_namespace_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- Roles with Permissions
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_roles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
permissions TEXT[] NOT NULL DEFAULT '{}',
|
||||
is_system_role BOOLEAN DEFAULT FALSE,
|
||||
hierarchy_level INT DEFAULT 100,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_compliance_roles_tenant ON compliance_roles(tenant_id);
|
||||
CREATE INDEX idx_compliance_roles_system ON compliance_roles(is_system_role);
|
||||
|
||||
-- ============================================================================
|
||||
-- System Roles (Pre-defined)
|
||||
-- ============================================================================
|
||||
INSERT INTO compliance_roles (name, description, permissions, is_system_role, hierarchy_level) VALUES
|
||||
('compliance_executive', 'Executive mit Lesezugriff auf Compliance-Daten und LLM-Queries',
|
||||
ARRAY['compliance:*:read', 'llm:query:execute', 'audit:own:read'], TRUE, 10),
|
||||
('compliance_officer', 'Compliance-Verantwortlicher mit vollem Zugriff',
|
||||
ARRAY['compliance:*', 'audit:*', 'llm:*', 'namespace:read'], TRUE, 20),
|
||||
('data_protection_officer', 'Datenschutzbeauftragter',
|
||||
ARRAY['compliance:privacy:*', 'consent:*', 'dsr:*', 'audit:read', 'llm:query:execute'], TRUE, 25),
|
||||
('namespace_admin', 'Administrator fuer einen Namespace',
|
||||
ARRAY['namespace:own:admin', 'compliance:own:*', 'llm:own:query', 'audit:own:read'], TRUE, 50),
|
||||
('auditor', 'Auditor mit Lesezugriff',
|
||||
ARRAY['compliance:read', 'audit:log:read', 'evidence:read'], TRUE, 60),
|
||||
('compliance_user', 'Standardbenutzer mit eingeschraenktem Zugriff',
|
||||
ARRAY['compliance:own:read', 'llm:own:query'], TRUE, 100)
|
||||
ON CONFLICT (tenant_id, name) DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- User-Role Assignments with Namespace Scope
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_user_roles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
role_id UUID NOT NULL REFERENCES compliance_roles(id) ON DELETE CASCADE,
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE CASCADE,
|
||||
granted_by UUID NOT NULL,
|
||||
expires_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, role_id, tenant_id, namespace_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_compliance_user_roles_user ON compliance_user_roles(user_id);
|
||||
CREATE INDEX idx_compliance_user_roles_tenant ON compliance_user_roles(tenant_id);
|
||||
CREATE INDEX idx_compliance_user_roles_namespace ON compliance_user_roles(namespace_id);
|
||||
CREATE INDEX idx_compliance_user_roles_expires ON compliance_user_roles(expires_at) WHERE expires_at IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- LLM Access Policies
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_llm_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
allowed_data_categories TEXT[] DEFAULT '{}', -- 'salary', 'health', 'personal', 'financial'
|
||||
blocked_data_categories TEXT[] DEFAULT '{}',
|
||||
require_pii_redaction BOOLEAN DEFAULT TRUE,
|
||||
pii_redaction_level VARCHAR(50) DEFAULT 'strict', -- 'strict', 'moderate', 'minimal', 'none'
|
||||
allowed_models TEXT[] DEFAULT '{}', -- 'qwen2.5:7b', 'claude-3-sonnet'
|
||||
max_tokens_per_request INT DEFAULT 4000,
|
||||
max_requests_per_day INT DEFAULT 1000,
|
||||
max_requests_per_hour INT DEFAULT 100,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
priority INT DEFAULT 100, -- Lower = higher priority
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_compliance_llm_policies_tenant ON compliance_llm_policies(tenant_id);
|
||||
CREATE INDEX idx_compliance_llm_policies_namespace ON compliance_llm_policies(namespace_id);
|
||||
CREATE INDEX idx_compliance_llm_policies_active ON compliance_llm_policies(is_active, priority);
|
||||
|
||||
-- ============================================================================
|
||||
-- LLM Audit Log (Immutable)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_llm_audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id),
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id),
|
||||
user_id UUID NOT NULL,
|
||||
session_id VARCHAR(100),
|
||||
operation VARCHAR(100) NOT NULL, -- 'query', 'completion', 'embedding', 'analysis'
|
||||
model_used VARCHAR(100) NOT NULL,
|
||||
provider VARCHAR(50) NOT NULL, -- 'ollama', 'anthropic', 'openai'
|
||||
prompt_hash VARCHAR(64) NOT NULL, -- SHA-256 of prompt (no raw PII stored)
|
||||
prompt_length INT NOT NULL,
|
||||
response_length INT,
|
||||
tokens_used INT NOT NULL,
|
||||
duration_ms INT NOT NULL,
|
||||
pii_detected BOOLEAN DEFAULT FALSE,
|
||||
pii_types_detected TEXT[] DEFAULT '{}',
|
||||
pii_redacted BOOLEAN DEFAULT FALSE,
|
||||
policy_id UUID REFERENCES compliance_llm_policies(id),
|
||||
policy_violations TEXT[] DEFAULT '{}',
|
||||
data_categories_accessed TEXT[] DEFAULT '{}',
|
||||
error_message TEXT,
|
||||
request_metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Partitioning-ready indexes for large audit tables
|
||||
CREATE INDEX idx_llm_audit_tenant_date ON compliance_llm_audit_log(tenant_id, created_at DESC);
|
||||
CREATE INDEX idx_llm_audit_user ON compliance_llm_audit_log(user_id, created_at DESC);
|
||||
CREATE INDEX idx_llm_audit_namespace ON compliance_llm_audit_log(namespace_id, created_at DESC);
|
||||
CREATE INDEX idx_llm_audit_operation ON compliance_llm_audit_log(operation, created_at DESC);
|
||||
CREATE INDEX idx_llm_audit_pii ON compliance_llm_audit_log(pii_detected, created_at DESC) WHERE pii_detected = TRUE;
|
||||
CREATE INDEX idx_llm_audit_violations ON compliance_llm_audit_log(created_at DESC) WHERE array_length(policy_violations, 1) > 0;
|
||||
|
||||
-- ============================================================================
|
||||
-- General Audit Trail
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_audit_trail (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id),
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id),
|
||||
user_id UUID NOT NULL,
|
||||
action VARCHAR(100) NOT NULL, -- 'create', 'update', 'delete', 'access', 'export'
|
||||
resource_type VARCHAR(100) NOT NULL, -- 'role', 'namespace', 'policy', 'evidence'
|
||||
resource_id UUID,
|
||||
old_values JSONB,
|
||||
new_values JSONB,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
reason TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_audit_trail_tenant_date ON compliance_audit_trail(tenant_id, created_at DESC);
|
||||
CREATE INDEX idx_audit_trail_user ON compliance_audit_trail(user_id, created_at DESC);
|
||||
CREATE INDEX idx_audit_trail_resource ON compliance_audit_trail(resource_type, resource_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- API Keys for SDK Access
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_api_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
key_hash VARCHAR(64) NOT NULL UNIQUE, -- SHA-256 of API key
|
||||
key_prefix VARCHAR(8) NOT NULL, -- First 8 chars for identification
|
||||
permissions TEXT[] DEFAULT '{}',
|
||||
namespace_restrictions UUID[] DEFAULT '{}', -- Empty = all namespaces
|
||||
rate_limit_per_hour INT DEFAULT 1000,
|
||||
expires_at TIMESTAMPTZ,
|
||||
last_used_at TIMESTAMPTZ,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_by UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_api_keys_tenant ON compliance_api_keys(tenant_id);
|
||||
CREATE INDEX idx_api_keys_prefix ON compliance_api_keys(key_prefix);
|
||||
CREATE INDEX idx_api_keys_active ON compliance_api_keys(is_active, expires_at);
|
||||
|
||||
-- ============================================================================
|
||||
-- LLM Usage Statistics (Aggregated)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS compliance_llm_usage_stats (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id),
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id),
|
||||
user_id UUID,
|
||||
period_start DATE NOT NULL,
|
||||
period_type VARCHAR(20) NOT NULL, -- 'daily', 'weekly', 'monthly'
|
||||
total_requests INT DEFAULT 0,
|
||||
total_tokens INT DEFAULT 0,
|
||||
total_duration_ms BIGINT DEFAULT 0,
|
||||
requests_with_pii INT DEFAULT 0,
|
||||
policy_violations INT DEFAULT 0,
|
||||
models_used JSONB DEFAULT '{}', -- {"qwen2.5:7b": 100, "claude-3-sonnet": 50}
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, namespace_id, user_id, period_start, period_type)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_llm_usage_tenant_period ON compliance_llm_usage_stats(tenant_id, period_start DESC);
|
||||
|
||||
-- ============================================================================
|
||||
-- Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to check if user has permission in namespace
|
||||
CREATE OR REPLACE FUNCTION check_namespace_permission(
|
||||
p_user_id UUID,
|
||||
p_tenant_id UUID,
|
||||
p_namespace_id UUID,
|
||||
p_permission TEXT
|
||||
) RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
has_permission BOOLEAN := FALSE;
|
||||
BEGIN
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM compliance_user_roles ur
|
||||
JOIN compliance_roles r ON ur.role_id = r.id
|
||||
WHERE ur.user_id = p_user_id
|
||||
AND ur.tenant_id = p_tenant_id
|
||||
AND (ur.namespace_id = p_namespace_id OR ur.namespace_id IS NULL)
|
||||
AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
|
||||
AND (
|
||||
p_permission = ANY(r.permissions)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM unnest(r.permissions) perm
|
||||
WHERE perm LIKE '%:*' AND p_permission LIKE replace(perm, ':*', '') || ':%'
|
||||
)
|
||||
)
|
||||
) INTO has_permission;
|
||||
|
||||
RETURN has_permission;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Function to get effective permissions for user in namespace
|
||||
CREATE OR REPLACE FUNCTION get_effective_permissions(
|
||||
p_user_id UUID,
|
||||
p_tenant_id UUID,
|
||||
p_namespace_id UUID
|
||||
) RETURNS TEXT[] AS $$
|
||||
DECLARE
|
||||
permissions TEXT[];
|
||||
BEGIN
|
||||
SELECT array_agg(DISTINCT perm)
|
||||
INTO permissions
|
||||
FROM (
|
||||
SELECT unnest(r.permissions) as perm
|
||||
FROM compliance_user_roles ur
|
||||
JOIN compliance_roles r ON ur.role_id = r.id
|
||||
WHERE ur.user_id = p_user_id
|
||||
AND ur.tenant_id = p_tenant_id
|
||||
AND (ur.namespace_id = p_namespace_id OR ur.namespace_id IS NULL)
|
||||
AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
|
||||
) sub;
|
||||
|
||||
RETURN COALESCE(permissions, '{}');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- ============================================================================
|
||||
-- Default Tenant for Breakpilot (Self-Hosting)
|
||||
-- ============================================================================
|
||||
INSERT INTO compliance_tenants (name, slug, settings, max_users, llm_quota_monthly)
|
||||
VALUES (
|
||||
'Breakpilot',
|
||||
'breakpilot',
|
||||
'{"deployment": "self-hosted", "hybrid_mode": true}',
|
||||
1000,
|
||||
100000
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- Default namespaces
|
||||
INSERT INTO compliance_namespaces (tenant_id, name, slug, data_classification)
|
||||
SELECT
|
||||
t.id,
|
||||
ns.name,
|
||||
ns.slug,
|
||||
ns.classification
|
||||
FROM compliance_tenants t
|
||||
CROSS JOIN (VALUES
|
||||
('Allgemein', 'general', 'internal'),
|
||||
('Finanzen', 'finance', 'restricted'),
|
||||
('Personal', 'hr', 'confidential'),
|
||||
('IT', 'it', 'internal'),
|
||||
('Compliance', 'compliance', 'confidential')
|
||||
) AS ns(name, slug, classification)
|
||||
WHERE t.slug = 'breakpilot'
|
||||
ON CONFLICT (tenant_id, slug) DO NOTHING;
|
||||
215
docs-src/ai-compliance-sdk/migrations/002_dsgvo_schema.sql
Normal file
215
docs-src/ai-compliance-sdk/migrations/002_dsgvo_schema.sql
Normal file
@@ -0,0 +1,215 @@
|
||||
-- DSGVO Schema Migration
|
||||
-- AI Compliance SDK - Phase 4: DSGVO Integration
|
||||
|
||||
-- ============================================================================
|
||||
-- VVT - Verarbeitungsverzeichnis (Art. 30 DSGVO)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_processing_activities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
purpose TEXT NOT NULL,
|
||||
legal_basis VARCHAR(50) NOT NULL, -- consent, contract, legal_obligation, vital_interests, public_interest, legitimate_interests
|
||||
legal_basis_details TEXT,
|
||||
data_categories JSONB DEFAULT '[]',
|
||||
data_subject_categories JSONB DEFAULT '[]',
|
||||
recipients JSONB DEFAULT '[]',
|
||||
third_country_transfer BOOLEAN DEFAULT FALSE,
|
||||
transfer_safeguards TEXT,
|
||||
retention_period VARCHAR(255),
|
||||
retention_policy_id UUID,
|
||||
tom_reference JSONB DEFAULT '[]',
|
||||
dsfa_required BOOLEAN DEFAULT FALSE,
|
||||
dsfa_id UUID,
|
||||
responsible_person VARCHAR(255),
|
||||
responsible_department VARCHAR(255),
|
||||
systems JSONB DEFAULT '[]',
|
||||
status VARCHAR(50) DEFAULT 'draft', -- draft, active, under_review, archived
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL,
|
||||
last_reviewed_at TIMESTAMPTZ,
|
||||
next_review_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_pa_tenant ON dsgvo_processing_activities(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_pa_status ON dsgvo_processing_activities(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_pa_namespace ON dsgvo_processing_activities(namespace_id) WHERE namespace_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- DSFA - Datenschutz-Folgenabschätzung (Art. 35 DSGVO)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_dsfa (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
processing_activity_id UUID REFERENCES dsgvo_processing_activities(id) ON DELETE SET NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
processing_description TEXT,
|
||||
necessity_assessment TEXT,
|
||||
proportionality_assessment TEXT,
|
||||
risks JSONB DEFAULT '[]',
|
||||
mitigations JSONB DEFAULT '[]',
|
||||
dpo_consulted BOOLEAN DEFAULT FALSE,
|
||||
dpo_opinion TEXT,
|
||||
authority_consulted BOOLEAN DEFAULT FALSE,
|
||||
authority_reference VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'draft', -- draft, in_progress, completed, approved, rejected
|
||||
overall_risk_level VARCHAR(20), -- low, medium, high, very_high
|
||||
conclusion TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL,
|
||||
approved_by UUID,
|
||||
approved_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsfa_tenant ON dsgvo_dsfa(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsfa_status ON dsgvo_dsfa(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsfa_pa ON dsgvo_dsfa(processing_activity_id) WHERE processing_activity_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- TOM - Technische und Organisatorische Maßnahmen (Art. 32 DSGVO)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_tom (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
category VARCHAR(50) NOT NULL, -- access_control, encryption, pseudonymization, etc.
|
||||
subcategory VARCHAR(100),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
type VARCHAR(20) NOT NULL, -- technical, organizational
|
||||
implementation_status VARCHAR(50) DEFAULT 'planned', -- planned, in_progress, implemented, verified, not_applicable
|
||||
implemented_at TIMESTAMPTZ,
|
||||
verified_at TIMESTAMPTZ,
|
||||
verified_by UUID,
|
||||
effectiveness_rating VARCHAR(20), -- low, medium, high
|
||||
documentation TEXT,
|
||||
responsible_person VARCHAR(255),
|
||||
responsible_department VARCHAR(255),
|
||||
review_frequency VARCHAR(50), -- monthly, quarterly, annually
|
||||
last_review_at TIMESTAMPTZ,
|
||||
next_review_at TIMESTAMPTZ,
|
||||
related_controls JSONB DEFAULT '[]',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_tom_tenant ON dsgvo_tom(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_tom_category ON dsgvo_tom(tenant_id, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_tom_status ON dsgvo_tom(tenant_id, implementation_status);
|
||||
|
||||
-- ============================================================================
|
||||
-- DSR - Data Subject Requests / Betroffenenrechte (Art. 15-22 DSGVO)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_dsr (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
request_type VARCHAR(50) NOT NULL, -- access, rectification, erasure, restriction, portability, objection
|
||||
status VARCHAR(50) DEFAULT 'received', -- received, verified, in_progress, completed, rejected, extended
|
||||
subject_name VARCHAR(255) NOT NULL,
|
||||
subject_email VARCHAR(255) NOT NULL,
|
||||
subject_identifier VARCHAR(255),
|
||||
request_description TEXT,
|
||||
request_channel VARCHAR(50), -- email, form, phone, letter
|
||||
received_at TIMESTAMPTZ NOT NULL,
|
||||
verified_at TIMESTAMPTZ,
|
||||
verification_method VARCHAR(100),
|
||||
deadline_at TIMESTAMPTZ NOT NULL,
|
||||
extended_deadline_at TIMESTAMPTZ,
|
||||
extension_reason TEXT,
|
||||
completed_at TIMESTAMPTZ,
|
||||
response_sent BOOLEAN DEFAULT FALSE,
|
||||
response_sent_at TIMESTAMPTZ,
|
||||
response_method VARCHAR(50),
|
||||
rejection_reason TEXT,
|
||||
notes TEXT,
|
||||
affected_systems JSONB DEFAULT '[]',
|
||||
assigned_to UUID,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsr_tenant ON dsgvo_dsr(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsr_status ON dsgvo_dsr(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsr_deadline ON dsgvo_dsr(tenant_id, deadline_at) WHERE status NOT IN ('completed', 'rejected');
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_dsr_type ON dsgvo_dsr(tenant_id, request_type);
|
||||
|
||||
-- ============================================================================
|
||||
-- Retention Policies - Löschfristen (Art. 17 DSGVO)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_retention_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
data_category VARCHAR(100) NOT NULL,
|
||||
retention_period_days INT NOT NULL,
|
||||
retention_period_text VARCHAR(255), -- Human readable
|
||||
legal_basis VARCHAR(100),
|
||||
legal_reference VARCHAR(255), -- § 147 AO, § 257 HGB, etc.
|
||||
deletion_method VARCHAR(50), -- automatic, manual, anonymization
|
||||
deletion_procedure TEXT,
|
||||
exception_criteria TEXT,
|
||||
applicable_systems JSONB DEFAULT '[]',
|
||||
responsible_person VARCHAR(255),
|
||||
responsible_department VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'draft', -- draft, active, archived
|
||||
last_review_at TIMESTAMPTZ,
|
||||
next_review_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_retention_tenant ON dsgvo_retention_policies(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_retention_status ON dsgvo_retention_policies(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsgvo_retention_category ON dsgvo_retention_policies(tenant_id, data_category);
|
||||
|
||||
-- ============================================================================
|
||||
-- Insert default TOM categories as reference data
|
||||
-- ============================================================================
|
||||
|
||||
-- This is optional - the categories are also defined in code
|
||||
-- But having them in the database allows for easier UI population
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsgvo_tom_categories (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
name_de VARCHAR(255) NOT NULL,
|
||||
name_en VARCHAR(255) NOT NULL,
|
||||
description_de TEXT,
|
||||
article_reference VARCHAR(50)
|
||||
);
|
||||
|
||||
INSERT INTO dsgvo_tom_categories (id, name_de, name_en, description_de, article_reference) VALUES
|
||||
('access_control', 'Zutrittskontrolle', 'Physical Access Control', 'Maßnahmen zur Verhinderung des unbefugten Zutritts zu Datenverarbeitungsanlagen', 'Art. 32 Abs. 1 lit. b'),
|
||||
('admission_control', 'Zugangskontrolle', 'Logical Access Control', 'Maßnahmen zur Verhinderung der unbefugten Nutzung von DV-Systemen', 'Art. 32 Abs. 1 lit. b'),
|
||||
('access_management', 'Zugriffskontrolle', 'Access Management', 'Maßnahmen zur Gewährleistung, dass nur befugte Personen Zugriff auf Daten haben', 'Art. 32 Abs. 1 lit. b'),
|
||||
('transfer_control', 'Weitergabekontrolle', 'Transfer Control', 'Maßnahmen zur Verhinderung des unbefugten Lesens, Kopierens oder Entfernens bei der Übertragung', 'Art. 32 Abs. 1 lit. b'),
|
||||
('input_control', 'Eingabekontrolle', 'Input Control', 'Maßnahmen zur Nachvollziehbarkeit von Eingabe, Änderung und Löschung von Daten', 'Art. 32 Abs. 1 lit. b'),
|
||||
('availability_control', 'Verfügbarkeitskontrolle', 'Availability Control', 'Maßnahmen zum Schutz gegen zufällige oder mutwillige Zerstörung oder Verlust', 'Art. 32 Abs. 1 lit. b, c'),
|
||||
('separation_control', 'Trennungskontrolle', 'Separation Control', 'Maßnahmen zur getrennten Verarbeitung von Daten, die zu unterschiedlichen Zwecken erhoben wurden', 'Art. 32 Abs. 1 lit. b'),
|
||||
('encryption', 'Verschlüsselung', 'Encryption', 'Verschlüsselung personenbezogener Daten', 'Art. 32 Abs. 1 lit. a'),
|
||||
('pseudonymization', 'Pseudonymisierung', 'Pseudonymization', 'Verarbeitung in einer Weise, dass die Daten ohne zusätzliche Informationen nicht mehr zugeordnet werden können', 'Art. 32 Abs. 1 lit. a'),
|
||||
('resilience', 'Belastbarkeit', 'Resilience', 'Fähigkeit, die Verfügbarkeit und den Zugang bei einem Zwischenfall rasch wiederherzustellen', 'Art. 32 Abs. 1 lit. b, c'),
|
||||
('recovery', 'Wiederherstellung', 'Recovery', 'Verfahren zur Wiederherstellung der Verfügbarkeit und des Zugangs', 'Art. 32 Abs. 1 lit. c'),
|
||||
('testing', 'Regelmäßige Überprüfung', 'Regular Testing', 'Verfahren zur regelmäßigen Überprüfung, Bewertung und Evaluierung der Wirksamkeit', 'Art. 32 Abs. 1 lit. d')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
96
docs-src/ai-compliance-sdk/migrations/003_ucca_schema.sql
Normal file
96
docs-src/ai-compliance-sdk/migrations/003_ucca_schema.sql
Normal file
@@ -0,0 +1,96 @@
|
||||
-- Migration 003: UCCA (Use-Case Compliance & Feasibility Advisor) Schema
|
||||
-- Creates table for storing AI use-case assessments
|
||||
|
||||
-- ============================================================================
|
||||
-- UCCA Assessments Table
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ucca_assessments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
-- Metadata
|
||||
title VARCHAR(500),
|
||||
policy_version VARCHAR(50) NOT NULL DEFAULT '1.0.0',
|
||||
status VARCHAR(50) DEFAULT 'completed',
|
||||
|
||||
-- Input
|
||||
intake JSONB NOT NULL, -- Full UseCaseIntake
|
||||
use_case_text_stored BOOLEAN DEFAULT FALSE, -- Opt-in for raw text storage
|
||||
use_case_text_hash VARCHAR(64), -- SHA-256 hash (always stored)
|
||||
|
||||
-- Results - Main verdict
|
||||
feasibility VARCHAR(20) NOT NULL, -- YES/CONDITIONAL/NO
|
||||
risk_level VARCHAR(20) NOT NULL, -- MINIMAL/LOW/MEDIUM/HIGH/UNACCEPTABLE
|
||||
complexity VARCHAR(10) NOT NULL, -- LOW/MEDIUM/HIGH
|
||||
risk_score INT NOT NULL DEFAULT 0, -- 0-100
|
||||
|
||||
-- Results - Details (JSONB for flexibility)
|
||||
triggered_rules JSONB DEFAULT '[]', -- Array of TriggeredRule
|
||||
required_controls JSONB DEFAULT '[]', -- Array of RequiredControl
|
||||
recommended_architecture JSONB DEFAULT '[]', -- Array of PatternRecommendation
|
||||
forbidden_patterns JSONB DEFAULT '[]', -- Array of ForbiddenPattern
|
||||
example_matches JSONB DEFAULT '[]', -- Array of ExampleMatch
|
||||
|
||||
-- Results - Flags
|
||||
dsfa_recommended BOOLEAN DEFAULT FALSE,
|
||||
art22_risk BOOLEAN DEFAULT FALSE, -- Art. 22 GDPR automated decision risk
|
||||
training_allowed VARCHAR(50), -- YES/CONDITIONAL/NO
|
||||
|
||||
-- LLM Explanation (optional)
|
||||
explanation_text TEXT,
|
||||
explanation_generated_at TIMESTAMPTZ,
|
||||
explanation_model VARCHAR(100),
|
||||
|
||||
-- Domain classification
|
||||
domain VARCHAR(50),
|
||||
|
||||
-- Audit trail
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes for Performance
|
||||
-- ============================================================================
|
||||
|
||||
-- Primary lookup by tenant
|
||||
CREATE INDEX idx_ucca_tenant ON ucca_assessments(tenant_id);
|
||||
|
||||
-- List view with sorting by date
|
||||
CREATE INDEX idx_ucca_tenant_created ON ucca_assessments(tenant_id, created_at DESC);
|
||||
|
||||
-- Filter by feasibility
|
||||
CREATE INDEX idx_ucca_tenant_feasibility ON ucca_assessments(tenant_id, feasibility);
|
||||
|
||||
-- Filter by domain
|
||||
CREATE INDEX idx_ucca_tenant_domain ON ucca_assessments(tenant_id, domain);
|
||||
|
||||
-- Filter by risk level
|
||||
CREATE INDEX idx_ucca_tenant_risk ON ucca_assessments(tenant_id, risk_level);
|
||||
|
||||
-- JSONB index for searching within triggered_rules
|
||||
CREATE INDEX idx_ucca_triggered_rules ON ucca_assessments USING GIN (triggered_rules);
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments for Documentation
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE ucca_assessments IS 'UCCA (Use-Case Compliance & Feasibility Advisor) assessments - stores evaluated AI use cases with GDPR compliance verdicts';
|
||||
|
||||
COMMENT ON COLUMN ucca_assessments.intake IS 'Full UseCaseIntake JSON including data types, purpose, automation level, hosting, etc.';
|
||||
COMMENT ON COLUMN ucca_assessments.use_case_text_stored IS 'Whether the raw use case description text is stored (opt-in)';
|
||||
COMMENT ON COLUMN ucca_assessments.use_case_text_hash IS 'SHA-256 hash of use case text for deduplication without storing raw text';
|
||||
COMMENT ON COLUMN ucca_assessments.feasibility IS 'Overall verdict: YES (low risk), CONDITIONAL (needs controls), NO (not allowed)';
|
||||
COMMENT ON COLUMN ucca_assessments.risk_score IS 'Numeric risk score 0-100 calculated from triggered rules';
|
||||
COMMENT ON COLUMN ucca_assessments.triggered_rules IS 'Array of rules that were triggered during evaluation';
|
||||
COMMENT ON COLUMN ucca_assessments.required_controls IS 'Array of controls/mitigations that must be implemented';
|
||||
COMMENT ON COLUMN ucca_assessments.recommended_architecture IS 'Array of recommended architecture patterns';
|
||||
COMMENT ON COLUMN ucca_assessments.forbidden_patterns IS 'Array of patterns that must NOT be used';
|
||||
COMMENT ON COLUMN ucca_assessments.example_matches IS 'Array of matching didactic examples';
|
||||
COMMENT ON COLUMN ucca_assessments.dsfa_recommended IS 'Whether a Data Protection Impact Assessment is recommended';
|
||||
COMMENT ON COLUMN ucca_assessments.art22_risk IS 'Whether there is risk under Art. 22 GDPR (automated individual decisions)';
|
||||
COMMENT ON COLUMN ucca_assessments.training_allowed IS 'Whether model training with the data is allowed';
|
||||
COMMENT ON COLUMN ucca_assessments.explanation_text IS 'LLM-generated explanation in German (optional)';
|
||||
168
docs-src/ai-compliance-sdk/migrations/004_ucca_escalations.sql
Normal file
168
docs-src/ai-compliance-sdk/migrations/004_ucca_escalations.sql
Normal file
@@ -0,0 +1,168 @@
|
||||
-- Migration 004: UCCA Escalation Workflow
|
||||
-- Implements E0-E3 escalation levels with DSB routing
|
||||
|
||||
-- ============================================================================
|
||||
-- Escalation Levels (Reference)
|
||||
-- ============================================================================
|
||||
-- E0: Auto-Approve - Only INFO rules triggered, Risk < 20
|
||||
-- E1: Team-Lead Review - WARN rules OR Risk 20-40
|
||||
-- E2: DSB Consultation - Art. 9 data OR Risk 40-60 OR DSFA recommended
|
||||
-- E3: DSB + Legal - BLOCK rules OR Risk > 60 OR Art. 22 risk
|
||||
|
||||
-- ============================================================================
|
||||
-- Escalation Queue Table
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ucca_escalations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
assessment_id UUID NOT NULL REFERENCES ucca_assessments(id) ON DELETE CASCADE,
|
||||
|
||||
-- Escalation Level
|
||||
escalation_level VARCHAR(10) NOT NULL CHECK (escalation_level IN ('E0', 'E1', 'E2', 'E3')),
|
||||
escalation_reason TEXT NOT NULL,
|
||||
|
||||
-- Routing
|
||||
assigned_to UUID, -- User ID of assignee (DSB, Team Lead, etc.)
|
||||
assigned_role VARCHAR(50), -- Role for assignment (dsb, team_lead, legal)
|
||||
assigned_at TIMESTAMPTZ,
|
||||
|
||||
-- Status
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending', 'assigned', 'in_review', 'approved', 'rejected', 'returned')),
|
||||
|
||||
-- Review
|
||||
reviewer_id UUID,
|
||||
reviewer_notes TEXT,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
|
||||
-- Decision
|
||||
decision VARCHAR(20) CHECK (decision IN ('approve', 'reject', 'modify', 'escalate')),
|
||||
decision_notes TEXT,
|
||||
decision_at TIMESTAMPTZ,
|
||||
|
||||
-- Conditions for approval
|
||||
conditions JSONB DEFAULT '[]', -- Array of conditions that must be met
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
due_date TIMESTAMPTZ, -- SLA deadline
|
||||
|
||||
-- Notifications sent
|
||||
notification_sent BOOLEAN DEFAULT FALSE,
|
||||
notification_sent_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Escalation History (Audit Trail)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ucca_escalation_history (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
escalation_id UUID NOT NULL REFERENCES ucca_escalations(id) ON DELETE CASCADE,
|
||||
|
||||
-- What changed
|
||||
action VARCHAR(50) NOT NULL, -- created, assigned, reviewed, decided, escalated, etc.
|
||||
old_status VARCHAR(30),
|
||||
new_status VARCHAR(30),
|
||||
old_level VARCHAR(10),
|
||||
new_level VARCHAR(10),
|
||||
|
||||
-- Who and when
|
||||
actor_id UUID NOT NULL,
|
||||
actor_role VARCHAR(50),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- DSB Assignment Pool
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ucca_dsb_pool (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL,
|
||||
user_name VARCHAR(255) NOT NULL,
|
||||
user_email VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL DEFAULT 'dsb', -- dsb, deputy_dsb, legal
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
max_concurrent_reviews INT DEFAULT 10,
|
||||
current_reviews INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
UNIQUE(tenant_id, user_id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- SLA Configuration per Escalation Level
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ucca_escalation_sla (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
escalation_level VARCHAR(10) NOT NULL CHECK (escalation_level IN ('E0', 'E1', 'E2', 'E3')),
|
||||
|
||||
-- SLA settings
|
||||
response_hours INT NOT NULL DEFAULT 24, -- Hours to first response
|
||||
resolution_hours INT NOT NULL DEFAULT 72, -- Hours to resolution
|
||||
|
||||
-- Notification settings
|
||||
notify_on_creation BOOLEAN DEFAULT TRUE,
|
||||
notify_on_approaching_sla BOOLEAN DEFAULT TRUE,
|
||||
notify_on_sla_breach BOOLEAN DEFAULT TRUE,
|
||||
approaching_sla_hours INT DEFAULT 8, -- Notify X hours before SLA breach
|
||||
|
||||
-- Auto-escalation
|
||||
auto_escalate_on_breach BOOLEAN DEFAULT FALSE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
UNIQUE(tenant_id, escalation_level)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Fast lookup by tenant and status
|
||||
CREATE INDEX idx_ucca_escalations_tenant_status ON ucca_escalations(tenant_id, status);
|
||||
|
||||
-- Fast lookup by assignee
|
||||
CREATE INDEX idx_ucca_escalations_assigned ON ucca_escalations(assigned_to, status);
|
||||
|
||||
-- Fast lookup by assessment
|
||||
CREATE INDEX idx_ucca_escalations_assessment ON ucca_escalations(assessment_id);
|
||||
|
||||
-- SLA monitoring (find escalations approaching or past due date)
|
||||
CREATE INDEX idx_ucca_escalations_due ON ucca_escalations(due_date) WHERE status NOT IN ('approved', 'rejected');
|
||||
|
||||
-- History lookup
|
||||
CREATE INDEX idx_ucca_escalation_history_escalation ON ucca_escalation_history(escalation_id);
|
||||
|
||||
-- DSB pool lookup
|
||||
CREATE INDEX idx_ucca_dsb_pool_tenant ON ucca_dsb_pool(tenant_id, is_active);
|
||||
|
||||
-- ============================================================================
|
||||
-- Default SLA Values (inserted on first use)
|
||||
-- ============================================================================
|
||||
|
||||
-- Note: These will be inserted per-tenant when needed via application logic
|
||||
-- E0: Auto-approve, no SLA
|
||||
-- E1: 24h response, 72h resolution
|
||||
-- E2: 8h response, 48h resolution
|
||||
-- E3: 4h response, 24h resolution (urgent)
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE ucca_escalations IS 'UCCA escalation queue for assessments requiring review';
|
||||
COMMENT ON COLUMN ucca_escalations.escalation_level IS 'E0=Auto, E1=Team, E2=DSB, E3=DSB+Legal';
|
||||
COMMENT ON COLUMN ucca_escalations.conditions IS 'JSON array of conditions required for approval';
|
||||
COMMENT ON TABLE ucca_escalation_history IS 'Audit trail of all escalation state changes';
|
||||
COMMENT ON TABLE ucca_dsb_pool IS 'Pool of DSB/Legal reviewers for assignment';
|
||||
COMMENT ON TABLE ucca_escalation_sla IS 'SLA configuration per escalation level per tenant';
|
||||
193
docs-src/ai-compliance-sdk/migrations/005_roadmap_schema.sql
Normal file
193
docs-src/ai-compliance-sdk/migrations/005_roadmap_schema.sql
Normal file
@@ -0,0 +1,193 @@
|
||||
-- ============================================================================
|
||||
-- Migration 005: Roadmap Schema
|
||||
-- Compliance Roadmap Management with Import Support
|
||||
-- ============================================================================
|
||||
|
||||
-- Roadmaps table
|
||||
CREATE TABLE IF NOT EXISTS roadmaps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
version VARCHAR(50) DEFAULT '1.0',
|
||||
|
||||
-- Links to other entities
|
||||
assessment_id UUID REFERENCES ucca_assessments(id) ON DELETE SET NULL,
|
||||
portfolio_id UUID, -- Will reference portfolio table when created
|
||||
|
||||
-- Status tracking
|
||||
status VARCHAR(50) DEFAULT 'draft', -- draft, active, completed, archived
|
||||
total_items INT DEFAULT 0,
|
||||
completed_items INT DEFAULT 0,
|
||||
progress INT DEFAULT 0, -- Percentage 0-100
|
||||
|
||||
-- Timeline
|
||||
start_date DATE,
|
||||
target_date DATE,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- Roadmap items table
|
||||
CREATE TABLE IF NOT EXISTS roadmap_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
roadmap_id UUID NOT NULL REFERENCES roadmaps(id) ON DELETE CASCADE,
|
||||
|
||||
-- Core fields
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50) DEFAULT 'TECHNICAL', -- TECHNICAL, ORGANIZATIONAL, PROCESSUAL, DOCUMENTATION, TRAINING
|
||||
priority VARCHAR(50) DEFAULT 'MEDIUM', -- CRITICAL, HIGH, MEDIUM, LOW
|
||||
status VARCHAR(50) DEFAULT 'PLANNED', -- PLANNED, IN_PROGRESS, BLOCKED, COMPLETED, DEFERRED
|
||||
|
||||
-- Compliance mapping
|
||||
control_id VARCHAR(100), -- e.g., "CTRL-AVV"
|
||||
regulation_ref VARCHAR(255), -- e.g., "DSGVO Art. 28"
|
||||
gap_id VARCHAR(100), -- e.g., "GAP_AVV_MISSING"
|
||||
|
||||
-- Effort estimation
|
||||
effort_days INT,
|
||||
effort_hours INT,
|
||||
estimated_cost INT, -- EUR
|
||||
|
||||
-- Assignment
|
||||
assignee_id UUID,
|
||||
assignee_name VARCHAR(255),
|
||||
department VARCHAR(255),
|
||||
|
||||
-- Timeline
|
||||
planned_start DATE,
|
||||
planned_end DATE,
|
||||
actual_start DATE,
|
||||
actual_end DATE,
|
||||
|
||||
-- Dependencies (JSONB arrays of UUIDs)
|
||||
depends_on JSONB DEFAULT '[]',
|
||||
blocked_by JSONB DEFAULT '[]',
|
||||
|
||||
-- Evidence
|
||||
evidence_required JSONB DEFAULT '[]', -- Array of strings
|
||||
evidence_provided JSONB DEFAULT '[]', -- Array of strings
|
||||
|
||||
-- Notes
|
||||
notes TEXT,
|
||||
risk_notes TEXT,
|
||||
|
||||
-- Import metadata
|
||||
source_row INT,
|
||||
source_file VARCHAR(500),
|
||||
|
||||
-- Ordering
|
||||
sort_order INT DEFAULT 0,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Import jobs table
|
||||
CREATE TABLE IF NOT EXISTS roadmap_import_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
roadmap_id UUID REFERENCES roadmaps(id) ON DELETE SET NULL,
|
||||
|
||||
-- File info
|
||||
filename VARCHAR(500) NOT NULL,
|
||||
format VARCHAR(50) NOT NULL, -- EXCEL, CSV, JSON
|
||||
file_size BIGINT,
|
||||
content_type VARCHAR(255),
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'pending', -- pending, parsing, parsed, validating, completed, failed
|
||||
error_message TEXT,
|
||||
|
||||
-- Parsing results
|
||||
total_rows INT DEFAULT 0,
|
||||
valid_rows INT DEFAULT 0,
|
||||
invalid_rows INT DEFAULT 0,
|
||||
imported_items INT DEFAULT 0,
|
||||
|
||||
-- Parsed items (before confirmation)
|
||||
parsed_items JSONB DEFAULT '[]',
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Roadmaps indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmaps_tenant ON roadmaps(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmaps_status ON roadmaps(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmaps_assessment ON roadmaps(assessment_id) WHERE assessment_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmaps_portfolio ON roadmaps(portfolio_id) WHERE portfolio_id IS NOT NULL;
|
||||
|
||||
-- Roadmap items indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_roadmap ON roadmap_items(roadmap_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_status ON roadmap_items(roadmap_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_priority ON roadmap_items(roadmap_id, priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_category ON roadmap_items(roadmap_id, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_assignee ON roadmap_items(assignee_id) WHERE assignee_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_control ON roadmap_items(control_id) WHERE control_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_deadline ON roadmap_items(planned_end) WHERE planned_end IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_roadmap_items_sort ON roadmap_items(roadmap_id, sort_order);
|
||||
|
||||
-- Import jobs indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_import_jobs_tenant ON roadmap_import_jobs(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_import_jobs_status ON roadmap_import_jobs(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_import_jobs_roadmap ON roadmap_import_jobs(roadmap_id) WHERE roadmap_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers for updated_at
|
||||
-- ============================================================================
|
||||
|
||||
-- Trigger function (reuse if exists)
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Roadmaps trigger
|
||||
DROP TRIGGER IF EXISTS update_roadmaps_updated_at ON roadmaps;
|
||||
CREATE TRIGGER update_roadmaps_updated_at
|
||||
BEFORE UPDATE ON roadmaps
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Roadmap items trigger
|
||||
DROP TRIGGER IF EXISTS update_roadmap_items_updated_at ON roadmap_items;
|
||||
CREATE TRIGGER update_roadmap_items_updated_at
|
||||
BEFORE UPDATE ON roadmap_items
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Import jobs trigger
|
||||
DROP TRIGGER IF EXISTS update_roadmap_import_jobs_updated_at ON roadmap_import_jobs;
|
||||
CREATE TRIGGER update_roadmap_import_jobs_updated_at
|
||||
BEFORE UPDATE ON roadmap_import_jobs
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE roadmaps IS 'Compliance implementation roadmaps';
|
||||
COMMENT ON TABLE roadmap_items IS 'Individual items/tasks in a compliance roadmap';
|
||||
COMMENT ON TABLE roadmap_import_jobs IS 'Track file imports for roadmap items';
|
||||
|
||||
COMMENT ON COLUMN roadmap_items.control_id IS 'Reference to controls catalog (e.g., CTRL-AVV)';
|
||||
COMMENT ON COLUMN roadmap_items.regulation_ref IS 'Reference to regulation article (e.g., DSGVO Art. 28)';
|
||||
COMMENT ON COLUMN roadmap_items.gap_id IS 'Reference to gap mapping (e.g., GAP_AVV_MISSING)';
|
||||
COMMENT ON COLUMN roadmap_items.depends_on IS 'Array of item IDs this item depends on';
|
||||
COMMENT ON COLUMN roadmap_items.blocked_by IS 'Array of item IDs currently blocking this item';
|
||||
207
docs-src/ai-compliance-sdk/migrations/006_workshop_schema.sql
Normal file
207
docs-src/ai-compliance-sdk/migrations/006_workshop_schema.sql
Normal file
@@ -0,0 +1,207 @@
|
||||
-- ============================================================================
|
||||
-- Migration 006: Workshop Session Schema
|
||||
-- Collaborative Compliance Workshop Sessions
|
||||
-- ============================================================================
|
||||
|
||||
-- Workshop sessions table
|
||||
CREATE TABLE IF NOT EXISTS workshop_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
-- Session info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
session_type VARCHAR(50) NOT NULL, -- 'ucca', 'dsfa', 'custom'
|
||||
status VARCHAR(50) DEFAULT 'DRAFT', -- DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, CANCELLED
|
||||
|
||||
-- Wizard configuration
|
||||
wizard_schema VARCHAR(100), -- Reference to wizard schema version
|
||||
current_step INT DEFAULT 1,
|
||||
total_steps INT DEFAULT 10,
|
||||
|
||||
-- Links to other entities
|
||||
assessment_id UUID REFERENCES ucca_assessments(id) ON DELETE SET NULL,
|
||||
roadmap_id UUID REFERENCES roadmaps(id) ON DELETE SET NULL,
|
||||
portfolio_id UUID, -- Will reference portfolio table when created
|
||||
|
||||
-- Scheduling
|
||||
scheduled_start TIMESTAMPTZ,
|
||||
scheduled_end TIMESTAMPTZ,
|
||||
actual_start TIMESTAMPTZ,
|
||||
actual_end TIMESTAMPTZ,
|
||||
|
||||
-- Access control
|
||||
join_code VARCHAR(10) NOT NULL UNIQUE,
|
||||
require_auth BOOLEAN DEFAULT FALSE,
|
||||
allow_anonymous BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Settings (JSONB)
|
||||
settings JSONB DEFAULT '{}',
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- Workshop participants table
|
||||
CREATE TABLE IF NOT EXISTS workshop_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_id UUID NOT NULL REFERENCES workshop_sessions(id) ON DELETE CASCADE,
|
||||
user_id UUID, -- Null for anonymous participants
|
||||
|
||||
-- Info
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255),
|
||||
role VARCHAR(50) DEFAULT 'STAKEHOLDER', -- FACILITATOR, EXPERT, STAKEHOLDER, OBSERVER
|
||||
department VARCHAR(255),
|
||||
|
||||
-- Status
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
last_active_at TIMESTAMPTZ,
|
||||
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
left_at TIMESTAMPTZ,
|
||||
|
||||
-- Permissions
|
||||
can_edit BOOLEAN DEFAULT TRUE,
|
||||
can_comment BOOLEAN DEFAULT TRUE,
|
||||
can_approve BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Workshop step progress table
|
||||
CREATE TABLE IF NOT EXISTS workshop_step_progress (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_id UUID NOT NULL REFERENCES workshop_sessions(id) ON DELETE CASCADE,
|
||||
step_number INT NOT NULL,
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'pending', -- pending, in_progress, completed, skipped
|
||||
progress INT DEFAULT 0, -- 0-100
|
||||
|
||||
-- Timestamps
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
-- Notes
|
||||
notes TEXT,
|
||||
|
||||
UNIQUE(session_id, step_number)
|
||||
);
|
||||
|
||||
-- Workshop responses table
|
||||
CREATE TABLE IF NOT EXISTS workshop_responses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_id UUID NOT NULL REFERENCES workshop_sessions(id) ON DELETE CASCADE,
|
||||
participant_id UUID NOT NULL REFERENCES workshop_participants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Question reference
|
||||
step_number INT NOT NULL,
|
||||
field_id VARCHAR(100) NOT NULL,
|
||||
|
||||
-- Response data
|
||||
value JSONB, -- Can be any JSON type
|
||||
value_type VARCHAR(50), -- string, boolean, array, number, object
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'SUBMITTED', -- PENDING, DRAFT, SUBMITTED, REVIEWED
|
||||
|
||||
-- Review
|
||||
reviewed_by UUID,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
review_notes TEXT,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Unique constraint per participant per field
|
||||
UNIQUE(session_id, participant_id, field_id)
|
||||
);
|
||||
|
||||
-- Workshop comments table
|
||||
CREATE TABLE IF NOT EXISTS workshop_comments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_id UUID NOT NULL REFERENCES workshop_sessions(id) ON DELETE CASCADE,
|
||||
participant_id UUID NOT NULL REFERENCES workshop_participants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Target (one of these should be set)
|
||||
step_number INT,
|
||||
field_id VARCHAR(100),
|
||||
response_id UUID REFERENCES workshop_responses(id) ON DELETE CASCADE,
|
||||
|
||||
-- Content
|
||||
text TEXT NOT NULL,
|
||||
is_resolved BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Session indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_sessions_tenant ON workshop_sessions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_sessions_status ON workshop_sessions(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_sessions_join_code ON workshop_sessions(join_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_sessions_assessment ON workshop_sessions(assessment_id) WHERE assessment_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_sessions_created_by ON workshop_sessions(created_by);
|
||||
|
||||
-- Participant indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_participants_session ON workshop_participants(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_participants_user ON workshop_participants(user_id) WHERE user_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_participants_active ON workshop_participants(session_id, is_active);
|
||||
|
||||
-- Step progress indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_step_progress_session ON workshop_step_progress(session_id);
|
||||
|
||||
-- Response indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_responses_session ON workshop_responses(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_responses_participant ON workshop_responses(participant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_responses_step ON workshop_responses(session_id, step_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_responses_field ON workshop_responses(session_id, field_id);
|
||||
|
||||
-- Comment indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_comments_session ON workshop_comments(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workshop_comments_response ON workshop_comments(response_id) WHERE response_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Sessions trigger
|
||||
DROP TRIGGER IF EXISTS update_workshop_sessions_updated_at ON workshop_sessions;
|
||||
CREATE TRIGGER update_workshop_sessions_updated_at
|
||||
BEFORE UPDATE ON workshop_sessions
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Responses trigger
|
||||
DROP TRIGGER IF EXISTS update_workshop_responses_updated_at ON workshop_responses;
|
||||
CREATE TRIGGER update_workshop_responses_updated_at
|
||||
BEFORE UPDATE ON workshop_responses
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Comments trigger
|
||||
DROP TRIGGER IF EXISTS update_workshop_comments_updated_at ON workshop_comments;
|
||||
CREATE TRIGGER update_workshop_comments_updated_at
|
||||
BEFORE UPDATE ON workshop_comments
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE workshop_sessions IS 'Collaborative compliance workshop sessions';
|
||||
COMMENT ON TABLE workshop_participants IS 'Participants in workshop sessions';
|
||||
COMMENT ON TABLE workshop_step_progress IS 'Progress tracking for each wizard step';
|
||||
COMMENT ON TABLE workshop_responses IS 'Participant responses to wizard questions';
|
||||
COMMENT ON TABLE workshop_comments IS 'Comments and discussions on responses';
|
||||
|
||||
COMMENT ON COLUMN workshop_sessions.join_code IS 'Code for participants to join the session';
|
||||
COMMENT ON COLUMN workshop_sessions.settings IS 'JSON settings (allow_back_navigation, require_all_responses, etc.)';
|
||||
COMMENT ON COLUMN workshop_responses.value IS 'JSON response value (can be any type)';
|
||||
267
docs-src/ai-compliance-sdk/migrations/007_portfolio_schema.sql
Normal file
267
docs-src/ai-compliance-sdk/migrations/007_portfolio_schema.sql
Normal file
@@ -0,0 +1,267 @@
|
||||
-- ============================================================================
|
||||
-- Migration 007: Portfolio Schema
|
||||
-- AI Use Case Portfolio Management with Merge Support
|
||||
-- ============================================================================
|
||||
|
||||
-- Portfolios table
|
||||
CREATE TABLE IF NOT EXISTS portfolios (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
-- Info
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(50) DEFAULT 'DRAFT', -- DRAFT, ACTIVE, REVIEW, APPROVED, ARCHIVED
|
||||
|
||||
-- Organization
|
||||
department VARCHAR(255),
|
||||
business_unit VARCHAR(255),
|
||||
owner VARCHAR(255),
|
||||
owner_email VARCHAR(255),
|
||||
|
||||
-- Aggregated metrics (computed)
|
||||
total_assessments INT DEFAULT 0,
|
||||
total_roadmaps INT DEFAULT 0,
|
||||
total_workshops INT DEFAULT 0,
|
||||
avg_risk_score DECIMAL(5,2) DEFAULT 0,
|
||||
high_risk_count INT DEFAULT 0,
|
||||
conditional_count INT DEFAULT 0,
|
||||
approved_count INT DEFAULT 0,
|
||||
compliance_score DECIMAL(5,2) DEFAULT 0, -- 0-100
|
||||
|
||||
-- Settings (JSONB)
|
||||
settings JSONB DEFAULT '{}',
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL,
|
||||
approved_at TIMESTAMPTZ,
|
||||
approved_by UUID
|
||||
);
|
||||
|
||||
-- Portfolio items table (links portfolios to assessments, roadmaps, workshops)
|
||||
CREATE TABLE IF NOT EXISTS portfolio_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
portfolio_id UUID NOT NULL REFERENCES portfolios(id) ON DELETE CASCADE,
|
||||
item_type VARCHAR(50) NOT NULL, -- ASSESSMENT, ROADMAP, WORKSHOP, DOCUMENT
|
||||
item_id UUID NOT NULL,
|
||||
|
||||
-- Cached info from the linked item
|
||||
title VARCHAR(500),
|
||||
status VARCHAR(50),
|
||||
risk_level VARCHAR(20),
|
||||
risk_score INT DEFAULT 0,
|
||||
feasibility VARCHAR(20),
|
||||
|
||||
-- Ordering and categorization
|
||||
sort_order INT DEFAULT 0,
|
||||
tags JSONB DEFAULT '[]',
|
||||
notes TEXT,
|
||||
|
||||
-- Audit
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
added_by UUID NOT NULL,
|
||||
|
||||
-- Unique constraint: item can only be in portfolio once
|
||||
UNIQUE(portfolio_id, item_id)
|
||||
);
|
||||
|
||||
-- Portfolio activity log table
|
||||
CREATE TABLE IF NOT EXISTS portfolio_activity (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
portfolio_id UUID NOT NULL REFERENCES portfolios(id) ON DELETE CASCADE,
|
||||
|
||||
-- Activity info
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
action VARCHAR(50) NOT NULL, -- added, removed, updated, merged, approved, submitted
|
||||
item_type VARCHAR(50),
|
||||
item_id UUID,
|
||||
item_title VARCHAR(500),
|
||||
user_id UUID NOT NULL,
|
||||
|
||||
-- Additional details
|
||||
details JSONB
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Portfolio indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_tenant ON portfolios(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_tenant_status ON portfolios(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_department ON portfolios(tenant_id, department) WHERE department IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_business_unit ON portfolios(tenant_id, business_unit) WHERE business_unit IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_owner ON portfolios(owner) WHERE owner IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_created_by ON portfolios(created_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolios_risk_score ON portfolios(avg_risk_score);
|
||||
|
||||
-- Portfolio item indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_portfolio ON portfolio_items(portfolio_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_type ON portfolio_items(portfolio_id, item_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_item ON portfolio_items(item_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_risk ON portfolio_items(portfolio_id, risk_level);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_feasibility ON portfolio_items(portfolio_id, feasibility);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_items_sort ON portfolio_items(portfolio_id, sort_order);
|
||||
|
||||
-- Activity indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_activity_portfolio ON portfolio_activity(portfolio_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_activity_timestamp ON portfolio_activity(portfolio_id, timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_portfolio_activity_user ON portfolio_activity(user_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Portfolios trigger
|
||||
DROP TRIGGER IF EXISTS update_portfolios_updated_at ON portfolios;
|
||||
CREATE TRIGGER update_portfolios_updated_at
|
||||
BEFORE UPDATE ON portfolios
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Functions for Metrics Calculation
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to recalculate portfolio metrics
|
||||
CREATE OR REPLACE FUNCTION recalculate_portfolio_metrics(p_portfolio_id UUID)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
v_total_assessments INT;
|
||||
v_total_roadmaps INT;
|
||||
v_total_workshops INT;
|
||||
v_avg_risk DECIMAL(5,2);
|
||||
v_high_risk INT;
|
||||
v_conditional INT;
|
||||
v_approved INT;
|
||||
v_compliance DECIMAL(5,2);
|
||||
BEGIN
|
||||
-- Count by type
|
||||
SELECT COUNT(*) INTO v_total_assessments
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id AND item_type = 'ASSESSMENT';
|
||||
|
||||
SELECT COUNT(*) INTO v_total_roadmaps
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id AND item_type = 'ROADMAP';
|
||||
|
||||
SELECT COUNT(*) INTO v_total_workshops
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id AND item_type = 'WORKSHOP';
|
||||
|
||||
-- Calculate risk metrics
|
||||
SELECT COALESCE(AVG(risk_score), 0) INTO v_avg_risk
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id AND item_type = 'ASSESSMENT';
|
||||
|
||||
SELECT COUNT(*) INTO v_high_risk
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id
|
||||
AND item_type = 'ASSESSMENT'
|
||||
AND risk_level IN ('HIGH', 'UNACCEPTABLE');
|
||||
|
||||
SELECT COUNT(*) INTO v_conditional
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id
|
||||
AND item_type = 'ASSESSMENT'
|
||||
AND feasibility = 'CONDITIONAL';
|
||||
|
||||
SELECT COUNT(*) INTO v_approved
|
||||
FROM portfolio_items
|
||||
WHERE portfolio_id = p_portfolio_id
|
||||
AND item_type = 'ASSESSMENT'
|
||||
AND feasibility = 'YES';
|
||||
|
||||
-- Calculate compliance score
|
||||
IF v_total_assessments > 0 THEN
|
||||
v_compliance := (v_approved::DECIMAL / v_total_assessments) * 100;
|
||||
ELSE
|
||||
v_compliance := 0;
|
||||
END IF;
|
||||
|
||||
-- Update portfolio
|
||||
UPDATE portfolios SET
|
||||
total_assessments = v_total_assessments,
|
||||
total_roadmaps = v_total_roadmaps,
|
||||
total_workshops = v_total_workshops,
|
||||
avg_risk_score = v_avg_risk,
|
||||
high_risk_count = v_high_risk,
|
||||
conditional_count = v_conditional,
|
||||
approved_count = v_approved,
|
||||
compliance_score = v_compliance,
|
||||
updated_at = NOW()
|
||||
WHERE id = p_portfolio_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger function to auto-update metrics on item changes
|
||||
CREATE OR REPLACE FUNCTION portfolio_items_metrics_trigger()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
PERFORM recalculate_portfolio_metrics(OLD.portfolio_id);
|
||||
RETURN OLD;
|
||||
ELSE
|
||||
PERFORM recalculate_portfolio_metrics(NEW.portfolio_id);
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger for auto metrics update
|
||||
DROP TRIGGER IF EXISTS trg_portfolio_items_metrics ON portfolio_items;
|
||||
CREATE TRIGGER trg_portfolio_items_metrics
|
||||
AFTER INSERT OR UPDATE OR DELETE ON portfolio_items
|
||||
FOR EACH ROW EXECUTE FUNCTION portfolio_items_metrics_trigger();
|
||||
|
||||
-- ============================================================================
|
||||
-- Views
|
||||
-- ============================================================================
|
||||
|
||||
-- View for portfolio summary with counts
|
||||
CREATE OR REPLACE VIEW portfolio_summary_view AS
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.name,
|
||||
p.description,
|
||||
p.status,
|
||||
p.department,
|
||||
p.business_unit,
|
||||
p.owner,
|
||||
p.total_assessments,
|
||||
p.total_roadmaps,
|
||||
p.total_workshops,
|
||||
p.avg_risk_score,
|
||||
p.high_risk_count,
|
||||
p.conditional_count,
|
||||
p.approved_count,
|
||||
p.compliance_score,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
(p.total_assessments + p.total_roadmaps + p.total_workshops) as total_items,
|
||||
CASE
|
||||
WHEN p.high_risk_count > 0 THEN 'CRITICAL'
|
||||
WHEN p.conditional_count > p.approved_count THEN 'WARNING'
|
||||
ELSE 'GOOD'
|
||||
END as health_status
|
||||
FROM portfolios p;
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE portfolios IS 'AI use case portfolios for grouping and managing multiple assessments';
|
||||
COMMENT ON TABLE portfolio_items IS 'Items linked to portfolios (assessments, roadmaps, workshops)';
|
||||
COMMENT ON TABLE portfolio_activity IS 'Activity log for portfolio changes';
|
||||
|
||||
COMMENT ON COLUMN portfolios.compliance_score IS 'Percentage of assessments with YES feasibility (0-100)';
|
||||
COMMENT ON COLUMN portfolios.avg_risk_score IS 'Average risk score across all assessments in portfolio';
|
||||
COMMENT ON COLUMN portfolio_items.item_type IS 'Type of linked item: ASSESSMENT, ROADMAP, WORKSHOP, DOCUMENT';
|
||||
COMMENT ON COLUMN portfolio_items.sort_order IS 'Custom ordering within the portfolio';
|
||||
|
||||
COMMENT ON FUNCTION recalculate_portfolio_metrics(UUID) IS 'Recalculates aggregated metrics for a portfolio';
|
||||
Reference in New Issue
Block a user