[split-required] Split 58 monoliths across Python, Go, TypeScript (Phases 1-3)

Phase 1 — Python (klausur-service): 5 monoliths → 36 files
- dsfa_corpus_ingestion.py (1,828 LOC → 5 files)
- cv_ocr_engines.py (2,102 LOC → 7 files)
- cv_layout.py (3,653 LOC → 10 files)
- vocab_worksheet_api.py (2,783 LOC → 8 files)
- grid_build_core.py (1,958 LOC → 6 files)

Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files
- staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3)
- policy_handlers.go (700 → 2), repository.go (684 → 2)
- search.go (592 → 2), ai_extraction_handlers.go (554 → 2)
- seed_data.go (591 → 2), grade_service.go (646 → 2)

Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files
- sdk/types.ts (2,108 → 16 domain files)
- ai/rag/page.tsx (2,686 → 14 files)
- 22 page.tsx files split into _components/ + _hooks/
- 11 component files split into sub-components
- 10 SDK data catalogs added to loc-exceptions
- Deleted dead backup index_original.ts (4,899 LOC)

All original public APIs preserved via re-export facades.
Zero new errors: Python imports verified, Go builds clean,
TypeScript tsc --noEmit shows only pre-existing errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-24 17:28:57 +02:00
parent 9ba420fa91
commit b681ddb131
251 changed files with 30016 additions and 25037 deletions

View File

