'use client' import React, { useEffect, useState, useCallback, use } from 'react' interface CheckItem { id: string check_code: string title: string description: string check_type: string target_url: string | null linked_req_ids: string[] last_run_at: string | null is_active: boolean latest_result: { status: string; message: string; ran_at: string } | null } interface ChecksResponse { project_id: string total: number items: CheckItem[] } const STATUS_STYLE: Record = { pass: 'bg-green-100 text-green-800', fail: 'bg-red-100 text-red-800', manual_review_required: 'bg-yellow-100 text-yellow-800', } export default function ChecksPage({ params, }: { params: Promise<{ projectId: string }> }) { const { projectId } = use(params) const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [running, setRunning] = useState(null) const [urlInputs, setUrlInputs] = useState>({}) const tenant = '00000000-0000-0000-0000-000000000001' const load = useCallback(async () => { try { const res = await fetch(`/api/sdk/v1/cra/projects/${projectId}/checks`, { headers: { 'X-Tenant-ID': tenant }, }) if (!res.ok) throw new Error(await res.text()) const json: ChecksResponse = await res.json() setData(json) const u: Record = {} for (const c of json.items) u[c.id] = c.target_url || '' setUrlInputs(u) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler beim Laden') } finally { setLoading(false) } }, [projectId]) useEffect(() => { load() }, [load]) const initChecks = async () => { setError('') try { const res = await fetch(`/api/sdk/v1/cra/projects/${projectId}/checks`, { method: 'POST', headers: { 'X-Tenant-ID': tenant }, }) if (!res.ok) throw new Error(await res.text()) await load() } catch (e) { setError(e instanceof Error ? e.message : 'Init fehlgeschlagen') } } const runCheck = async (checkId: string) => { setRunning(checkId) setError('') try { const res = await fetch(`/api/sdk/v1/cra/checks/${checkId}/run`, { method: 'POST', headers: { 'X-Tenant-ID': tenant, 'Content-Type': 'application/json' }, body: JSON.stringify({ target_url: urlInputs[checkId] || null }), }) if (!res.ok) throw new Error(await res.text()) await load() } catch (e) { setError(e instanceof Error ? e.message : 'Run fehlgeschlagen') } finally { setRunning(null) } } if (loading) return

Laedt...

return (
← Zurueck zum Projekt

Automatisierte Checks

CRA-typische Online-Pruefungen: security.txt, Update-Policy, TLS-Konfiguration, Vuln-Disclosure.

{error && (
{error}
)} {data && data.items.length === 0 && (

Noch keine Checks fuer dieses Projekt konfiguriert.

)} {data && data.items.length > 0 && (
{data.items.map(c => (

{c.title}

{c.check_code}

{c.description}

{c.linked_req_ids.length > 0 && (
{c.linked_req_ids.map(r => ( {r} ))}
)}
{c.latest_result && ( {c.latest_result.status} )}
{(c.check_type === 'url_probe' || c.check_type === 'tls_probe' || c.check_type === 'manual_review') && (
setUrlInputs({ ...urlInputs, [c.id]: e.target.value })} className="flex-1 px-3 py-1.5 border border-gray-300 rounded text-sm focus:ring-2 focus:ring-red-500" />
)} {c.latest_result && (
{c.latest_result.message}
Geprueft: {new Date(c.latest_result.ran_at).toLocaleString('de-DE')}
)}
))}
)}
Hinweis: Aktuell implementiert: cra_security_txt (HTTP) und cra_tls_cert_check (TLS-Handshake). Andere Check-Typen sind als manual_review_required markiert — der Pruefer beantwortet sie manuell.
) }