// PARTIAL DEPRECATED — CRUD-Overlap mit Python backend-compliance // // Die folgenden CRUD-Endpoints sind deprecated, da Python (obligation_routes.py) // nun als Primary fuer List/Create/Update/Delete dient: // - ListObligations, CreateObligation, GetObligation, UpdateObligation, DeleteObligation // // AKTIV bleiben (einzigartige AI-Features ohne Python-Aequivalent): // - AssessFromScope, RunGapAnalysis, ExportAssessment, ExportDirect // - ListAssessments, GetAssessment, UpdateAssessment // - TOM-Controls: ListTOMControls, MapTOMControls, GetTOMGapAnalysis // - GetObligationsByFramework, GetFrameworks // // Go-Routen werden NICHT entfernt (Abwaertskompatibilitaet), aber Frontend // nutzt Python-Backend fuer alle CRUD-Operationen. package handlers import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/breakpilot/ai-compliance-sdk/internal/rbac" "github.com/breakpilot/ai-compliance-sdk/internal/ucca" ) // ObligationsHandlers handles API requests for the generic obligations framework type ObligationsHandlers struct { registry *ucca.ObligationsRegistry store *ucca.ObligationsStore // Optional: for persisting assessments tomIndex *ucca.TOMControlIndex tomMapper *ucca.TOMObligationMapper gapAnalyzer *ucca.TOMGapAnalyzer } // NewObligationsHandlers creates a new ObligationsHandlers instance func NewObligationsHandlers() *ObligationsHandlers { h := &ObligationsHandlers{ registry: ucca.NewObligationsRegistry(), } h.initTOM() return h } // NewObligationsHandlersWithStore creates a new ObligationsHandlers with a store func NewObligationsHandlersWithStore(store *ucca.ObligationsStore) *ObligationsHandlers { h := &ObligationsHandlers{ registry: ucca.NewObligationsRegistry(), store: store, } h.initTOM() return h } // initTOM initializes TOM control index, mapper, and gap analyzer func (h *ObligationsHandlers) initTOM() { tomIndex, err := ucca.LoadTOMControls() if err != nil { fmt.Printf("Warning: Could not load TOM controls: %v\n", err) return } h.tomIndex = tomIndex mapping, err := ucca.LoadV2TOMMapping() if err != nil { regs, err2 := ucca.LoadAllV2Regulations() if err2 == nil { var allObligations []ucca.V2Obligation for _, reg := range regs { allObligations = append(allObligations, reg.Obligations...) } h.tomMapper = ucca.NewTOMObligationMapperFromObligations(tomIndex, allObligations) } } else { h.tomMapper = ucca.NewTOMObligationMapper(tomIndex, mapping) } if h.tomMapper != nil { h.gapAnalyzer = ucca.NewTOMGapAnalyzer(h.tomMapper, tomIndex) } } // RegisterRoutes registers all obligations-related routes func (h *ObligationsHandlers) RegisterRoutes(r *gin.RouterGroup) { obligations := r.Group("/obligations") { obligations.POST("/assess", h.AssessObligations) obligations.GET("/:assessmentId", h.GetAssessment) obligations.GET("/:assessmentId/by-regulation", h.GetByRegulation) obligations.GET("/:assessmentId/by-deadline", h.GetByDeadline) obligations.GET("/:assessmentId/by-responsible", h.GetByResponsible) obligations.POST("/export/memo", h.ExportMemo) obligations.POST("/export/direct", h.ExportMemoFromOverview) obligations.GET("/regulations", h.ListRegulations) obligations.GET("/regulations/:regulationId/decision-tree", h.GetDecisionTree) obligations.POST("/quick-check", h.QuickCheck) obligations.POST("/assess-from-scope", h.AssessFromScope) obligations.GET("/tom-controls/for-obligation/:obligationId", h.GetTOMControlsForObligation) obligations.POST("/gap-analysis", h.GapAnalysis) obligations.GET("/tom-controls/:controlId/obligations", h.GetObligationsForControl) } } // AssessObligations assesses which obligations apply based on provided facts // POST /sdk/v1/ucca/obligations/assess func (h *ObligationsHandlers) AssessObligations(c *gin.Context) { tenantID := rbac.GetTenantID(c) if tenantID == uuid.Nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Tenant ID required"}) return } var req ucca.ObligationsAssessRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } if req.Facts == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Facts are required"}) return } overview := h.registry.EvaluateAll(tenantID, req.Facts, req.OrganizationName) var warnings []string if len(overview.ApplicableRegulations) == 0 { warnings = append(warnings, "Keine der konfigurierten Regulierungen scheint anwendbar zu sein. Bitte prüfen Sie die eingegebenen Daten.") } if overview.ExecutiveSummary.OverdueObligations > 0 { warnings = append(warnings, "Es gibt überfällige Pflichten, die sofortige Aufmerksamkeit erfordern.") } if h.store != nil { assessment := &ucca.ObligationsAssessment{ ID: overview.ID, TenantID: tenantID, OrganizationName: req.OrganizationName, Facts: req.Facts, Overview: overview, Status: "completed", CreatedAt: time.Now(), UpdatedAt: time.Now(), CreatedBy: rbac.GetUserID(c), } if err := h.store.CreateAssessment(c.Request.Context(), assessment); err != nil { c.Set("store_error", err.Error()) } } c.JSON(http.StatusOK, ucca.ObligationsAssessResponse{ Overview: overview, Warnings: warnings, }) } // GetAssessment retrieves a stored assessment by ID // GET /sdk/v1/ucca/obligations/:assessmentId func (h *ObligationsHandlers) GetAssessment(c *gin.Context) { tenantID := rbac.GetTenantID(c) assessmentID := c.Param("assessmentId") if h.store == nil { c.JSON(http.StatusNotImplemented, gin.H{"error": "Persistence not configured"}) return } id, err := uuid.Parse(assessmentID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"}) return } assessment, err := h.store.GetAssessment(c.Request.Context(), tenantID, id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"}) return } c.JSON(http.StatusOK, assessment.Overview) } // QuickCheck performs a quick obligations check without persistence // POST /sdk/v1/ucca/obligations/quick-check func (h *ObligationsHandlers) QuickCheck(c *gin.Context) { var req struct { EmployeeCount int `json:"employee_count"` AnnualRevenue float64 `json:"annual_revenue"` BalanceSheetTotal float64 `json:"balance_sheet_total,omitempty"` Country string `json:"country"` PrimarySector string `json:"primary_sector"` SpecialServices []string `json:"special_services,omitempty"` IsKRITIS bool `json:"is_kritis,omitempty"` ProcessesPersonalData bool `json:"processes_personal_data,omitempty"` UsesAI bool `json:"uses_ai,omitempty"` IsFinancialInstitution bool `json:"is_financial_institution,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } facts := &ucca.UnifiedFacts{ Organization: ucca.OrganizationFacts{ EmployeeCount: req.EmployeeCount, AnnualRevenue: req.AnnualRevenue, BalanceSheetTotal: req.BalanceSheetTotal, Country: req.Country, EUMember: isEUCountry(req.Country), }, Sector: ucca.SectorFacts{ PrimarySector: req.PrimarySector, SpecialServices: req.SpecialServices, IsKRITIS: req.IsKRITIS, KRITISThresholdMet: req.IsKRITIS, IsFinancialInstitution: req.IsFinancialInstitution, }, DataProtection: ucca.DataProtectionFacts{ ProcessesPersonalData: req.ProcessesPersonalData, }, AIUsage: ucca.AIUsageFacts{ UsesAI: req.UsesAI, }, Financial: ucca.FinancialFacts{ IsRegulated: req.IsFinancialInstitution, }, } tenantID := rbac.GetTenantID(c) if tenantID == uuid.Nil { tenantID = uuid.New() } overview := h.registry.EvaluateAll(tenantID, facts, "") c.JSON(http.StatusOK, gin.H{ "applicable_regulations": overview.ApplicableRegulations, "total_obligations": len(overview.Obligations), "critical_obligations": overview.ExecutiveSummary.CriticalObligations, "sanctions_summary": overview.SanctionsSummary, "executive_summary": overview.ExecutiveSummary, }) } // AssessFromScope assesses obligations from a ScopeDecision // POST /sdk/v1/ucca/obligations/assess-from-scope func (h *ObligationsHandlers) AssessFromScope(c *gin.Context) { tenantID := rbac.GetTenantID(c) if tenantID == uuid.Nil { tenantID = uuid.New() } var scope ucca.ScopeDecision if err := c.ShouldBindJSON(&scope); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } facts := ucca.MapScopeToFacts(&scope) overview := h.registry.EvaluateAll(tenantID, facts, "") if h.tomMapper != nil { overview.TOMControlRequirements = h.tomMapper.DeriveControlsFromObligations(overview.Obligations) } var warnings []string if len(overview.ApplicableRegulations) == 0 { warnings = append(warnings, "Keine anwendbaren Regulierungen gefunden. Pruefen Sie die Scope-Angaben.") } c.JSON(http.StatusOK, ucca.ObligationsAssessResponse{ Overview: overview, Warnings: warnings, }) }