@@ -0,0 +1,200 @@
'use client'
import { useEffect, useState, useCallback } from 'react'
import type {
ToolStatus,
Finding,
SeveritySummary,
MonitoringMetric,
ActiveAlert,
ScanType,
TabId,
} from './types'
import { SCAN_TYPE_LABELS } from './types'
const DEFAULT_METRICS: MonitoringMetric[] = [
{ name: 'API Latency', value: 45, unit: 'ms', status: 'ok', trend: 'stable' },
{ name: 'Auth Failures', value: 3, unit: '/h', status: 'ok', trend: 'down' },
{ name: 'Rate Limit Hits', value: 12, unit: '/h', status: 'warning', trend: 'up' },
{ name: 'Failed Logins', value: 0, unit: '/24h', status: 'ok', trend: 'stable' },
{ name: 'SSL Expiry', value: 45, unit: 'days', status: 'ok', trend: 'down' },
{ name: 'Open Ports', value: 8, unit: '', status: 'ok', trend: 'stable' },
]
export function useSecurityDashboard() {
const [tools, setTools] = useState<ToolStatus[]>([])
const [findings, setFindings] = useState<Finding[]>([])
const [summary, setSummary] = useState<SeveritySummary>({ critical: 0, high: 0, medium: 0, low: 0, info: 0, total: 0 })
const [history, setHistory] = useState<import('./types').HistoryItem[]>([])
const [loading, setLoading] = useState(true)
const [scanning, setScanning] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const [activeTab, setActiveTab] = useState<TabId>('overview')
const [monitoringMetrics, setMonitoringMetrics] = useState<MonitoringMetric[]>([])
const [activeAlerts, setActiveAlerts] = useState<ActiveAlert[]>([])
const [severityFilter, setSeverityFilter] = useState<string | null>(null)
const [toolFilter, setToolFilter] = useState<string | null>(null)
const [showFullDocs, setShowFullDocs] = useState(false)
const [scanMessage, setScanMessage] = useState<string | null>(null)
const [lastScanTime, setLastScanTime] = useState<string | null>(null)
const fetchData = useCallback(async (showLoadingSpinner = false) => {
if (showLoadingSpinner) {
setLoading(true)
}
setError(null)
try {
const [toolsRes, findingsRes, summaryRes, historyRes] = await Promise.all([
fetch('/api/v1/security/tools'),
fetch('/api/v1/security/findings'),
fetch('/api/v1/security/summary'),
fetch('/api/v1/security/history'),
])
if (toolsRes.ok) setTools(await toolsRes.json())
if (findingsRes.ok) setFindings(await findingsRes.json())
if (summaryRes.ok) setSummary(await summaryRes.json())
if (historyRes.ok) setHistory(await historyRes.json())
const [metricsRes, alertsRes] = await Promise.all([
fetch('/api/v1/security/monitoring/metrics'),
fetch('/api/v1/security/monitoring/alerts'),
])
if (metricsRes.ok) {
setMonitoringMetrics(await metricsRes.json())
} else {
setMonitoringMetrics(DEFAULT_METRICS)
}
if (alertsRes.ok) {
setActiveAlerts(await alertsRes.json())
} else {
setActiveAlerts([])
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Verbindung zum Backend fehlgeschlagen')
} finally {
setLoading(false)
}
}, [])
// Initial load
useEffect(() => {
fetchData(true)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// Auto-refresh every 60 seconds
useEffect(() => {
const interval = setInterval(() => fetchData(false), 60000)
return () => clearInterval(interval)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const runScan = async (scanType: ScanType) => {
console.log(`Starting scan: ${scanType}`)
setScanning(scanType)
setError(null)
setScanMessage(`${SCAN_TYPE_LABELS[scanType]} wird gestartet...`)
try {
const response = await fetch(`/api/v1/security/scan/${scanType}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
console.log(`Scan response status: ${response.status}`)
if (!response.ok) {
const errorText = await response.text()
console.error(`Scan error: ${errorText}`)
throw new Error(`Scan fehlgeschlagen: ${response.status} - ${errorText}`)
}
const result = await response.json()
console.log('Scan result:', result)
setScanMessage(`${SCAN_TYPE_LABELS[scanType]} laeuft im Hintergrund. Ergebnisse werden in wenigen Sekunden aktualisiert.`)
setLastScanTime(new Date().toLocaleTimeString('de-DE'))
setTimeout(() => { fetchData(false); setScanMessage(null) }, 5000)
setTimeout(() => fetchData(false), 15000)
setTimeout(() => fetchData(false), 30000)
} catch (err) {
console.error('Scan error:', err)
setError(err instanceof Error ? err.message : 'Scan fehlgeschlagen - Pruefe Browser-Konsole')
setScanMessage(null)
} finally {
setScanning(null)
}
}
const filteredFindings = findings.filter(f => {
if (severityFilter && f.severity.toUpperCase() !== severityFilter.toUpperCase()) return false
if (toolFilter && f.tool.toLowerCase() !== toolFilter.toLowerCase()) return false
return true
})
const overallStatus = getOverallStatus(summary)
return {
tools,
findings,
filteredFindings,
summary,
history,
loading,
scanning,
error,
activeTab,
setActiveTab,
monitoringMetrics,
activeAlerts,
severityFilter,
setSeverityFilter,
toolFilter,
setToolFilter,
showFullDocs,
setShowFullDocs,
scanMessage,
lastScanTime,
overallStatus,
runScan,
}
}
// --- Helper functions ---
function getOverallStatus(summary: SeveritySummary) {
if (summary.critical > 0) return { label: 'Critical Issues', color: 'bg-red-100 text-red-800' }
if (summary.high > 0) return { label: 'High Issues', color: 'bg-orange-100 text-orange-800' }
if (summary.medium > 0) return { label: 'Warnings', color: 'bg-yellow-100 text-yellow-800' }
return { label: 'Secure', color: 'bg-green-100 text-green-800' }
}
export function getSeverityBadge(severity: string) {
const base = 'px-3 py-1 rounded-full text-xs font-semibold uppercase'
switch (severity.toUpperCase()) {
case 'CRITICAL': return `${base} bg-red-100 text-red-800`
case 'HIGH': return `${base} bg-orange-100 text-orange-800`
case 'MEDIUM': return `${base} bg-yellow-100 text-yellow-800`
case 'LOW': return `${base} bg-green-100 text-green-800`
default: return `${base} bg-blue-100 text-blue-800`
}
}
export function getStatusBadge(installed: boolean) {
return installed
? 'px-2 py-1 rounded text-xs font-semibold bg-green-100 text-green-800'
: 'px-2 py-1 rounded text-xs font-semibold bg-red-100 text-red-800'
}
export function getHistoryStatusColor(status: string) {
switch (status) {
case 'success': return 'bg-green-500'
case 'warning': return 'bg-yellow-500'
case 'error': return 'bg-red-500'
default: return 'bg-slate-400'
}
}