- LegalContext → LegalDomainContext (Kollision mit legal_rag.go LegalContext) - ExplainResponse.LegalContext bleibt unveraendert (RAG-Typ) - Registration Handler: Intake ist struct, kein []byte - Unbenutzten json Import entfernt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
221 lines
6.2 KiB
Go
221 lines
6.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// RegistrationHandlers handles EU AI Database registration endpoints
|
|
type RegistrationHandlers struct {
|
|
store *ucca.RegistrationStore
|
|
uccaStore *ucca.Store
|
|
}
|
|
|
|
// NewRegistrationHandlers creates new registration handlers
|
|
func NewRegistrationHandlers(store *ucca.RegistrationStore, uccaStore *ucca.Store) *RegistrationHandlers {
|
|
return &RegistrationHandlers{store: store, uccaStore: uccaStore}
|
|
}
|
|
|
|
// Create creates a new registration
|
|
func (h *RegistrationHandlers) Create(c *gin.Context) {
|
|
var reg ucca.AIRegistration
|
|
if err := c.ShouldBindJSON(®); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
|
|
if tenantID == uuid.Nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
|
return
|
|
}
|
|
reg.TenantID = tenantID
|
|
|
|
if reg.SystemName == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "system_name required"})
|
|
return
|
|
}
|
|
|
|
if err := h.store.Create(c.Request.Context(), ®); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create registration: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, reg)
|
|
}
|
|
|
|
// List lists all registrations for the tenant
|
|
func (h *RegistrationHandlers) List(c *gin.Context) {
|
|
tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
|
|
if tenantID == uuid.Nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
|
return
|
|
}
|
|
|
|
registrations, err := h.store.List(c.Request.Context(), tenantID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list registrations: " + err.Error()})
|
|
return
|
|
}
|
|
if registrations == nil {
|
|
registrations = []ucca.AIRegistration{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"registrations": registrations, "total": len(registrations)})
|
|
}
|
|
|
|
// Get returns a single registration
|
|
func (h *RegistrationHandlers) Get(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
|
return
|
|
}
|
|
|
|
reg, err := h.store.GetByID(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, reg)
|
|
}
|
|
|
|
// Update updates a registration
|
|
func (h *RegistrationHandlers) Update(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
|
return
|
|
}
|
|
|
|
existing, err := h.store.GetByID(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"})
|
|
return
|
|
}
|
|
|
|
var updates ucca.AIRegistration
|
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Merge updates into existing
|
|
updates.ID = existing.ID
|
|
updates.TenantID = existing.TenantID
|
|
updates.CreatedAt = existing.CreatedAt
|
|
|
|
if err := h.store.Update(c.Request.Context(), &updates); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, updates)
|
|
}
|
|
|
|
// UpdateStatus changes the registration status
|
|
func (h *RegistrationHandlers) UpdateStatus(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
|
return
|
|
}
|
|
|
|
var body struct {
|
|
Status string `json:"status"`
|
|
SubmittedBy string `json:"submitted_by"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
|
return
|
|
}
|
|
|
|
validStatuses := map[string]bool{
|
|
"draft": true, "ready": true, "submitted": true,
|
|
"registered": true, "update_required": true, "withdrawn": true,
|
|
}
|
|
if !validStatuses[body.Status] {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid status. Valid: draft, ready, submitted, registered, update_required, withdrawn"})
|
|
return
|
|
}
|
|
|
|
if err := h.store.UpdateStatus(c.Request.Context(), id, body.Status, body.SubmittedBy); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update status: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"id": id, "status": body.Status})
|
|
}
|
|
|
|
// Prefill creates a registration pre-filled from a UCCA assessment
|
|
func (h *RegistrationHandlers) Prefill(c *gin.Context) {
|
|
assessmentID, err := uuid.Parse(c.Param("assessment_id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"})
|
|
return
|
|
}
|
|
|
|
tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
|
|
if tenantID == uuid.Nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
|
return
|
|
}
|
|
|
|
// Load UCCA assessment
|
|
assessment, err := h.uccaStore.GetAssessment(c.Request.Context(), assessmentID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"})
|
|
return
|
|
}
|
|
|
|
// Pre-fill registration from assessment intake
|
|
intake := assessment.Intake
|
|
|
|
reg := ucca.AIRegistration{
|
|
TenantID: tenantID,
|
|
SystemName: intake.Title,
|
|
SystemDescription: intake.UseCaseText,
|
|
IntendedPurpose: intake.UseCaseText,
|
|
RiskClassification: string(assessment.RiskLevel),
|
|
GPAIClassification: "none",
|
|
RegistrationStatus: "draft",
|
|
UCCAAssessmentID: &assessmentID,
|
|
}
|
|
|
|
// Map domain to readable text
|
|
if intake.Domain != "" {
|
|
reg.IntendedPurpose = string(intake.Domain) + ": " + intake.UseCaseText
|
|
}
|
|
|
|
c.JSON(http.StatusOK, reg)
|
|
}
|
|
|
|
// Export generates the EU AI Database submission JSON
|
|
func (h *RegistrationHandlers) Export(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
|
|
return
|
|
}
|
|
|
|
reg, err := h.store.GetByID(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"})
|
|
return
|
|
}
|
|
|
|
exportJSON := h.store.BuildExportJSON(reg)
|
|
|
|
// Save export data to DB
|
|
reg.ExportData = exportJSON
|
|
h.store.Update(c.Request.Context(), reg)
|
|
|
|
c.Header("Content-Type", "application/json")
|
|
c.Header("Content-Disposition", "attachment; filename=eu_ai_registration_"+reg.SystemName+".json")
|
|
c.Data(http.StatusOK, "application/json", exportJSON)
|
|
}
|