Files
breakpilot-compliance/ai-compliance-sdk/cmd/server/main.go
Benjamin Boenisch 85d2362724 feat(academy): add PDF certificate generation and download endpoint
Add gofpdf-based certificate PDF generation for the Compliance Academy.
Landscape A4 certificates with company branding, course details, and
verification URL. New route: GET /sdk/v1/academy/certificates/:id/pdf

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:05:31 +01:00

693 lines
27 KiB
Go

package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/breakpilot/ai-compliance-sdk/internal/api/handlers"
"github.com/breakpilot/ai-compliance-sdk/internal/audit"
"github.com/breakpilot/ai-compliance-sdk/internal/config"
"github.com/breakpilot/ai-compliance-sdk/internal/dsgvo"
"github.com/breakpilot/ai-compliance-sdk/internal/llm"
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
"github.com/breakpilot/ai-compliance-sdk/internal/academy"
"github.com/breakpilot/ai-compliance-sdk/internal/incidents"
"github.com/breakpilot/ai-compliance-sdk/internal/roadmap"
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
"github.com/breakpilot/ai-compliance-sdk/internal/whistleblower"
"github.com/breakpilot/ai-compliance-sdk/internal/dsb"
"github.com/breakpilot/ai-compliance-sdk/internal/multitenant"
"github.com/breakpilot/ai-compliance-sdk/internal/reporting"
"github.com/breakpilot/ai-compliance-sdk/internal/sso"
"github.com/breakpilot/ai-compliance-sdk/internal/vendor"
"github.com/breakpilot/ai-compliance-sdk/internal/workshop"
"github.com/breakpilot/ai-compliance-sdk/internal/portfolio"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
// Load configuration
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
// Set Gin mode
if cfg.IsProduction() {
gin.SetMode(gin.ReleaseMode)
}
// Connect to database
ctx := context.Background()
pool, err := pgxpool.New(ctx, cfg.DatabaseURL)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer pool.Close()
// Verify connection
if err := pool.Ping(ctx); err != nil {
log.Fatalf("Failed to ping database: %v", err)
}
log.Println("Connected to database")
// Initialize stores
rbacStore := rbac.NewStore(pool)
auditStore := audit.NewStore(pool)
dsgvoStore := dsgvo.NewStore(pool)
uccaStore := ucca.NewStore(pool)
escalationStore := ucca.NewEscalationStore(pool)
roadmapStore := roadmap.NewStore(pool)
workshopStore := workshop.NewStore(pool)
portfolioStore := portfolio.NewStore(pool)
academyStore := academy.NewStore(pool)
whistleblowerStore := whistleblower.NewStore(pool)
incidentStore := incidents.NewStore(pool)
vendorStore := vendor.NewStore(pool)
reportingStore := reporting.NewStore(pool, dsgvoStore, vendorStore, incidentStore, whistleblowerStore, academyStore)
ssoStore := sso.NewStore(pool)
multitenantStore := multitenant.NewStore(pool, rbacStore, reportingStore)
dsbStore := dsb.NewStore(pool, reportingStore)
// Initialize services
rbacService := rbac.NewService(rbacStore)
policyEngine := rbac.NewPolicyEngine(rbacService, rbacStore)
// Initialize LLM providers
providerRegistry := llm.NewProviderRegistry(cfg.LLMProvider, cfg.LLMFallbackProvider)
// Register Ollama adapter
ollamaAdapter := llm.NewOllamaAdapter(cfg.OllamaURL, cfg.OllamaDefaultModel)
providerRegistry.Register(ollamaAdapter)
// Register Anthropic adapter if API key is configured
if cfg.AnthropicAPIKey != "" {
anthropicAdapter := llm.NewAnthropicAdapter(cfg.AnthropicAPIKey, cfg.AnthropicDefaultModel)
providerRegistry.Register(anthropicAdapter)
}
// Initialize PII detector
piiDetector := llm.NewPIIDetectorWithPatterns(llm.AllPIIPatterns())
// Initialize access gate
accessGate := llm.NewAccessGate(policyEngine, piiDetector, providerRegistry)
// Initialize audit components
trailBuilder := audit.NewTrailBuilder(auditStore)
exporter := audit.NewExporter(auditStore)
// Initialize handlers
rbacHandlers := handlers.NewRBACHandlers(rbacStore, rbacService, policyEngine)
llmHandlers := handlers.NewLLMHandlers(accessGate, providerRegistry, piiDetector, auditStore, trailBuilder)
auditHandlers := handlers.NewAuditHandlers(auditStore, exporter)
dsgvoHandlers := handlers.NewDSGVOHandlers(dsgvoStore)
uccaHandlers := handlers.NewUCCAHandlers(uccaStore, escalationStore, providerRegistry)
escalationHandlers := handlers.NewEscalationHandlers(escalationStore, uccaStore)
roadmapHandlers := handlers.NewRoadmapHandlers(roadmapStore)
workshopHandlers := handlers.NewWorkshopHandlers(workshopStore)
portfolioHandlers := handlers.NewPortfolioHandlers(portfolioStore)
draftingHandlers := handlers.NewDraftingHandlers(accessGate, providerRegistry, piiDetector, auditStore, trailBuilder)
academyHandlers := handlers.NewAcademyHandlers(academyStore)
whistleblowerHandlers := handlers.NewWhistleblowerHandlers(whistleblowerStore)
incidentHandlers := handlers.NewIncidentHandlers(incidentStore)
vendorHandlers := handlers.NewVendorHandlers(vendorStore)
reportingHandlers := handlers.NewReportingHandlers(reportingStore)
ssoHandlers := handlers.NewSSOHandlers(ssoStore, cfg.JWTSecret)
multitenantHandlers := handlers.NewMultiTenantHandlers(multitenantStore, rbacStore)
industryHandlers := handlers.NewIndustryHandlers()
dsbHandlers := handlers.NewDSBHandlers(dsbStore)
// Initialize middleware
rbacMiddleware := rbac.NewMiddleware(rbacService, policyEngine)
// Create Gin router
router := gin.Default()
// CORS configuration
router.Use(cors.New(cors.Config{
AllowOrigins: cfg.AllowedOrigins,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-User-ID", "X-Tenant-ID", "X-Namespace-ID", "X-Tenant-Slug"},
ExposeHeaders: []string{"Content-Length", "Content-Disposition"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
// Health check
router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"timestamp": time.Now().UTC().Format(time.RFC3339),
})
})
// API v1 routes
v1 := router.Group("/sdk/v1")
{
// Public routes (no auth required)
v1.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
// Apply user context extraction middleware
v1.Use(rbacMiddleware.ExtractUserContext())
// Tenant routes
tenants := v1.Group("/tenants")
{
tenants.GET("", rbacHandlers.ListTenants)
tenants.GET("/:id", rbacHandlers.GetTenant)
tenants.POST("", rbacHandlers.CreateTenant)
tenants.PUT("/:id", rbacHandlers.UpdateTenant)
// Tenant namespaces (use :id to avoid route conflict)
tenants.GET("/:id/namespaces", rbacHandlers.ListNamespaces)
tenants.POST("/:id/namespaces", rbacHandlers.CreateNamespace)
}
// Namespace routes
namespaces := v1.Group("/namespaces")
{
namespaces.GET("/:id", rbacHandlers.GetNamespace)
}
// Role routes
roles := v1.Group("/roles")
{
roles.GET("", rbacHandlers.ListRoles)
roles.GET("/system", rbacHandlers.ListSystemRoles)
roles.GET("/:id", rbacHandlers.GetRole)
roles.POST("", rbacHandlers.CreateRole)
}
// User role routes
userRoles := v1.Group("/user-roles")
{
userRoles.POST("", rbacHandlers.AssignRole)
userRoles.DELETE("/:userId/:roleId", rbacHandlers.RevokeRole)
userRoles.GET("/:userId", rbacHandlers.GetUserRoles)
}
// Permission routes
permissions := v1.Group("/permissions")
{
permissions.GET("/effective", rbacHandlers.GetEffectivePermissions)
permissions.GET("/context", rbacHandlers.GetUserContext)
permissions.GET("/check", rbacHandlers.CheckPermission)
}
// LLM Policy routes
policies := v1.Group("/llm/policies")
{
policies.GET("", rbacHandlers.ListLLMPolicies)
policies.GET("/:id", rbacHandlers.GetLLMPolicy)
policies.POST("", rbacHandlers.CreateLLMPolicy)
policies.PUT("/:id", rbacHandlers.UpdateLLMPolicy)
policies.DELETE("/:id", rbacHandlers.DeleteLLMPolicy)
}
// LLM routes (require LLM permission)
llmRoutes := v1.Group("/llm")
llmRoutes.Use(rbacMiddleware.RequireLLMAccess())
{
llmRoutes.POST("/chat", llmHandlers.Chat)
llmRoutes.POST("/complete", llmHandlers.Complete)
llmRoutes.GET("/models", llmHandlers.ListModels)
llmRoutes.GET("/providers/status", llmHandlers.GetProviderStatus)
llmRoutes.POST("/analyze", llmHandlers.AnalyzeText)
llmRoutes.POST("/redact", llmHandlers.RedactText)
}
// Audit routes (require audit permission)
auditRoutes := v1.Group("/audit")
auditRoutes.Use(rbacMiddleware.RequireAnyPermission(rbac.PermissionAuditAll, rbac.PermissionAuditRead, rbac.PermissionAuditLogRead))
{
auditRoutes.GET("/llm", auditHandlers.QueryLLMAudit)
auditRoutes.GET("/general", auditHandlers.QueryGeneralAudit)
auditRoutes.GET("/llm-operations", auditHandlers.QueryLLMAudit) // Alias
auditRoutes.GET("/trail", auditHandlers.QueryGeneralAudit) // Alias
auditRoutes.GET("/usage", auditHandlers.GetUsageStats)
auditRoutes.GET("/compliance-report", auditHandlers.GetComplianceReport)
// Export routes
auditRoutes.GET("/export/llm", auditHandlers.ExportLLMAudit)
auditRoutes.GET("/export/general", auditHandlers.ExportGeneralAudit)
auditRoutes.GET("/export/compliance", auditHandlers.ExportComplianceReport)
}
// DSGVO routes (Art. 30, 32, 35, 15-22 DSGVO)
dsgvoRoutes := v1.Group("/dsgvo")
{
// Statistics
dsgvoRoutes.GET("/stats", dsgvoHandlers.GetStats)
// VVT - Verarbeitungsverzeichnis (Art. 30)
vvt := dsgvoRoutes.Group("/processing-activities")
{
vvt.GET("", dsgvoHandlers.ListProcessingActivities)
vvt.GET("/:id", dsgvoHandlers.GetProcessingActivity)
vvt.POST("", dsgvoHandlers.CreateProcessingActivity)
vvt.PUT("/:id", dsgvoHandlers.UpdateProcessingActivity)
vvt.DELETE("/:id", dsgvoHandlers.DeleteProcessingActivity)
}
// TOM - Technische und Organisatorische Maßnahmen (Art. 32)
tom := dsgvoRoutes.Group("/tom")
{
tom.GET("", dsgvoHandlers.ListTOMs)
tom.GET("/:id", dsgvoHandlers.GetTOM)
tom.POST("", dsgvoHandlers.CreateTOM)
}
// DSR - Data Subject Requests / Betroffenenrechte (Art. 15-22)
dsr := dsgvoRoutes.Group("/dsr")
{
dsr.GET("", dsgvoHandlers.ListDSRs)
dsr.GET("/:id", dsgvoHandlers.GetDSR)
dsr.POST("", dsgvoHandlers.CreateDSR)
dsr.PUT("/:id", dsgvoHandlers.UpdateDSR)
}
// Retention Policies - Löschfristen (Art. 17)
retention := dsgvoRoutes.Group("/retention-policies")
{
retention.GET("", dsgvoHandlers.ListRetentionPolicies)
retention.POST("", dsgvoHandlers.CreateRetentionPolicy)
}
// DSFA - Datenschutz-Folgenabschätzung (Art. 35)
dsfa := dsgvoRoutes.Group("/dsfa")
{
dsfa.GET("", dsgvoHandlers.ListDSFAs)
dsfa.GET("/:id", dsgvoHandlers.GetDSFA)
dsfa.POST("", dsgvoHandlers.CreateDSFA)
dsfa.PUT("/:id", dsgvoHandlers.UpdateDSFA)
dsfa.DELETE("/:id", dsgvoHandlers.DeleteDSFA)
dsfa.GET("/:id/export", dsgvoHandlers.ExportDSFA)
}
// Export routes
exports := dsgvoRoutes.Group("/export")
{
exports.GET("/vvt", dsgvoHandlers.ExportVVT)
exports.GET("/tom", dsgvoHandlers.ExportTOM)
exports.GET("/dsr", dsgvoHandlers.ExportDSR)
exports.GET("/retention", dsgvoHandlers.ExportRetentionPolicies)
}
}
// UCCA routes - Use-Case Compliance & Feasibility Advisor
uccaRoutes := v1.Group("/ucca")
{
// Main assessment endpoint
uccaRoutes.POST("/assess", uccaHandlers.Assess)
// Assessment management
uccaRoutes.GET("/assessments", uccaHandlers.ListAssessments)
uccaRoutes.GET("/assessments/:id", uccaHandlers.GetAssessment)
uccaRoutes.DELETE("/assessments/:id", uccaHandlers.DeleteAssessment)
// LLM explanation
uccaRoutes.POST("/assessments/:id/explain", uccaHandlers.Explain)
// Catalogs (patterns, examples, rules, controls, problem-solutions)
uccaRoutes.GET("/patterns", uccaHandlers.ListPatterns)
uccaRoutes.GET("/examples", uccaHandlers.ListExamples)
uccaRoutes.GET("/rules", uccaHandlers.ListRules)
uccaRoutes.GET("/controls", uccaHandlers.ListControls)
uccaRoutes.GET("/problem-solutions", uccaHandlers.ListProblemSolutions)
// Export
uccaRoutes.GET("/export/:id", uccaHandlers.Export)
// Statistics
uccaRoutes.GET("/stats", uccaHandlers.GetStats)
// Wizard routes - Legal Assistant integrated
uccaRoutes.GET("/wizard/schema", uccaHandlers.GetWizardSchema)
uccaRoutes.POST("/wizard/ask", uccaHandlers.AskWizardQuestion)
// Escalation management (E0-E3 workflow)
uccaRoutes.GET("/escalations", escalationHandlers.ListEscalations)
uccaRoutes.GET("/escalations/stats", escalationHandlers.GetEscalationStats)
uccaRoutes.GET("/escalations/:id", escalationHandlers.GetEscalation)
uccaRoutes.POST("/escalations", escalationHandlers.CreateEscalation)
uccaRoutes.POST("/escalations/:id/assign", escalationHandlers.AssignEscalation)
uccaRoutes.POST("/escalations/:id/review", escalationHandlers.StartReview)
uccaRoutes.POST("/escalations/:id/decide", escalationHandlers.DecideEscalation)
// DSB Pool management
uccaRoutes.GET("/dsb-pool", escalationHandlers.ListDSBPool)
uccaRoutes.POST("/dsb-pool", escalationHandlers.AddDSBPoolMember)
}
// Roadmap routes - Compliance Implementation Roadmaps
roadmapRoutes := v1.Group("/roadmaps")
{
// Roadmap CRUD
roadmapRoutes.POST("", roadmapHandlers.CreateRoadmap)
roadmapRoutes.GET("", roadmapHandlers.ListRoadmaps)
roadmapRoutes.GET("/:id", roadmapHandlers.GetRoadmap)
roadmapRoutes.PUT("/:id", roadmapHandlers.UpdateRoadmap)
roadmapRoutes.DELETE("/:id", roadmapHandlers.DeleteRoadmap)
roadmapRoutes.GET("/:id/stats", roadmapHandlers.GetRoadmapStats)
// Roadmap items
roadmapRoutes.POST("/:id/items", roadmapHandlers.CreateItem)
roadmapRoutes.GET("/:id/items", roadmapHandlers.ListItems)
// Import workflow
roadmapRoutes.POST("/import/upload", roadmapHandlers.UploadImport)
roadmapRoutes.GET("/import/:jobId", roadmapHandlers.GetImportJob)
roadmapRoutes.POST("/import/:jobId/confirm", roadmapHandlers.ConfirmImport)
}
// Roadmap item routes (separate group for item-level operations)
roadmapItemRoutes := v1.Group("/roadmap-items")
{
roadmapItemRoutes.GET("/:id", roadmapHandlers.GetItem)
roadmapItemRoutes.PUT("/:id", roadmapHandlers.UpdateItem)
roadmapItemRoutes.PATCH("/:id/status", roadmapHandlers.UpdateItemStatus)
roadmapItemRoutes.DELETE("/:id", roadmapHandlers.DeleteItem)
}
// Workshop routes - Collaborative Compliance Workshops
workshopRoutes := v1.Group("/workshops")
{
// Session CRUD
workshopRoutes.POST("", workshopHandlers.CreateSession)
workshopRoutes.GET("", workshopHandlers.ListSessions)
workshopRoutes.GET("/:id", workshopHandlers.GetSession)
workshopRoutes.PUT("/:id", workshopHandlers.UpdateSession)
workshopRoutes.DELETE("/:id", workshopHandlers.DeleteSession)
// Session lifecycle
workshopRoutes.POST("/:id/start", workshopHandlers.StartSession)
workshopRoutes.POST("/:id/pause", workshopHandlers.PauseSession)
workshopRoutes.POST("/:id/complete", workshopHandlers.CompleteSession)
// Participants
workshopRoutes.GET("/:id/participants", workshopHandlers.ListParticipants)
workshopRoutes.PUT("/:id/participants/:participantId", workshopHandlers.UpdateParticipant)
workshopRoutes.DELETE("/:id/participants/:participantId", workshopHandlers.RemoveParticipant)
// Responses
workshopRoutes.POST("/:id/responses", workshopHandlers.SubmitResponse)
workshopRoutes.GET("/:id/responses", workshopHandlers.GetResponses)
// Comments
workshopRoutes.POST("/:id/comments", workshopHandlers.AddComment)
workshopRoutes.GET("/:id/comments", workshopHandlers.GetComments)
// Wizard navigation
workshopRoutes.POST("/:id/advance", workshopHandlers.AdvanceStep)
workshopRoutes.POST("/:id/goto", workshopHandlers.GoToStep)
// Statistics & Summary
workshopRoutes.GET("/:id/stats", workshopHandlers.GetSessionStats)
workshopRoutes.GET("/:id/summary", workshopHandlers.GetSessionSummary)
// Export
workshopRoutes.GET("/:id/export", workshopHandlers.ExportSession)
// Join by code (public endpoint for joining)
workshopRoutes.POST("/join/:code", workshopHandlers.JoinSession)
}
// Portfolio routes - AI Use Case Portfolio Management
portfolioRoutes := v1.Group("/portfolios")
{
// Portfolio CRUD
portfolioRoutes.POST("", portfolioHandlers.CreatePortfolio)
portfolioRoutes.GET("", portfolioHandlers.ListPortfolios)
portfolioRoutes.GET("/:id", portfolioHandlers.GetPortfolio)
portfolioRoutes.PUT("/:id", portfolioHandlers.UpdatePortfolio)
portfolioRoutes.DELETE("/:id", portfolioHandlers.DeletePortfolio)
// Portfolio items
portfolioRoutes.POST("/:id/items", portfolioHandlers.AddItem)
portfolioRoutes.GET("/:id/items", portfolioHandlers.ListItems)
portfolioRoutes.POST("/:id/items/bulk", portfolioHandlers.BulkAddItems)
portfolioRoutes.DELETE("/:id/items/:itemId", portfolioHandlers.RemoveItem)
portfolioRoutes.PUT("/:id/items/order", portfolioHandlers.ReorderItems)
// Statistics & Activity
portfolioRoutes.GET("/:id/stats", portfolioHandlers.GetPortfolioStats)
portfolioRoutes.GET("/:id/activity", portfolioHandlers.GetPortfolioActivity)
portfolioRoutes.POST("/:id/recalculate", portfolioHandlers.RecalculateMetrics)
// Approval workflow
portfolioRoutes.POST("/:id/submit-review", portfolioHandlers.SubmitForReview)
portfolioRoutes.POST("/:id/approve", portfolioHandlers.ApprovePortfolio)
// Merge & Compare
portfolioRoutes.POST("/merge", portfolioHandlers.MergePortfolios)
portfolioRoutes.POST("/compare", portfolioHandlers.ComparePortfolios)
}
// Drafting Engine routes - Compliance Document Drafting & Validation
draftingRoutes := v1.Group("/drafting")
draftingRoutes.Use(rbacMiddleware.RequireLLMAccess())
{
draftingRoutes.POST("/draft", draftingHandlers.DraftDocument)
draftingRoutes.POST("/validate", draftingHandlers.ValidateDocument)
draftingRoutes.GET("/history", draftingHandlers.GetDraftHistory)
}
// Academy routes - E-Learning / Compliance Training
academyRoutes := v1.Group("/academy")
{
// Courses
academyRoutes.POST("/courses", academyHandlers.CreateCourse)
academyRoutes.GET("/courses", academyHandlers.ListCourses)
academyRoutes.GET("/courses/:id", academyHandlers.GetCourse)
academyRoutes.PUT("/courses/:id", academyHandlers.UpdateCourse)
academyRoutes.DELETE("/courses/:id", academyHandlers.DeleteCourse)
// Enrollments
academyRoutes.POST("/enrollments", academyHandlers.CreateEnrollment)
academyRoutes.GET("/enrollments", academyHandlers.ListEnrollments)
academyRoutes.PUT("/enrollments/:id/progress", academyHandlers.UpdateProgress)
academyRoutes.POST("/enrollments/:id/complete", academyHandlers.CompleteEnrollment)
// Certificates
academyRoutes.GET("/certificates/:id", academyHandlers.GetCertificate)
academyRoutes.GET("/certificates/:id/pdf", academyHandlers.DownloadCertificatePDF)
academyRoutes.POST("/enrollments/:id/certificate", academyHandlers.GenerateCertificate)
// Quiz
academyRoutes.POST("/courses/:id/quiz", academyHandlers.SubmitQuiz)
// Statistics
academyRoutes.GET("/stats", academyHandlers.GetStatistics)
}
// Whistleblower routes - Hinweisgebersystem (HinSchG)
whistleblowerRoutes := v1.Group("/whistleblower")
{
// Public endpoints (anonymous reporting)
whistleblowerRoutes.POST("/reports/submit", whistleblowerHandlers.SubmitReport)
whistleblowerRoutes.GET("/reports/access/:accessKey", whistleblowerHandlers.GetReportByAccessKey)
whistleblowerRoutes.POST("/reports/access/:accessKey/messages", whistleblowerHandlers.SendPublicMessage)
// Admin endpoints
whistleblowerRoutes.GET("/reports", whistleblowerHandlers.ListReports)
whistleblowerRoutes.GET("/reports/:id", whistleblowerHandlers.GetReport)
whistleblowerRoutes.PUT("/reports/:id", whistleblowerHandlers.UpdateReport)
whistleblowerRoutes.DELETE("/reports/:id", whistleblowerHandlers.DeleteReport)
whistleblowerRoutes.POST("/reports/:id/acknowledge", whistleblowerHandlers.AcknowledgeReport)
whistleblowerRoutes.POST("/reports/:id/investigate", whistleblowerHandlers.StartInvestigation)
whistleblowerRoutes.POST("/reports/:id/measures", whistleblowerHandlers.AddMeasure)
whistleblowerRoutes.POST("/reports/:id/close", whistleblowerHandlers.CloseReport)
whistleblowerRoutes.POST("/reports/:id/messages", whistleblowerHandlers.SendAdminMessage)
whistleblowerRoutes.GET("/reports/:id/messages", whistleblowerHandlers.ListMessages)
// Statistics
whistleblowerRoutes.GET("/stats", whistleblowerHandlers.GetStatistics)
}
// Incidents routes - Datenpannen-Management (DSGVO Art. 33/34)
incidentRoutes := v1.Group("/incidents")
{
// Incident CRUD
incidentRoutes.POST("", incidentHandlers.CreateIncident)
incidentRoutes.GET("", incidentHandlers.ListIncidents)
incidentRoutes.GET("/:id", incidentHandlers.GetIncident)
incidentRoutes.PUT("/:id", incidentHandlers.UpdateIncident)
incidentRoutes.DELETE("/:id", incidentHandlers.DeleteIncident)
// Risk Assessment
incidentRoutes.POST("/:id/assess-risk", incidentHandlers.AssessRisk)
// Authority Notification (Art. 33)
incidentRoutes.POST("/:id/notify-authority", incidentHandlers.SubmitAuthorityNotification)
// Data Subject Notification (Art. 34)
incidentRoutes.POST("/:id/notify-subjects", incidentHandlers.NotifyDataSubjects)
// Measures
incidentRoutes.POST("/:id/measures", incidentHandlers.AddMeasure)
incidentRoutes.PUT("/:id/measures/:measureId", incidentHandlers.UpdateMeasure)
incidentRoutes.POST("/:id/measures/:measureId/complete", incidentHandlers.CompleteMeasure)
// Timeline
incidentRoutes.POST("/:id/timeline", incidentHandlers.AddTimelineEntry)
// Lifecycle
incidentRoutes.POST("/:id/close", incidentHandlers.CloseIncident)
// Statistics
incidentRoutes.GET("/stats", incidentHandlers.GetStatistics)
}
// Vendor Compliance routes - Vendor Management & AVV/DPA (DSGVO Art. 28)
vendorRoutes := v1.Group("/vendors")
{
// Vendor CRUD
vendorRoutes.POST("", vendorHandlers.CreateVendor)
vendorRoutes.GET("", vendorHandlers.ListVendors)
vendorRoutes.GET("/:id", vendorHandlers.GetVendor)
vendorRoutes.PUT("/:id", vendorHandlers.UpdateVendor)
vendorRoutes.DELETE("/:id", vendorHandlers.DeleteVendor)
// Contracts (AVV/DPA)
vendorRoutes.POST("/contracts", vendorHandlers.CreateContract)
vendorRoutes.GET("/contracts", vendorHandlers.ListContracts)
vendorRoutes.GET("/contracts/:id", vendorHandlers.GetContract)
vendorRoutes.PUT("/contracts/:id", vendorHandlers.UpdateContract)
vendorRoutes.DELETE("/contracts/:id", vendorHandlers.DeleteContract)
// Findings
vendorRoutes.POST("/findings", vendorHandlers.CreateFinding)
vendorRoutes.GET("/findings", vendorHandlers.ListFindings)
vendorRoutes.GET("/findings/:id", vendorHandlers.GetFinding)
vendorRoutes.PUT("/findings/:id", vendorHandlers.UpdateFinding)
vendorRoutes.POST("/findings/:id/resolve", vendorHandlers.ResolveFinding)
// Control Instances
vendorRoutes.POST("/controls", vendorHandlers.UpsertControlInstance)
vendorRoutes.GET("/controls", vendorHandlers.ListControlInstances)
// Templates
vendorRoutes.GET("/templates", vendorHandlers.ListTemplates)
vendorRoutes.GET("/templates/:templateId", vendorHandlers.GetTemplate)
vendorRoutes.POST("/templates", vendorHandlers.CreateTemplate)
vendorRoutes.POST("/templates/:templateId/apply", vendorHandlers.ApplyTemplate)
// Statistics
vendorRoutes.GET("/stats", vendorHandlers.GetStatistics)
}
// Reporting routes - Executive Compliance Reporting Dashboard
reportingRoutes := v1.Group("/reporting")
{
reportingRoutes.GET("/executive", reportingHandlers.GetExecutiveReport)
reportingRoutes.GET("/score", reportingHandlers.GetComplianceScore)
reportingRoutes.GET("/deadlines", reportingHandlers.GetUpcomingDeadlines)
reportingRoutes.GET("/risks", reportingHandlers.GetRiskOverview)
}
// SSO routes - Single Sign-On (SAML/OIDC)
ssoRoutes := v1.Group("/sso")
{
// Config CRUD
ssoRoutes.POST("/configs", ssoHandlers.CreateConfig)
ssoRoutes.GET("/configs", ssoHandlers.ListConfigs)
ssoRoutes.GET("/configs/:id", ssoHandlers.GetConfig)
ssoRoutes.PUT("/configs/:id", ssoHandlers.UpdateConfig)
ssoRoutes.DELETE("/configs/:id", ssoHandlers.DeleteConfig)
// SSO Users
ssoRoutes.GET("/users", ssoHandlers.ListUsers)
// OIDC Flow
ssoRoutes.GET("/oidc/login", ssoHandlers.InitiateOIDCLogin)
ssoRoutes.GET("/oidc/callback", ssoHandlers.HandleOIDCCallback)
}
// Multi-Tenant Administration routes
mtRoutes := v1.Group("/multi-tenant")
{
mtRoutes.GET("/overview", multitenantHandlers.GetOverview)
mtRoutes.POST("/tenants", multitenantHandlers.CreateTenant)
mtRoutes.GET("/tenants/:id", multitenantHandlers.GetTenantDetail)
mtRoutes.PUT("/tenants/:id", multitenantHandlers.UpdateTenant)
mtRoutes.GET("/tenants/:id/namespaces", multitenantHandlers.ListNamespaces)
mtRoutes.POST("/tenants/:id/namespaces", multitenantHandlers.CreateNamespace)
mtRoutes.POST("/switch", multitenantHandlers.SwitchTenant)
}
// Industry-Specific Templates routes (Phase 3.3)
industryRoutes := v1.Group("/industry/templates")
{
industryRoutes.GET("", industryHandlers.ListIndustries)
industryRoutes.GET("/:slug", industryHandlers.GetIndustry)
industryRoutes.GET("/:slug/vvt", industryHandlers.GetVVTTemplates)
industryRoutes.GET("/:slug/tom", industryHandlers.GetTOMRecommendations)
industryRoutes.GET("/:slug/risks", industryHandlers.GetRiskScenarios)
}
// DSB-as-a-Service Portal routes (Phase 3.4)
dsbRoutes := v1.Group("/dsb")
{
dsbRoutes.GET("/dashboard", dsbHandlers.GetDashboard)
dsbRoutes.POST("/assignments", dsbHandlers.CreateAssignment)
dsbRoutes.GET("/assignments", dsbHandlers.ListAssignments)
dsbRoutes.GET("/assignments/:id", dsbHandlers.GetAssignment)
dsbRoutes.PUT("/assignments/:id", dsbHandlers.UpdateAssignment)
dsbRoutes.POST("/assignments/:id/hours", dsbHandlers.CreateHourEntry)
dsbRoutes.GET("/assignments/:id/hours", dsbHandlers.ListHours)
dsbRoutes.GET("/assignments/:id/hours/summary", dsbHandlers.GetHoursSummary)
dsbRoutes.POST("/assignments/:id/tasks", dsbHandlers.CreateTask)
dsbRoutes.GET("/assignments/:id/tasks", dsbHandlers.ListTasks)
dsbRoutes.PUT("/tasks/:taskId", dsbHandlers.UpdateTask)
dsbRoutes.POST("/tasks/:taskId/complete", dsbHandlers.CompleteTask)
dsbRoutes.POST("/assignments/:id/communications", dsbHandlers.CreateCommunication)
dsbRoutes.GET("/assignments/:id/communications", dsbHandlers.ListCommunications)
}
}
// Create HTTP server
srv := &http.Server{
Addr: ":" + cfg.Port,
Handler: router,
ReadTimeout: 30 * time.Second,
WriteTimeout: 5 * time.Minute, // LLM requests can take longer
IdleTimeout: 60 * time.Second,
}
// Start server in goroutine
go func() {
log.Printf("AI Compliance SDK starting on port %s", cfg.Port)
log.Printf("Environment: %s", cfg.Environment)
log.Printf("Primary LLM Provider: %s", cfg.LLMProvider)
log.Printf("Fallback LLM Provider: %s", cfg.LLMFallbackProvider)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Server exited")
}