feat: Async scan with polling — no more timeout issues

Fundamental fix: scans now run asynchronously with progress polling.

Backend:
- POST /scan starts background task, returns scan_id immediately
- GET /scan/{scan_id} returns status + progress + result when done
- 7 progress steps shown: Website scan, DSI discovery, DSE analysis,
  SOLL/IST comparison, corrections, report, email
- In-memory job store (dict with scan_id → status/result)
- No timeout limits on scan duration

Frontend:
- POST starts scan, receives scan_id
- Polls GET every 5 seconds (max 120 attempts = 10 min)
- Shows live progress message during scan
- Displays result when completed, error when failed

Proxy:
- POST timeout reduced to 30s (just starts the job)
- GET timeout 10s (just status check)
- No more 504/connection-dropped errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-05 07:30:09 +02:00
parent d7b287889e
commit cb607bf228
4 changed files with 189 additions and 57 deletions
+32 -3
View File
@@ -40,13 +40,42 @@ export default function AgentPage() {
setScanError(null)
setScanData(null)
try {
const res = await fetch('/api/sdk/v1/agent/scan', {
// Step 1: Start async scan
const startRes = await fetch('/api/sdk/v1/agent/scan', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: url.trim(), mode }),
})
if (!res.ok) throw new Error(`Scan fehlgeschlagen: ${res.status}`)
setScanData(await res.json())
if (!startRes.ok) throw new Error(`Scan konnte nicht gestartet werden: ${startRes.status}`)
const { scan_id } = await startRes.json()
if (!scan_id) throw new Error('Keine Scan-ID erhalten')
// Step 2: Poll for results
let attempts = 0
const maxAttempts = 120 // 10 min at 5s intervals
while (attempts < maxAttempts) {
await new Promise(r => setTimeout(r, 5000))
const pollRes = await fetch(`/api/sdk/v1/agent/scan?scan_id=${scan_id}`)
if (!pollRes.ok) { attempts++; continue }
const status = await pollRes.json()
// Update progress message
if (status.progress) {
setScanError(null)
// Show progress as temporary "error" (will be cleared when done)
setScanData({ _progress: status.progress } as any)
}
if (status.status === 'completed' && status.result) {
setScanData(status.result)
break
}
if (status.status === 'failed') {
throw new Error(status.error || 'Scan fehlgeschlagen')
}
attempts++
}
if (attempts >= maxAttempts) throw new Error('Scan-Timeout (10 Minuten)')
} catch (e) {
setScanError(e instanceof Error ? e.message : 'Unbekannter Fehler')
} finally {