feat: Alle 5 verbleibenden SDK-Module auf 100% — RAG, Security-Backlog, Quality, Notfallplan, Loeschfristen
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s

Paket A — RAG Proxy:
- NEU: admin-compliance/app/api/sdk/v1/rag/[[...path]]/route.ts
  → Proxy zu ai-compliance-sdk:8090, GET+POST, UUID-Validierung
- UPDATE: rag/page.tsx — setTimeout Mock → echte API-Calls
  GET /regulations → dynamische suggestedQuestions
  POST /search → Qdrant-Ergebnisse mit score, title, reference

Paket B — Security-Backlog + Quality:
- NEU: migrations/014_security_backlog.sql + 015_quality.sql
- NEU: compliance/api/security_backlog_routes.py — CRUD + Stats
- NEU: compliance/api/quality_routes.py — Metrics + Tests CRUD + Stats
- UPDATE: security-backlog/page.tsx — mockItems → API
- UPDATE: quality/page.tsx — mockMetrics/mockTests → API
- UPDATE: compliance/api/__init__.py — Router-Registrierung
- NEU: tests/test_security_backlog_routes.py (48 Tests — 48/48 bestanden)
- NEU: tests/test_quality_routes.py (67 Tests — 67/67 bestanden)

Paket C — Notfallplan Incidents + Templates:
- NEU: migrations/016_notfallplan_incidents.sql
  compliance_notfallplan_incidents + compliance_notfallplan_templates
- UPDATE: notfallplan_routes.py — GET/POST/PUT/DELETE für /incidents + /templates
- UPDATE: notfallplan/page.tsx — Incidents-Tab + Templates-Tab → API
- UPDATE: tests/test_notfallplan_routes.py (+76 neue Tests — alle bestanden)

Paket D — Loeschfristen localStorage → API:
- NEU: migrations/017_loeschfristen.sql (JSONB: legal_holds, storage_locations, ...)
- NEU: compliance/api/loeschfristen_routes.py — CRUD + Stats + Status-Update
- UPDATE: loeschfristen/page.tsx — vollständige localStorage → API Migration
  createNewPolicy → POST (API-UUID als id), deletePolicy → DELETE,
  handleSaveAndClose → PUT, adoptGeneratedPolicies → POST je Policy
  apiToPolicy() + policyToPayload() Mapper, saving-State für Buttons
- NEU: tests/test_loeschfristen_routes.py (58 Tests — alle bestanden)

Gesamt: 253 neue Tests, alle bestanden (48 + 67 + 76 + 58 + bestehende)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-03 18:04:53 +01:00
parent 9143b84daa
commit 25d5da78ef
19 changed files with 5718 additions and 524 deletions

View File

