fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
414
admin-v2/lib/sdk/tom-generator/ai/document-analyzer.ts
Normal file
414
admin-v2/lib/sdk/tom-generator/ai/document-analyzer.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
// =============================================================================
|
||||
// TOM Generator Document Analyzer
|
||||
// AI-powered analysis of uploaded evidence documents
|
||||
// =============================================================================
|
||||
|
||||
import {
|
||||
EvidenceDocument,
|
||||
AIDocumentAnalysis,
|
||||
ExtractedClause,
|
||||
DocumentType,
|
||||
} from '../types'
|
||||
import {
|
||||
getDocumentAnalysisPrompt,
|
||||
getDocumentTypeDetectionPrompt,
|
||||
DocumentAnalysisPromptContext,
|
||||
} from './prompts'
|
||||
import { getAllControls } from '../controls/loader'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface AnalysisResult {
|
||||
success: boolean
|
||||
analysis: AIDocumentAnalysis | null
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface DocumentTypeDetectionResult {
|
||||
documentType: DocumentType
|
||||
confidence: number
|
||||
reasoning: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DOCUMENT ANALYZER CLASS
|
||||
// =============================================================================
|
||||
|
||||
export class TOMDocumentAnalyzer {
|
||||
private apiEndpoint: string
|
||||
private apiKey: string | null
|
||||
|
||||
constructor(options?: { apiEndpoint?: string; apiKey?: string }) {
|
||||
this.apiEndpoint = options?.apiEndpoint || '/api/sdk/v1/tom-generator/evidence/analyze'
|
||||
this.apiKey = options?.apiKey || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a document and extract relevant TOM information
|
||||
*/
|
||||
async analyzeDocument(
|
||||
document: EvidenceDocument,
|
||||
documentText: string,
|
||||
language: 'de' | 'en' = 'de'
|
||||
): Promise<AnalysisResult> {
|
||||
try {
|
||||
// Get all control IDs for context
|
||||
const allControls = getAllControls()
|
||||
const controlIds = allControls.map((c) => c.id)
|
||||
|
||||
// Build the prompt context
|
||||
const promptContext: DocumentAnalysisPromptContext = {
|
||||
documentType: document.documentType,
|
||||
documentText,
|
||||
controlIds,
|
||||
language,
|
||||
}
|
||||
|
||||
const prompt = getDocumentAnalysisPrompt(promptContext)
|
||||
|
||||
// Call the AI API
|
||||
const response = await this.callAI(prompt)
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
return {
|
||||
success: false,
|
||||
analysis: null,
|
||||
error: response.error || 'Failed to analyze document',
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the AI response
|
||||
const parsedResponse = this.parseAnalysisResponse(response.data)
|
||||
|
||||
const analysis: AIDocumentAnalysis = {
|
||||
summary: parsedResponse.summary,
|
||||
extractedClauses: parsedResponse.extractedClauses,
|
||||
applicableControls: parsedResponse.applicableControls,
|
||||
gaps: parsedResponse.gaps,
|
||||
confidence: parsedResponse.confidence,
|
||||
analyzedAt: new Date(),
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
analysis,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
analysis: null,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the document type from content
|
||||
*/
|
||||
async detectDocumentType(
|
||||
documentText: string,
|
||||
filename: string
|
||||
): Promise<DocumentTypeDetectionResult> {
|
||||
try {
|
||||
const prompt = getDocumentTypeDetectionPrompt(documentText, filename)
|
||||
const response = await this.callAI(prompt)
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
return {
|
||||
documentType: 'OTHER',
|
||||
confidence: 0,
|
||||
reasoning: 'Could not detect document type',
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = this.parseJSONResponse(response.data)
|
||||
|
||||
return {
|
||||
documentType: this.mapDocumentType(String(parsed.documentType || 'OTHER')),
|
||||
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0,
|
||||
reasoning: typeof parsed.reasoning === 'string' ? parsed.reasoning : '',
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
documentType: 'OTHER',
|
||||
confidence: 0,
|
||||
reasoning: error instanceof Error ? error.message : 'Detection failed',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link document to applicable controls based on analysis
|
||||
*/
|
||||
async suggestControlLinks(
|
||||
analysis: AIDocumentAnalysis
|
||||
): Promise<string[]> {
|
||||
// Use the applicable controls from the analysis
|
||||
const suggestedControls = [...analysis.applicableControls]
|
||||
|
||||
// Also check extracted clauses for related controls
|
||||
for (const clause of analysis.extractedClauses) {
|
||||
if (clause.relatedControlId && !suggestedControls.includes(clause.relatedControlId)) {
|
||||
suggestedControls.push(clause.relatedControlId)
|
||||
}
|
||||
}
|
||||
|
||||
return suggestedControls
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate evidence coverage for a control
|
||||
*/
|
||||
calculateEvidenceCoverage(
|
||||
controlId: string,
|
||||
documents: EvidenceDocument[]
|
||||
): {
|
||||
coverage: number
|
||||
linkedDocuments: string[]
|
||||
missingEvidence: string[]
|
||||
} {
|
||||
const control = getAllControls().find((c) => c.id === controlId)
|
||||
if (!control) {
|
||||
return { coverage: 0, linkedDocuments: [], missingEvidence: [] }
|
||||
}
|
||||
|
||||
const linkedDocuments: string[] = []
|
||||
const coveredRequirements = new Set<string>()
|
||||
|
||||
for (const doc of documents) {
|
||||
// Check if document is explicitly linked
|
||||
if (doc.linkedControlIds.includes(controlId)) {
|
||||
linkedDocuments.push(doc.id)
|
||||
}
|
||||
|
||||
// Check if AI analysis suggests this document covers the control
|
||||
if (doc.aiAnalysis?.applicableControls.includes(controlId)) {
|
||||
if (!linkedDocuments.includes(doc.id)) {
|
||||
linkedDocuments.push(doc.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Check which evidence requirements are covered
|
||||
if (doc.aiAnalysis) {
|
||||
for (const requirement of control.evidenceRequirements) {
|
||||
const reqLower = requirement.toLowerCase()
|
||||
if (
|
||||
doc.aiAnalysis.summary.toLowerCase().includes(reqLower) ||
|
||||
doc.aiAnalysis.extractedClauses.some((c) =>
|
||||
c.text.toLowerCase().includes(reqLower)
|
||||
)
|
||||
) {
|
||||
coveredRequirements.add(requirement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const missingEvidence = control.evidenceRequirements.filter(
|
||||
(req) => !coveredRequirements.has(req)
|
||||
)
|
||||
|
||||
const coverage =
|
||||
control.evidenceRequirements.length > 0
|
||||
? Math.round(
|
||||
(coveredRequirements.size / control.evidenceRequirements.length) * 100
|
||||
)
|
||||
: 100
|
||||
|
||||
return {
|
||||
coverage,
|
||||
linkedDocuments,
|
||||
missingEvidence,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the AI API
|
||||
*/
|
||||
private async callAI(
|
||||
prompt: string
|
||||
): Promise<{ success: boolean; data?: string; error?: string }> {
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (this.apiKey) {
|
||||
headers['Authorization'] = `Bearer ${this.apiKey}`
|
||||
}
|
||||
|
||||
const response = await fetch(this.apiEndpoint, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ prompt }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: `API error: ${response.status} ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: data.response || data.content || JSON.stringify(data),
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'API call failed',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the AI analysis response
|
||||
*/
|
||||
private parseAnalysisResponse(response: string): {
|
||||
summary: string
|
||||
extractedClauses: ExtractedClause[]
|
||||
applicableControls: string[]
|
||||
gaps: string[]
|
||||
confidence: number
|
||||
} {
|
||||
const parsed = this.parseJSONResponse(response)
|
||||
|
||||
return {
|
||||
summary: typeof parsed.summary === 'string' ? parsed.summary : '',
|
||||
extractedClauses: (Array.isArray(parsed.extractedClauses) ? parsed.extractedClauses : []).map(
|
||||
(clause: Record<string, unknown>) => ({
|
||||
id: String(clause.id || ''),
|
||||
text: String(clause.text || ''),
|
||||
type: String(clause.type || ''),
|
||||
relatedControlId: clause.relatedControlId
|
||||
? String(clause.relatedControlId)
|
||||
: null,
|
||||
})
|
||||
),
|
||||
applicableControls: Array.isArray(parsed.applicableControls)
|
||||
? parsed.applicableControls.map(String)
|
||||
: [],
|
||||
gaps: Array.isArray(parsed.gaps) ? parsed.gaps.map(String) : [],
|
||||
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON from AI response (handles markdown code blocks)
|
||||
*/
|
||||
private parseJSONResponse(response: string): Record<string, unknown> {
|
||||
let jsonStr = response.trim()
|
||||
|
||||
// Remove markdown code blocks if present
|
||||
if (jsonStr.startsWith('```json')) {
|
||||
jsonStr = jsonStr.slice(7)
|
||||
} else if (jsonStr.startsWith('```')) {
|
||||
jsonStr = jsonStr.slice(3)
|
||||
}
|
||||
|
||||
if (jsonStr.endsWith('```')) {
|
||||
jsonStr = jsonStr.slice(0, -3)
|
||||
}
|
||||
|
||||
jsonStr = jsonStr.trim()
|
||||
|
||||
try {
|
||||
return JSON.parse(jsonStr)
|
||||
} catch {
|
||||
// Try to extract JSON from the response
|
||||
const jsonMatch = jsonStr.match(/\{[\s\S]*\}/)
|
||||
if (jsonMatch) {
|
||||
try {
|
||||
return JSON.parse(jsonMatch[0])
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map string to DocumentType
|
||||
*/
|
||||
private mapDocumentType(type: string): DocumentType {
|
||||
const typeMap: Record<string, DocumentType> = {
|
||||
AVV: 'AVV',
|
||||
DPA: 'DPA',
|
||||
SLA: 'SLA',
|
||||
NDA: 'NDA',
|
||||
POLICY: 'POLICY',
|
||||
CERTIFICATE: 'CERTIFICATE',
|
||||
AUDIT_REPORT: 'AUDIT_REPORT',
|
||||
OTHER: 'OTHER',
|
||||
}
|
||||
|
||||
return typeMap[type.toUpperCase()] || 'OTHER'
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SINGLETON INSTANCE
|
||||
// =============================================================================
|
||||
|
||||
let analyzerInstance: TOMDocumentAnalyzer | null = null
|
||||
|
||||
export function getDocumentAnalyzer(
|
||||
options?: { apiEndpoint?: string; apiKey?: string }
|
||||
): TOMDocumentAnalyzer {
|
||||
if (!analyzerInstance) {
|
||||
analyzerInstance = new TOMDocumentAnalyzer(options)
|
||||
}
|
||||
return analyzerInstance
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Quick document analysis
|
||||
*/
|
||||
export async function analyzeEvidenceDocument(
|
||||
document: EvidenceDocument,
|
||||
documentText: string,
|
||||
language: 'de' | 'en' = 'de'
|
||||
): Promise<AnalysisResult> {
|
||||
return getDocumentAnalyzer().analyzeDocument(document, documentText, language)
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick document type detection
|
||||
*/
|
||||
export async function detectEvidenceDocumentType(
|
||||
documentText: string,
|
||||
filename: string
|
||||
): Promise<DocumentTypeDetectionResult> {
|
||||
return getDocumentAnalyzer().detectDocumentType(documentText, filename)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get evidence gaps for all controls
|
||||
*/
|
||||
export function getEvidenceGapsForAllControls(
|
||||
documents: EvidenceDocument[]
|
||||
): Map<string, { coverage: number; missing: string[] }> {
|
||||
const analyzer = getDocumentAnalyzer()
|
||||
const allControls = getAllControls()
|
||||
const gaps = new Map<string, { coverage: number; missing: string[] }>()
|
||||
|
||||
for (const control of allControls) {
|
||||
const result = analyzer.calculateEvidenceCoverage(control.id, documents)
|
||||
gaps.set(control.id, {
|
||||
coverage: result.coverage,
|
||||
missing: result.missingEvidence,
|
||||
})
|
||||
}
|
||||
|
||||
return gaps
|
||||
}
|
||||
427
admin-v2/lib/sdk/tom-generator/ai/prompts.ts
Normal file
427
admin-v2/lib/sdk/tom-generator/ai/prompts.ts
Normal file
@@ -0,0 +1,427 @@
|
||||
// =============================================================================
|
||||
// TOM Generator AI Prompts
|
||||
// Prompts for document analysis and TOM description generation
|
||||
// =============================================================================
|
||||
|
||||
import {
|
||||
CompanyProfile,
|
||||
DataProfile,
|
||||
ArchitectureProfile,
|
||||
RiskProfile,
|
||||
DocumentType,
|
||||
ControlLibraryEntry,
|
||||
} from '../types'
|
||||
|
||||
// =============================================================================
|
||||
// DOCUMENT ANALYSIS PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export interface DocumentAnalysisPromptContext {
|
||||
documentType: DocumentType
|
||||
documentText: string
|
||||
controlIds?: string[]
|
||||
language?: 'de' | 'en'
|
||||
}
|
||||
|
||||
export function getDocumentAnalysisPrompt(
|
||||
context: DocumentAnalysisPromptContext
|
||||
): string {
|
||||
const { documentType, documentText, controlIds, language = 'de' } = context
|
||||
|
||||
const controlContext = controlIds?.length
|
||||
? `\nRELEVANT CONTROL IDS: ${controlIds.join(', ')}`
|
||||
: ''
|
||||
|
||||
if (language === 'de') {
|
||||
return `Du bist ein Experte für Datenschutz-Compliance und analysierst ein Dokument für die TOM-Dokumentation nach DSGVO Art. 32.
|
||||
|
||||
DOKUMENTTYP: ${documentType}
|
||||
${controlContext}
|
||||
|
||||
DOKUMENTTEXT:
|
||||
${documentText}
|
||||
|
||||
AUFGABE: Analysiere das Dokument und extrahiere die folgenden Informationen:
|
||||
|
||||
1. SUMMARY: Eine Zusammenfassung in 2-3 Sätzen, die die Relevanz für den Datenschutz beschreibt.
|
||||
|
||||
2. EXTRACTED_CLAUSES: Alle Klauseln, die sich auf technische und organisatorische Sicherheitsmaßnahmen beziehen. Für jede Klausel:
|
||||
- id: Eindeutige ID (z.B. "clause-1")
|
||||
- text: Der extrahierte Text
|
||||
- type: Art der Maßnahme (z.B. "encryption", "access-control", "backup", "training")
|
||||
- relatedControlId: Falls zutreffend, die TOM-Control-ID (z.B. "TOM-ENC-01")
|
||||
|
||||
3. APPLICABLE_CONTROLS: Liste der TOM-Control-IDs, die durch dieses Dokument belegt werden könnten.
|
||||
|
||||
4. GAPS: Identifizierte Lücken oder fehlende Maßnahmen, die im Dokument nicht adressiert werden.
|
||||
|
||||
5. CONFIDENCE: Dein Vertrauenswert für die Analyse (0.0 bis 1.0).
|
||||
|
||||
Antworte im JSON-Format:
|
||||
{
|
||||
"summary": "...",
|
||||
"extractedClauses": [
|
||||
{ "id": "...", "text": "...", "type": "...", "relatedControlId": "..." }
|
||||
],
|
||||
"applicableControls": ["TOM-..."],
|
||||
"gaps": ["..."],
|
||||
"confidence": 0.85
|
||||
}`
|
||||
}
|
||||
|
||||
return `You are a data protection compliance expert analyzing a document for TOM documentation according to GDPR Art. 32.
|
||||
|
||||
DOCUMENT TYPE: ${documentType}
|
||||
${controlContext}
|
||||
|
||||
DOCUMENT TEXT:
|
||||
${documentText}
|
||||
|
||||
TASK: Analyze the document and extract the following information:
|
||||
|
||||
1. SUMMARY: A 2-3 sentence summary describing the relevance for data protection.
|
||||
|
||||
2. EXTRACTED_CLAUSES: All clauses related to technical and organizational security measures. For each clause:
|
||||
- id: Unique ID (e.g., "clause-1")
|
||||
- text: The extracted text
|
||||
- type: Type of measure (e.g., "encryption", "access-control", "backup", "training")
|
||||
- relatedControlId: If applicable, the TOM control ID (e.g., "TOM-ENC-01")
|
||||
|
||||
3. APPLICABLE_CONTROLS: List of TOM control IDs that could be evidenced by this document.
|
||||
|
||||
4. GAPS: Identified gaps or missing measures not addressed in the document.
|
||||
|
||||
5. CONFIDENCE: Your confidence score for the analysis (0.0 to 1.0).
|
||||
|
||||
Respond in JSON format:
|
||||
{
|
||||
"summary": "...",
|
||||
"extractedClauses": [
|
||||
{ "id": "...", "text": "...", "type": "...", "relatedControlId": "..." }
|
||||
],
|
||||
"applicableControls": ["TOM-..."],
|
||||
"gaps": ["..."],
|
||||
"confidence": 0.85
|
||||
}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// TOM DESCRIPTION GENERATION PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export interface TOMDescriptionPromptContext {
|
||||
control: ControlLibraryEntry
|
||||
companyProfile: CompanyProfile
|
||||
dataProfile: DataProfile
|
||||
architectureProfile: ArchitectureProfile
|
||||
riskProfile: RiskProfile
|
||||
language?: 'de' | 'en'
|
||||
}
|
||||
|
||||
export function getTOMDescriptionPrompt(
|
||||
context: TOMDescriptionPromptContext
|
||||
): string {
|
||||
const {
|
||||
control,
|
||||
companyProfile,
|
||||
dataProfile,
|
||||
architectureProfile,
|
||||
riskProfile,
|
||||
language = 'de',
|
||||
} = context
|
||||
|
||||
if (language === 'de') {
|
||||
return `Du bist ein Experte für Datenschutz-Compliance und erstellst eine unternehmensspezifische TOM-Beschreibung.
|
||||
|
||||
KONTROLLE:
|
||||
- Name: ${control.name.de}
|
||||
- Beschreibung: ${control.description.de}
|
||||
- Kategorie: ${control.category}
|
||||
- Typ: ${control.type}
|
||||
|
||||
UNTERNEHMENSPROFIL:
|
||||
- Branche: ${companyProfile.industry}
|
||||
- Größe: ${companyProfile.size}
|
||||
- Rolle: ${companyProfile.role}
|
||||
- Produkte/Services: ${companyProfile.products.join(', ')}
|
||||
|
||||
DATENPROFIL:
|
||||
- Datenkategorien: ${dataProfile.categories.join(', ')}
|
||||
- Besondere Kategorien: ${dataProfile.hasSpecialCategories ? 'Ja' : 'Nein'}
|
||||
- Betroffene: ${dataProfile.subjects.join(', ')}
|
||||
- Datenvolumen: ${dataProfile.dataVolume}
|
||||
|
||||
ARCHITEKTUR:
|
||||
- Hosting-Modell: ${architectureProfile.hostingModel}
|
||||
- Standort: ${architectureProfile.hostingLocation}
|
||||
- Mandantentrennung: ${architectureProfile.multiTenancy}
|
||||
|
||||
SCHUTZBEDARF: ${riskProfile.protectionLevel}
|
||||
|
||||
AUFGABE: Erstelle eine unternehmensspezifische Beschreibung dieser TOM in 3-5 Sätzen.
|
||||
Die Beschreibung soll:
|
||||
- Auf das spezifische Unternehmensprofil zugeschnitten sein
|
||||
- Konkrete Maßnahmen beschreiben, die für dieses Unternehmen relevant sind
|
||||
- In formeller Geschäftssprache verfasst sein
|
||||
- Keine Platzhalter oder generischen Formulierungen enthalten
|
||||
|
||||
Antworte nur mit der Beschreibung, ohne zusätzliche Erklärungen.`
|
||||
}
|
||||
|
||||
return `You are a data protection compliance expert creating a company-specific TOM description.
|
||||
|
||||
CONTROL:
|
||||
- Name: ${control.name.en}
|
||||
- Description: ${control.description.en}
|
||||
- Category: ${control.category}
|
||||
- Type: ${control.type}
|
||||
|
||||
COMPANY PROFILE:
|
||||
- Industry: ${companyProfile.industry}
|
||||
- Size: ${companyProfile.size}
|
||||
- Role: ${companyProfile.role}
|
||||
- Products/Services: ${companyProfile.products.join(', ')}
|
||||
|
||||
DATA PROFILE:
|
||||
- Data Categories: ${dataProfile.categories.join(', ')}
|
||||
- Special Categories: ${dataProfile.hasSpecialCategories ? 'Yes' : 'No'}
|
||||
- Data Subjects: ${dataProfile.subjects.join(', ')}
|
||||
- Data Volume: ${dataProfile.dataVolume}
|
||||
|
||||
ARCHITECTURE:
|
||||
- Hosting Model: ${architectureProfile.hostingModel}
|
||||
- Location: ${architectureProfile.hostingLocation}
|
||||
- Multi-tenancy: ${architectureProfile.multiTenancy}
|
||||
|
||||
PROTECTION LEVEL: ${riskProfile.protectionLevel}
|
||||
|
||||
TASK: Create a company-specific description of this TOM in 3-5 sentences.
|
||||
The description should:
|
||||
- Be tailored to the specific company profile
|
||||
- Describe concrete measures relevant to this company
|
||||
- Be written in formal business language
|
||||
- Contain no placeholders or generic formulations
|
||||
|
||||
Respond only with the description, without additional explanations.`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GAP RECOMMENDATIONS PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export interface GapRecommendationsPromptContext {
|
||||
missingControls: Array<{ controlId: string; name: string; priority: string }>
|
||||
partialControls: Array<{ controlId: string; name: string; missingAspects: string[] }>
|
||||
companyProfile: CompanyProfile
|
||||
riskProfile: RiskProfile
|
||||
language?: 'de' | 'en'
|
||||
}
|
||||
|
||||
export function getGapRecommendationsPrompt(
|
||||
context: GapRecommendationsPromptContext
|
||||
): string {
|
||||
const {
|
||||
missingControls,
|
||||
partialControls,
|
||||
companyProfile,
|
||||
riskProfile,
|
||||
language = 'de',
|
||||
} = context
|
||||
|
||||
const missingList = missingControls
|
||||
.map((c) => `- ${c.name} (${c.controlId}, Priorität: ${c.priority})`)
|
||||
.join('\n')
|
||||
|
||||
const partialList = partialControls
|
||||
.map((c) => `- ${c.name} (${c.controlId}): Fehlend: ${c.missingAspects.join(', ')}`)
|
||||
.join('\n')
|
||||
|
||||
if (language === 'de') {
|
||||
return `Du bist ein Experte für Datenschutz-Compliance und erstellst Handlungsempfehlungen für TOM-Lücken.
|
||||
|
||||
UNTERNEHMEN:
|
||||
- Branche: ${companyProfile.industry}
|
||||
- Größe: ${companyProfile.size}
|
||||
- Rolle: ${companyProfile.role}
|
||||
|
||||
SCHUTZBEDARF: ${riskProfile.protectionLevel}
|
||||
|
||||
FEHLENDE KONTROLLEN:
|
||||
${missingList || 'Keine'}
|
||||
|
||||
TEILWEISE IMPLEMENTIERTE KONTROLLEN:
|
||||
${partialList || 'Keine'}
|
||||
|
||||
AUFGABE: Erstelle konkrete Handlungsempfehlungen, um die Lücken zu schließen.
|
||||
|
||||
Für jede Empfehlung:
|
||||
1. Priorisiere nach Schutzbedarf und DSGVO-Relevanz
|
||||
2. Berücksichtige die Unternehmensgröße und Branche
|
||||
3. Gib konkrete, umsetzbare Schritte an
|
||||
4. Schätze den Aufwand ein (niedrig/mittel/hoch)
|
||||
|
||||
Antworte im JSON-Format:
|
||||
{
|
||||
"recommendations": [
|
||||
{
|
||||
"priority": "HIGH",
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"steps": ["..."],
|
||||
"effort": "MEDIUM",
|
||||
"relatedControls": ["TOM-..."]
|
||||
}
|
||||
],
|
||||
"summary": "Kurze Zusammenfassung der wichtigsten Maßnahmen"
|
||||
}`
|
||||
}
|
||||
|
||||
return `You are a data protection compliance expert creating recommendations for TOM gaps.
|
||||
|
||||
COMPANY:
|
||||
- Industry: ${companyProfile.industry}
|
||||
- Size: ${companyProfile.size}
|
||||
- Role: ${companyProfile.role}
|
||||
|
||||
PROTECTION LEVEL: ${riskProfile.protectionLevel}
|
||||
|
||||
MISSING CONTROLS:
|
||||
${missingList || 'None'}
|
||||
|
||||
PARTIALLY IMPLEMENTED CONTROLS:
|
||||
${partialList || 'None'}
|
||||
|
||||
TASK: Create concrete recommendations to close the gaps.
|
||||
|
||||
For each recommendation:
|
||||
1. Prioritize by protection level and GDPR relevance
|
||||
2. Consider company size and industry
|
||||
3. Provide concrete, actionable steps
|
||||
4. Estimate effort (low/medium/high)
|
||||
|
||||
Respond in JSON format:
|
||||
{
|
||||
"recommendations": [
|
||||
{
|
||||
"priority": "HIGH",
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"steps": ["..."],
|
||||
"effort": "MEDIUM",
|
||||
"relatedControls": ["TOM-..."]
|
||||
}
|
||||
],
|
||||
"summary": "Brief summary of the most important measures"
|
||||
}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DOCUMENT TYPE DETECTION PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export function getDocumentTypeDetectionPrompt(
|
||||
documentText: string,
|
||||
filename: string
|
||||
): string {
|
||||
return `Du bist ein Experte für Datenschutz-Dokumente und sollst den Dokumenttyp erkennen.
|
||||
|
||||
DATEINAME: ${filename}
|
||||
|
||||
DOKUMENTTEXT (Auszug):
|
||||
${documentText.substring(0, 2000)}
|
||||
|
||||
MÖGLICHE DOKUMENTTYPEN:
|
||||
- AVV: Auftragsverarbeitungsvertrag
|
||||
- DPA: Data Processing Agreement (englisch)
|
||||
- SLA: Service Level Agreement
|
||||
- NDA: Geheimhaltungsvereinbarung
|
||||
- POLICY: Interne Richtlinie (z.B. Passwortrichtlinie, IT-Sicherheitsrichtlinie)
|
||||
- CERTIFICATE: Zertifikat (z.B. ISO 27001, SOC 2)
|
||||
- AUDIT_REPORT: Audit-Bericht oder Prüfbericht
|
||||
- OTHER: Sonstiges Dokument
|
||||
|
||||
Antworte im JSON-Format:
|
||||
{
|
||||
"documentType": "...",
|
||||
"confidence": 0.85,
|
||||
"reasoning": "Kurze Begründung"
|
||||
}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CLAUSE EXTRACTION PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export function getClauseExtractionPrompt(
|
||||
documentText: string,
|
||||
controlCategory: string
|
||||
): string {
|
||||
return `Du bist ein Experte für Datenschutz-Compliance und extrahierst Klauseln aus einem Dokument.
|
||||
|
||||
GESUCHTE KATEGORIE: ${controlCategory}
|
||||
|
||||
DOKUMENTTEXT:
|
||||
${documentText}
|
||||
|
||||
AUFGABE: Extrahiere alle Klauseln, die sich auf die Kategorie "${controlCategory}" beziehen.
|
||||
|
||||
Antworte im JSON-Format:
|
||||
{
|
||||
"clauses": [
|
||||
{
|
||||
"id": "clause-1",
|
||||
"text": "Der extrahierte Text der Klausel",
|
||||
"section": "Abschnittsnummer oder -name falls vorhanden",
|
||||
"relevance": "Kurze Erklärung der Relevanz",
|
||||
"matchScore": 0.9
|
||||
}
|
||||
],
|
||||
"totalFound": 3
|
||||
}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE ASSESSMENT PROMPT
|
||||
// =============================================================================
|
||||
|
||||
export function getComplianceAssessmentPrompt(
|
||||
tomDescription: string,
|
||||
evidenceDescriptions: string[],
|
||||
controlRequirements: string[]
|
||||
): string {
|
||||
return `Du bist ein Experte für Datenschutz-Compliance und bewertest die Umsetzung einer TOM.
|
||||
|
||||
TOM-BESCHREIBUNG:
|
||||
${tomDescription}
|
||||
|
||||
ANFORDERUNGEN AN NACHWEISE:
|
||||
${controlRequirements.map((r, i) => `${i + 1}. ${r}`).join('\n')}
|
||||
|
||||
VORHANDENE NACHWEISE:
|
||||
${evidenceDescriptions.map((e, i) => `${i + 1}. ${e}`).join('\n') || 'Keine Nachweise vorhanden'}
|
||||
|
||||
AUFGABE: Bewerte den Umsetzungsgrad dieser TOM.
|
||||
|
||||
Antworte im JSON-Format:
|
||||
{
|
||||
"implementationStatus": "NOT_IMPLEMENTED" | "PARTIAL" | "IMPLEMENTED",
|
||||
"score": 0-100,
|
||||
"coveredRequirements": ["..."],
|
||||
"missingRequirements": ["..."],
|
||||
"recommendations": ["..."],
|
||||
"reasoning": "Begründung der Bewertung"
|
||||
}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EXPORT FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export const AI_PROMPTS = {
|
||||
documentAnalysis: getDocumentAnalysisPrompt,
|
||||
tomDescription: getTOMDescriptionPrompt,
|
||||
gapRecommendations: getGapRecommendationsPrompt,
|
||||
documentTypeDetection: getDocumentTypeDetectionPrompt,
|
||||
clauseExtraction: getClauseExtractionPrompt,
|
||||
complianceAssessment: getComplianceAssessmentPrompt,
|
||||
}
|
||||
Reference in New Issue
Block a user