feat: Fortschritts-Tracker + Verifikation-Endpoints + Tech-File Erweiterung
- Übersicht: Completeness Gates durch Projektfortschritts-Tracker ersetzt (6 CE-Prozessschritte mit Status + Naechster-Schritt Empfehlung) - Verifikation: GET/POST/DELETE /verifications Endpoints + Alias-Handler - Tech-File: Anhang IV Struktur-Erweiterung - Maßnahmen: Expandable Details vorbereitet Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -158,178 +158,3 @@ func (h *IACEHandler) VerifyMitigation(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "mitigation verified"})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Evidence & Verification
|
||||
// ============================================================================
|
||||
|
||||
// UploadEvidence handles POST /projects/:id/evidence
|
||||
// Creates a new evidence record for a project.
|
||||
func (h *IACEHandler) UploadEvidence(c *gin.Context) {
|
||||
projectID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
MitigationID *uuid.UUID `json:"mitigation_id,omitempty"`
|
||||
VerificationPlanID *uuid.UUID `json:"verification_plan_id,omitempty"`
|
||||
FileName string `json:"file_name" binding:"required"`
|
||||
FilePath string `json:"file_path" binding:"required"`
|
||||
FileHash string `json:"file_hash" binding:"required"`
|
||||
FileSize int64 `json:"file_size" binding:"required"`
|
||||
MimeType string `json:"mime_type" binding:"required"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userID := rbac.GetUserID(c)
|
||||
|
||||
evidence := &iace.Evidence{
|
||||
ProjectID: projectID,
|
||||
MitigationID: req.MitigationID,
|
||||
VerificationPlanID: req.VerificationPlanID,
|
||||
FileName: req.FileName,
|
||||
FilePath: req.FilePath,
|
||||
FileHash: req.FileHash,
|
||||
FileSize: req.FileSize,
|
||||
MimeType: req.MimeType,
|
||||
Description: req.Description,
|
||||
UploadedBy: userID,
|
||||
}
|
||||
|
||||
if err := h.store.CreateEvidence(c.Request.Context(), evidence); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Audit trail
|
||||
newVals, _ := json.Marshal(evidence)
|
||||
h.store.AddAuditEntry(
|
||||
c.Request.Context(), projectID, "evidence", evidence.ID,
|
||||
iace.AuditActionCreate, userID.String(), nil, newVals,
|
||||
)
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"evidence": evidence})
|
||||
}
|
||||
|
||||
// ListEvidence handles GET /projects/:id/evidence
|
||||
// Lists all evidence records for a project.
|
||||
func (h *IACEHandler) ListEvidence(c *gin.Context) {
|
||||
projectID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
evidence, err := h.store.ListEvidence(c.Request.Context(), projectID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if evidence == nil {
|
||||
evidence = []iace.Evidence{}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"evidence": evidence,
|
||||
"total": len(evidence),
|
||||
})
|
||||
}
|
||||
|
||||
// CreateVerificationPlan handles POST /projects/:id/verification-plan
|
||||
// Creates a new verification plan for a project.
|
||||
func (h *IACEHandler) CreateVerificationPlan(c *gin.Context) {
|
||||
projectID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req iace.CreateVerificationPlanRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Override project ID from URL path
|
||||
req.ProjectID = projectID
|
||||
|
||||
plan, err := h.store.CreateVerificationPlan(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Audit trail
|
||||
userID := rbac.GetUserID(c)
|
||||
newVals, _ := json.Marshal(plan)
|
||||
h.store.AddAuditEntry(
|
||||
c.Request.Context(), projectID, "verification_plan", plan.ID,
|
||||
iace.AuditActionCreate, userID.String(), nil, newVals,
|
||||
)
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"verification_plan": plan})
|
||||
}
|
||||
|
||||
// UpdateVerificationPlan handles PUT /verification-plan/:vid
|
||||
// Updates a verification plan with the provided fields.
|
||||
func (h *IACEHandler) UpdateVerificationPlan(c *gin.Context) {
|
||||
planID, err := uuid.Parse(c.Param("vid"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid verification plan ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
plan, err := h.store.UpdateVerificationPlan(c.Request.Context(), planID, updates)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if plan == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "verification plan not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"verification_plan": plan})
|
||||
}
|
||||
|
||||
// CompleteVerification handles POST /verification-plan/:vid/complete
|
||||
// Marks a verification plan as completed with a result.
|
||||
func (h *IACEHandler) CompleteVerification(c *gin.Context) {
|
||||
planID, err := uuid.Parse(c.Param("vid"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid verification plan ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Result string `json:"result" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userID := rbac.GetUserID(c)
|
||||
|
||||
if err := h.store.CompleteVerification(
|
||||
c.Request.Context(), planID, req.Result, userID.String(),
|
||||
); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "verification completed"})
|
||||
}
|
||||
|
||||
@@ -35,19 +35,30 @@ func (h *IACEHandler) GenerateTechFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Define the standard CE technical file sections to generate
|
||||
// Define sections per EU Machinery Regulation 2023/1230 Annex IV structure.
|
||||
// Core Annex IV sections come first, then supplementary CE-Akte sections.
|
||||
sectionDefinitions := []struct {
|
||||
SectionType string
|
||||
Title string
|
||||
}{
|
||||
{"general_description", "General Description of the Machinery"},
|
||||
{"risk_assessment_report", "Risk Assessment Report"},
|
||||
{"hazard_log_combined", "Combined Hazard Log"},
|
||||
{"essential_requirements", "Essential Health and Safety Requirements"},
|
||||
{"design_specifications", "Design Specifications and Drawings"},
|
||||
{"test_reports", "Test Reports and Verification Results"},
|
||||
{"standards_applied", "Applied Harmonised Standards"},
|
||||
{"declaration_of_conformity", "EU Declaration of Conformity"},
|
||||
// Annex IV mandatory sections
|
||||
{"general_description", "Anhang IV.1 — Allgemeine Beschreibung der Maschine"},
|
||||
{"design_specifications", "Anhang IV.2 — Gesamtplan und Schaltplaene"},
|
||||
{"component_list", "Anhang IV.3 — Detailplaene und Komponentenliste"},
|
||||
{"risk_assessment_report", "Anhang IV.4 — Risikobeurteilung"},
|
||||
{"standards_applied", "Anhang IV.5 — Angewandte harmonisierte Normen"},
|
||||
{"test_reports", "Anhang IV.6 — Pruefberichte und Ergebnisse"},
|
||||
{"instructions_for_use", "Anhang IV.7 — Betriebsanleitung"},
|
||||
{"declaration_of_conformity", "Anhang IV.8 — EU-Konformitaetserklaerung"},
|
||||
{"assembly_declaration", "Anhang IV.9 — Einbauerklaerung (falls zutreffend)"},
|
||||
// Supplementary CE-Akte sections
|
||||
{"hazard_log_combined", "Gefaehrdungsprotokoll (Hazard Log)"},
|
||||
{"essential_requirements", "Grundlegende Anforderungen (EHSR)"},
|
||||
{"mitigation_report", "Massnahmenbericht (3-Stufen-Verfahren)"},
|
||||
{"verification_report", "Verifikationsbericht"},
|
||||
{"evidence_index", "Nachweisverzeichnis"},
|
||||
{"classification_report", "Regulatorischer Klassifizierungsbericht"},
|
||||
{"monitoring_plan", "Post-Market-Monitoring-Plan"},
|
||||
}
|
||||
|
||||
// Check if project has AI components for additional sections
|
||||
@@ -184,6 +195,7 @@ func (h *IACEHandler) GenerateSingleSection(c *gin.Context) {
|
||||
"evidence_index": "Evidence Index",
|
||||
"instructions_for_use": "Instructions for Use",
|
||||
"monitoring_plan": "Post-Market Monitoring Plan",
|
||||
"assembly_declaration": "Anhang IV.9 — Einbauerklaerung (falls zutreffend)",
|
||||
"ai_intended_purpose": "AI System Intended Purpose",
|
||||
"ai_model_description": "AI Model Description and Training Data",
|
||||
"ai_risk_management": "AI Risk Management System",
|
||||
|
||||
Reference in New Issue
Block a user