Files
breakpilot-compliance/ai-compliance-sdk/internal/api/handlers/gci_handlers.go
Benjamin Boenisch 7a09086930
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 28s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 17s
feat(gci): add Gesamt-Compliance-Index scoring engine and dashboard
Implements the 4-level GCI scoring model (Module -> Risk-Weighted -> Regulation Area -> Final GCI)
with DSGVO, NIS2, ISO 27001, and EU AI Act integration.

Backend:
- 9 Go files: engine, models, weights, validity, NIS2 roles/scoring, ISO mapping/gap-analysis, mock data
- GCI handlers with 13 API endpoints under /sdk/v1/gci/
- Routes registered in main.go

Frontend:
- TypeScript types, API client, Next.js API proxy
- Dashboard page with 6 tabs (Overview, Breakdown, NIS2, ISO 27001, Matrix, Audit Trail)
- Sidebar navigation entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:20:17 +01:00

189 lines
4.9 KiB
Go

package handlers
import (
"net/http"
"github.com/breakpilot/ai-compliance-sdk/internal/gci"
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
"github.com/gin-gonic/gin"
)
type GCIHandlers struct {
engine *gci.Engine
}
func NewGCIHandlers(engine *gci.Engine) *GCIHandlers {
return &GCIHandlers{engine: engine}
}
// GetScore returns the GCI score for the current tenant
// GET /sdk/v1/gci/score
func (h *GCIHandlers) GetScore(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
profile := c.DefaultQuery("profile", "default")
result := h.engine.Calculate(tenantID, profile)
c.JSON(http.StatusOK, result)
}
// GetScoreBreakdown returns the detailed 4-level GCI breakdown
// GET /sdk/v1/gci/score/breakdown
func (h *GCIHandlers) GetScoreBreakdown(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
profile := c.DefaultQuery("profile", "default")
breakdown := h.engine.CalculateBreakdown(tenantID, profile)
c.JSON(http.StatusOK, breakdown)
}
// GetHistory returns historical GCI snapshots for trend analysis
// GET /sdk/v1/gci/score/history
func (h *GCIHandlers) GetHistory(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
history := h.engine.GetHistory(tenantID)
c.JSON(http.StatusOK, gin.H{
"tenant_id": tenantID,
"snapshots": history,
"total": len(history),
})
}
// GetMatrix returns the compliance matrix (roles x regulations)
// GET /sdk/v1/gci/matrix
func (h *GCIHandlers) GetMatrix(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
matrix := h.engine.GetMatrix(tenantID)
c.JSON(http.StatusOK, gin.H{
"tenant_id": tenantID,
"matrix": matrix,
})
}
// GetAuditTrail returns the audit trail for the latest GCI calculation
// GET /sdk/v1/gci/audit-trail
func (h *GCIHandlers) GetAuditTrail(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
profile := c.DefaultQuery("profile", "default")
result := h.engine.Calculate(tenantID, profile)
c.JSON(http.StatusOK, gin.H{
"tenant_id": tenantID,
"gci_score": result.GCIScore,
"audit_trail": result.AuditTrail,
})
}
// GetNIS2Score returns the NIS2-specific compliance score
// GET /sdk/v1/gci/nis2/score
func (h *GCIHandlers) GetNIS2Score(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
score := gci.CalculateNIS2Score(tenantID)
c.JSON(http.StatusOK, score)
}
// ListNIS2Roles returns available NIS2 responsibility roles
// GET /sdk/v1/gci/nis2/roles
func (h *GCIHandlers) ListNIS2Roles(c *gin.Context) {
roles := gci.ListNIS2Roles()
c.JSON(http.StatusOK, gin.H{
"roles": roles,
"total": len(roles),
})
}
// AssignNIS2Role assigns a NIS2 role to a user (stub - returns mock)
// POST /sdk/v1/gci/nis2/roles/assign
func (h *GCIHandlers) AssignNIS2Role(c *gin.Context) {
var req struct {
RoleID string `json:"role_id" binding:"required"`
UserID string `json:"user_id" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
role, found := gci.GetNIS2Role(req.RoleID)
if !found {
c.JSON(http.StatusNotFound, gin.H{"error": "NIS2 role not found"})
return
}
c.JSON(http.StatusOK, gin.H{
"status": "assigned",
"role": role,
"user_id": req.UserID,
})
}
// GetISOGapAnalysis returns the ISO 27001 gap analysis
// GET /sdk/v1/gci/iso/gap-analysis
func (h *GCIHandlers) GetISOGapAnalysis(c *gin.Context) {
tenantID := rbac.GetTenantID(c).String()
analysis := gci.CalculateISOGapAnalysis(tenantID)
c.JSON(http.StatusOK, analysis)
}
// ListISOMappings returns all ISO 27001 control mappings
// GET /sdk/v1/gci/iso/mappings
func (h *GCIHandlers) ListISOMappings(c *gin.Context) {
category := c.Query("category")
if category != "" {
controls := gci.GetISOControlsByCategory(category)
c.JSON(http.StatusOK, gin.H{
"controls": controls,
"total": len(controls),
"category": category,
})
return
}
categories := []string{"A.5", "A.6", "A.7", "A.8"}
result := make(map[string][]gci.ISOControl)
total := 0
for _, cat := range categories {
controls := gci.GetISOControlsByCategory(cat)
if len(controls) > 0 {
result[cat] = controls
total += len(controls)
}
}
c.JSON(http.StatusOK, gin.H{
"categories": result,
"total": total,
})
}
// GetISOMapping returns a single ISO control by ID
// GET /sdk/v1/gci/iso/mappings/:controlId
func (h *GCIHandlers) GetISOMapping(c *gin.Context) {
controlID := c.Param("controlId")
control, found := gci.GetISOControlByID(controlID)
if !found {
c.JSON(http.StatusNotFound, gin.H{"error": "ISO control not found"})
return
}
c.JSON(http.StatusOK, control)
}
// GetWeightProfiles returns available weighting profiles
// GET /sdk/v1/gci/profiles
func (h *GCIHandlers) GetWeightProfiles(c *gin.Context) {
profiles := []string{"default", "nis2_relevant", "ki_nutzer"}
result := make([]gci.WeightProfile, 0, len(profiles))
for _, id := range profiles {
result = append(result, gci.GetProfile(id))
}
c.JSON(http.StatusOK, gin.H{
"profiles": result,
})
}