1cf5de1d45
Phase 1 — Intake + Scope + Path: - Migration 119: compliance_cra_projects table (intake + classification + path + status state machine) - Backend service cra_routes.py: CRUD + scope-check + path-select - Deterministic Annex III/IV classifier (verbatim mapping from migration 059 wiki) - Path validation per classification (CRITICAL → notified_body mandatory) - Frontend: project list, dashboard, 3-step wizard (intake/scope/path) - Sidebar entry under "CRA Compliance" (red) Phase 2 — Annex I Requirements + Priorisierungs-Backlog: - cra_annex_i_data.py: 40 Annex-I requirements (8 categories), 9 measures (M540-M548), 3 CRA deadlines - Endpoints: /requirements (40 items), /backlog (priority-sorted with deadline pressure) - Frontend: requirements table with filters + expandable details, backlog with deadline banner + score-ranked table - Dashboard KPI cards (Critical count, days to CE deadline, etc.) + top-10 backlog snippet Phase 3 — SBOM Upload + Automated Checks: - Migration 120: compliance_cra_sboms (versioned uploads, CycloneDX + SPDX) - SBOM endpoints: POST /sbom/upload (format detection, summary extraction), GET /sboms - Checks reuse compliance_evidence_checks: init creates 6 default CRA checks, run executes - Real implementations: cra_security_txt (HTTP + Contact: line) and cra_tls_cert_check (TLS handshake) - Frontend: SBOM file upload + version list, Checks page with per-check URL input + Run button Backend-Reuse: gap_projects (intake pre-population), compliance_evidence_checks/_check_results. Tenant scoping via existing X-Tenant-ID header pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
|
|
const BACKEND_URL = process.env.BACKEND_URL || 'http://backend-compliance:8002'
|
|
|
|
function tenantHeader(request: NextRequest): string {
|
|
return request.headers.get('x-tenant-id') || '00000000-0000-0000-0000-000000000001'
|
|
}
|
|
|
|
/** GET /api/sdk/v1/cra/projects -> Backend list */
|
|
export async function GET(request: NextRequest) {
|
|
const tenantId = tenantHeader(request)
|
|
const { searchParams } = new URL(request.url)
|
|
const qs = searchParams.toString()
|
|
try {
|
|
const resp = await fetch(
|
|
`${BACKEND_URL}/api/v1/cra/projects${qs ? `?${qs}` : ''}`,
|
|
{ headers: { 'X-Tenant-ID': tenantId } }
|
|
)
|
|
const body = await resp.text()
|
|
return new NextResponse(body, {
|
|
status: resp.status,
|
|
headers: { 'Content-Type': resp.headers.get('Content-Type') || 'application/json' },
|
|
})
|
|
} catch (err) {
|
|
return NextResponse.json(
|
|
{ error: 'Backend unreachable', details: String(err) },
|
|
{ status: 502 }
|
|
)
|
|
}
|
|
}
|
|
|
|
/** POST /api/sdk/v1/cra/projects -> Backend create */
|
|
export async function POST(request: NextRequest) {
|
|
const tenantId = tenantHeader(request)
|
|
const body = await request.text()
|
|
try {
|
|
const resp = await fetch(`${BACKEND_URL}/api/v1/cra/projects`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-Tenant-ID': tenantId,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body,
|
|
})
|
|
const text = await resp.text()
|
|
return new NextResponse(text, {
|
|
status: resp.status,
|
|
headers: { 'Content-Type': resp.headers.get('Content-Type') || 'application/json' },
|
|
})
|
|
} catch (err) {
|
|
return NextResponse.json(
|
|
{ error: 'Backend unreachable', details: String(err) },
|
|
{ status: 502 }
|
|
)
|
|
}
|
|
}
|