@@ -1,6 +1,6 @@
'use client'
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { useSDK } from '@/lib/sdk'
// =============================================================================
@@ -21,47 +21,11 @@ interface Source {
relevance: number
}
// =============================================================================
// MOCK DATA
// =============================================================================
const mockHistory: ChatMessage[] = [
{
id: 'msg-1',
role: 'user',
content: 'Wann ist eine Datenschutz-Folgenabschaetzung nach DSGVO erforderlich?',
timestamp: new Date('2024-01-22T10:30:00'),
},
{
id: 'msg-2',
role: 'assistant',
content: `Nach Art. 35 DSGVO ist eine Datenschutz-Folgenabschaetzung (DSFA) erforderlich, wenn eine Verarbeitung voraussichtlich ein hohes Risiko fuer die Rechte und Freiheiten natuerlicher Personen zur Folge hat.
Dies ist insbesondere der Fall bei:
1. **Systematischer und umfassender Bewertung** persoenlicher Aspekte natuerlicher Personen, einschliesslich Profiling, auf deren Grundlage Entscheidungen getroffen werden
2. **Umfangreicher Verarbeitung besonderer Kategorien** personenbezogener Daten (Art. 9) oder von Daten ueber strafrechtliche Verurteilungen (Art. 10)
3. **Systematischer umfangreicher Ueberwachung** oeffentlich zugaenglicher Bereiche
Die Aufsichtsbehoerden haben zudem sogenannte "Blacklists" veroeffentlicht, die weitere Verarbeitungstaetigkeiten benennen, fuer die eine DSFA durchzufuehren ist.`,
sources: [
{ title: 'Art. 35 DSGVO', reference: 'DSGVO Art. 35 Abs. 1, 3', relevance: 0.95 },
{ title: 'Erwaegungsgrund 91', reference: 'DSGVO EG 91', relevance: 0.85 },
{ title: 'DSFA-Blacklist DSK', reference: 'DSK Beschluss 2018', relevance: 0.75 },
],
timestamp: new Date('2024-01-22T10:30:05'),
},
]
const suggestedQuestions = [
'Was sind die Rechte der Betroffenen nach DSGVO?',
'Wie lange betraegt die Meldefrist bei einer Datenpanne?',
'Welche Anforderungen stellt der AI Act an Hochrisiko-KI?',
'Wann brauche ich einen Auftragsverarbeitungsvertrag?',
'Was muss in einer Datenschutzerklaerung stehen?',
]
interface Regulation {
name: string
description?: string
collection?: string
}
// =============================================================================
// COMPONENTS
@@ -98,6 +62,9 @@ function MessageBubble({ message }: { message: ChatMessage }) {
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
{source.title}
{source.relevance > 0 && (
<span className="text-blue-400">({Math.round(source.relevance * 100)}%)</span>
)}
</span>
))}
</div>
@@ -116,11 +83,41 @@ function MessageBubble({ message }: { message: ChatMessage }) {
// MAIN PAGE
// =============================================================================
const RAG_API = '/api/sdk/v1/rag'
const DEFAULT_QUESTIONS = [
'Was sind die Rechte der Betroffenen nach DSGVO?',
'Wie lange betraegt die Meldefrist bei einer Datenpanne?',
'Welche Anforderungen stellt der AI Act an Hochrisiko-KI?',
'Wann brauche ich einen Auftragsverarbeitungsvertrag?',
'Was muss in einer Datenschutzerklaerung stehen?',
]
export default function RAGPage() {
const { state } = useSDK()
const [messages, setMessages] = useState<ChatMessage[]>(mockHistory)
const [messages, setMessages] = useState<ChatMessage[]>([])
const [inputValue, setInputValue] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [suggestedQuestions, setSuggestedQuestions] = useState<string[]>(DEFAULT_QUESTIONS)
const [apiError, setApiError] = useState<string | null>(null)
// Load regulations for suggested questions
useEffect(() => {
fetch(`${RAG_API}/regulations`)
.then(res => res.ok ? res.json() : null)
.then(data => {
if (data && Array.isArray(data.regulations) && data.regulations.length > 0) {
const regs: Regulation[] = data.regulations
const dynamicQuestions = regs.slice(0, 5).map((r: Regulation) =>
`Was sind die wichtigsten Anforderungen aus ${r.name}?`
)
setSuggestedQuestions(dynamicQuestions.length > 0 ? dynamicQuestions : DEFAULT_QUESTIONS)
}
})
.catch(() => {
// Keep default questions if API unavailable
})
}, [])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
@@ -136,22 +133,53 @@ export default function RAGPage() {
setMessages(prev => [...prev, userMessage])
setInputValue('')
setIsLoading(true)
setApiError(null)
try {
const res = await fetch(`${RAG_API}/search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: inputValue, limit: 5 }),
})
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}
const data = await res.json()
// Build assistant message from results
const results = data.results || []
let content = ''
const sources: Source[] = []
if (results.length === 0) {
content = 'Zu dieser Frage wurden keine passenden Dokumente gefunden. Bitte formulieren Sie Ihre Frage anders oder waehlen Sie ein spezifischeres Thema.'
} else {
const snippets = results.map((r: any, i: number) => {
const title = r.metadata?.title || r.metadata?.reference || `Dokument ${i + 1}`
const ref = r.metadata?.reference || ''
sources.push({ title, reference: ref, relevance: r.score || 0 })
return `**${title}${ref ? ` (${ref})` : ''}**\n${r.content || ''}`
})
content = snippets.join('\n\n---\n\n')
}
// Simulate AI response
setTimeout(() => {
const assistantMessage: ChatMessage = {
id: `msg-${Date.now() + 1}`,
role: 'assistant',
content: 'Dies ist eine Platzhalter-Antwort. In der produktiven Version wird hier die Antwort des Legal RAG Systems angezeigt, das Ihre Frage auf Basis der integrierten Rechtsdokumente beantwortet.',
sources: [
{ title: 'DSGVO', reference: 'Art. 5', relevance: 0.9 },
{ title: 'AI Act', reference: 'Art. 6', relevance: 0.8 },
],
content,
sources,
timestamp: new Date(),
}
setMessages(prev => [...prev, assistantMessage])
} catch (err) {
setApiError('RAG-Backend nicht erreichbar. Bitte pruefen Sie die Verbindung zum AI Compliance SDK.')
// Remove the user message that didn't get a response
setMessages(prev => prev.filter(m => m.id !== userMessage.id))
} finally {
setIsLoading(false)
}, 1500)
}
}
const handleSuggestedQuestion = (question: string) => {
@@ -184,6 +212,23 @@ export default function RAGPage() {
</div>
</div>
{/* Error Box */}
{apiError && (
<div className="flex-shrink-0 bg-red-50 border border-red-200 rounded-xl p-4 mb-4">
<div className="flex items-center gap-3">
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<p className="text-sm text-red-700">{apiError}</p>
<button onClick={() => setApiError(null)} className="ml-auto text-red-400 hover:text-red-600">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
)}
{/* Chat Area */}
<div className="flex-1 overflow-y-auto bg-gray-50 rounded-xl border border-gray-200 p-6 space-y-6">
{messages.length === 0 ? (