'use client' import { useState, useEffect } from 'react' // ============================================== // Types // ============================================== interface TestResult { name: string description: string expected: string actual: string status: 'passed' | 'failed' | 'pending' | 'skipped' duration_ms: number error_message?: string } interface TestCategoryResult { category: string display_name: string description: string why_important: string tests: TestResult[] passed: number failed: number total: number duration_ms: number } interface FullTestResults { timestamp: string categories: TestCategoryResult[] total_passed: number total_failed: number total_tests: number duration_ms: number } type StepStatus = 'pending' | 'active' | 'completed' | 'failed' interface WizardStep { id: string name: string icon: string status: StepStatus category?: string } // ============================================== // Constants // ============================================== const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000' const STEPS: WizardStep[] = [ { id: 'welcome', name: 'Willkommen', icon: '👋', status: 'pending' }, { id: 'request-id', name: 'Request-ID', icon: '🔑', status: 'pending', category: 'request-id' }, { id: 'security-headers', name: 'Security Headers', icon: '🛡️', status: 'pending', category: 'security-headers' }, { id: 'rate-limiter', name: 'Rate Limiting', icon: '⏱️', status: 'pending', category: 'rate-limiter' }, { id: 'pii-redactor', name: 'PII Redaktion', icon: '🔒', status: 'pending', category: 'pii-redactor' }, { id: 'input-gate', name: 'Input Validierung', icon: '🚧', status: 'pending', category: 'input-gate' }, { id: 'cors', name: 'CORS', icon: '🌐', status: 'pending', category: 'cors' }, { id: 'summary', name: 'Zusammenfassung', icon: '📊', status: 'pending' }, ] const EDUCATION_CONTENT: Record = { 'welcome': { title: 'Willkommen zum Middleware-Test-Wizard', content: [ 'Middleware ist die unsichtbare Schutzschicht Ihrer Anwendung. Sie verarbeitet jede Anfrage bevor sie Ihren Code erreicht - und jede Antwort bevor sie den Benutzer erreicht.', 'In diesem Wizard testen wir alle Middleware-Komponenten und Sie lernen dabei:', '• Warum jede Komponente wichtig ist', '• Welche Angriffe sie verhindert', '• Wie Sie Probleme erkennen und beheben', 'Klicken Sie auf "Starten" um den Test-Wizard zu beginnen.', ], }, 'request-id': { title: 'Request-ID & Distributed Tracing', content: [ 'Stellen Sie sich vor, ein Benutzer meldet einen Fehler. Ohne Request-ID muessen Sie Tausende von Log-Eintraegen durchsuchen. Mit Request-ID finden Sie den genauen Pfad der Anfrage durch alle Microservices in Sekunden.', 'Request-IDs sind essentiell fuer:', '• Fehlersuche in verteilten Systemen', '• Performance-Analyse', '• Audit-Trails fuer Compliance', ], }, 'security-headers': { title: 'Security Headers - Erste Verteidigungslinie', content: [ 'Security Headers sind Anweisungen an den Browser, wie er Ihre Seite schuetzen soll:', '• X-Content-Type-Options: nosniff - Verhindert MIME-Sniffing Angriffe', '• X-Frame-Options: DENY - Blockiert Clickjacking-Angriffe', '• Content-Security-Policy - Stoppt XSS durch Whitelist erlaubter Quellen', '• Strict-Transport-Security - Erzwingt HTTPS', 'OWASP empfiehlt diese Headers als Mindeststandard.', ], }, 'rate-limiter': { title: 'Rate Limiting - Schutz vor Ueberflutung', content: [ 'Ohne Rate Limiting kann ein Angreifer:', '• Passwort-Brute-Force durchfuehren (Millionen Versuche/Minute)', '• Ihre Server mit Anfragen ueberfluten (DDoS)', '• Teure API-Aufrufe missbrauchen', 'BreakPilot limitiert:', '• 100 Anfragen/Minute pro IP (allgemein)', '• 20 Anfragen/Minute fuer Auth-Endpoints', '• 500 Anfragen/Minute pro authentifiziertem Benutzer', ], }, 'pii-redactor': { title: 'PII Redaktion - DSGVO Pflicht', content: [ 'Personenbezogene Daten in Logs sind ein DSGVO-Verstoss:', '• Email-Adressen: Bussgelder bis 20 Mio. EUR', '• IP-Adressen: Gelten als personenbezogen (EuGH-Urteil)', '• Telefonnummern: Direkter Personenbezug', 'Der PII Redactor erkennt automatisch:', '• Email-Adressen → [EMAIL_REDACTED]', '• IP-Adressen → [IP_REDACTED]', '• Deutsche Telefonnummern → [PHONE_REDACTED]', ], }, 'input-gate': { title: 'Input Gate - Der Tuersteher', content: [ 'Das Input Gate prueft jede Anfrage bevor sie Ihren Code erreicht:', '• Groessenlimit: Blockiert ueberdimensionierte Payloads (DoS-Schutz)', '• Content-Type: Erlaubt nur erwartete Formate', '• Dateiendungen: Blockiert .exe, .bat, .sh Uploads', 'Ein Angreifer, der an Ihrem Code vorbeikommt, wird hier gestoppt.', ], }, 'cors': { title: 'CORS - Kontrollierte Zugriffe', content: [ 'CORS bestimmt, welche Websites Ihre API aufrufen duerfen:', '• Zu offen (*): Jede Website kann Ihre API missbrauchen', '• Zu streng: Ihre eigene Frontend-App wird blockiert', 'BreakPilot erlaubt nur:', '• https://breakpilot.app (Produktion)', '• http://localhost:3000 (Development)', ], }, 'summary': { title: 'Test-Zusammenfassung', content: [ 'Hier sehen Sie eine Uebersicht aller durchgefuehrten Tests:', '• Anzahl bestandener Tests', '• Fehlgeschlagene Tests mit Details', '• Empfehlungen zur Behebung', ], }, } // ============================================== // Components // ============================================== function WizardStepper({ steps, currentStep, onStepClick }: { steps: WizardStep[] currentStep: number onStepClick: (index: number) => void }) { return (
{steps.map((step, index) => (
{index < steps.length - 1 && (
)}
))}
) } function EducationCard({ stepId }: { stepId: string }) { const content = EDUCATION_CONTENT[stepId] if (!content) return null return (

💡 Warum ist das wichtig?

{content.title}

{content.content.map((line, index) => (

{line}

))}
) } function TestResultCard({ result }: { result: TestResult }) { const statusColors = { passed: 'bg-green-100 border-green-300 text-green-800', failed: 'bg-red-100 border-red-300 text-red-800', pending: 'bg-yellow-100 border-yellow-300 text-yellow-800', skipped: 'bg-gray-100 border-gray-300 text-gray-600', } const statusIcons = { passed: '✓', failed: '✗', pending: '○', skipped: '−', } return (

{statusIcons[result.status]} {result.name}

{result.description}

{result.duration_ms.toFixed(0)}ms
Erwartet: {result.expected}
Erhalten: {result.actual}
{result.error_message && (
Fehler: {result.error_message}
)}
) } function TestSummaryCard({ results }: { results: FullTestResults }) { const passRate = results.total_tests > 0 ? ((results.total_passed / results.total_tests) * 100).toFixed(1) : '0' return (

Test-Ergebnisse

{/* Summary Stats */}
{results.total_tests}
Gesamt
{results.total_passed}
Bestanden
{results.total_failed}
Fehlgeschlagen
= 80 ? 'bg-green-50' : parseFloat(passRate) >= 50 ? 'bg-yellow-50' : 'bg-red-50' }`}>
= 80 ? 'text-green-600' : parseFloat(passRate) >= 50 ? 'text-yellow-600' : 'text-red-600' }`}>{passRate}%
Erfolgsrate
{/* Category Results */}
{results.categories.map((category) => (

{category.display_name}

{category.passed}/{category.total} bestanden

{category.description}

))}
{/* Duration */}
Gesamtdauer: {(results.duration_ms / 1000).toFixed(2)}s
) } // ============================================== // Main Component // ============================================== export default function TestWizardPage() { const [currentStep, setCurrentStep] = useState(0) const [steps, setSteps] = useState(STEPS) const [categoryResults, setCategoryResults] = useState>({}) const [fullResults, setFullResults] = useState(null) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const currentStepData = steps[currentStep] const isTestStep = currentStepData?.category !== undefined const isWelcome = currentStepData?.id === 'welcome' const isSummary = currentStepData?.id === 'summary' const runCategoryTest = async (category: string) => { setIsLoading(true) setError(null) try { const response = await fetch(`${BACKEND_URL}/api/admin/ui-tests/${category}`, { method: 'POST', }) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } const result: TestCategoryResult = await response.json() setCategoryResults((prev) => ({ ...prev, [category]: result })) // Update step status setSteps((prev) => prev.map((step) => step.category === category ? { ...step, status: result.failed === 0 ? 'completed' : 'failed' } : step ) ) } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setIsLoading(false) } } const runAllTests = async () => { setIsLoading(true) setError(null) try { const response = await fetch(`${BACKEND_URL}/api/admin/ui-tests/run-all`, { method: 'POST', }) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`) } const results: FullTestResults = await response.json() setFullResults(results) // Update all step statuses setSteps((prev) => prev.map((step) => { if (step.category) { const catResult = results.categories.find((c) => c.category === step.category) if (catResult) { return { ...step, status: catResult.failed === 0 ? 'completed' : 'failed' } } } return step }) ) // Store category results const newCategoryResults: Record = {} results.categories.forEach((cat) => { newCategoryResults[cat.category] = cat }) setCategoryResults(newCategoryResults) } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setIsLoading(false) } } const goToNext = () => { if (currentStep < steps.length - 1) { setSteps((prev) => prev.map((step, idx) => idx === currentStep && step.status === 'pending' ? { ...step, status: 'completed' } : step ) ) setCurrentStep((prev) => prev + 1) } } const goToPrev = () => { if (currentStep > 0) { setCurrentStep((prev) => prev - 1) } } const handleStepClick = (index: number) => { // Allow clicking on completed steps or the next available step if (index <= currentStep || steps[index - 1]?.status !== 'pending') { setCurrentStep(index) } } return (
{/* Header */}

🧪 UI Test Wizard

Interaktives Middleware-Testing mit Lernmaterial

← Zurueck zur Middleware-Konfiguration
{/* Stepper */}
{/* Content */}
{/* Step Header */}
{currentStepData?.icon}

Schritt {currentStep + 1}: {currentStepData?.name}

{currentStep + 1} von {steps.length}

{/* Education Card */} {/* Error Display */} {error && (
Fehler: {error}
)} {/* Welcome Step */} {isWelcome && (
)} {/* Test Steps */} {isTestStep && currentStepData?.category && (
{/* Run Test Button */} {!categoryResults[currentStepData.category] && (
)} {/* Test Results */} {categoryResults[currentStepData.category] && (

Testergebnisse

{categoryResults[currentStepData.category].tests.map((test, index) => ( ))}
)}
)} {/* Summary Step */} {isSummary && (
{!fullResults ? (

Fuehren Sie alle Tests aus um eine Zusammenfassung zu sehen.

) : ( )}
)} {/* Navigation */}
{!isSummary && ( )}
{/* Footer Info */}
Diese Tests pruefen die Middleware-Konfiguration Ihrer Anwendung. Bei Fragen wenden Sie sich an den Administrator.
) }