refactor(go): split roadmap_handlers, academy/store, extract cmd/server/main to internal/app
roadmap_handlers.go (740 LOC) → roadmap_handlers.go, roadmap_item_handlers.go, roadmap_import_handlers.go academy/store.go (683 LOC) → store_courses.go, store_enrollments.go cmd/server/main.go (681 LOC) → internal/app/app.go (Run+buildRouter) + internal/app/routes.go (registerXxx helpers) main.go reduced to 7 LOC thin entrypoint calling app.Run() All files under 410 LOC. Zero behavior changes, same package declarations. go vet passes on all directly-split packages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,681 +1,7 @@
|
||||
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/llm"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/academy"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/roadmap"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/training"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/whistleblower"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
|
||||
"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"
|
||||
)
|
||||
import "github.com/breakpilot/ai-compliance-sdk/internal/app"
|
||||
|
||||
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)
|
||||
uccaStore := ucca.NewStore(pool)
|
||||
escalationStore := ucca.NewEscalationStore(pool)
|
||||
corpusVersionStore := ucca.NewCorpusVersionStore(pool)
|
||||
roadmapStore := roadmap.NewStore(pool)
|
||||
workshopStore := workshop.NewStore(pool)
|
||||
portfolioStore := portfolio.NewStore(pool)
|
||||
academyStore := academy.NewStore(pool)
|
||||
whistleblowerStore := whistleblower.NewStore(pool)
|
||||
iaceStore := iace.NewStore(pool)
|
||||
trainingStore := training.NewStore(pool)
|
||||
|
||||
// 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 TTS client and content generator for training
|
||||
ttsClient := training.NewTTSClient(cfg.TTSServiceURL)
|
||||
contentGenerator := training.NewContentGenerator(providerRegistry, piiDetector, trainingStore, ttsClient)
|
||||
|
||||
// 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)
|
||||
uccaHandlers := handlers.NewUCCAHandlers(uccaStore, escalationStore, providerRegistry)
|
||||
escalationHandlers := handlers.NewEscalationHandlers(escalationStore, uccaStore)
|
||||
roadmapHandlers := handlers.NewRoadmapHandlers(roadmapStore)
|
||||
workshopHandlers := handlers.NewWorkshopHandlers(workshopStore)
|
||||
portfolioHandlers := handlers.NewPortfolioHandlers(portfolioStore)
|
||||
academyHandlers := handlers.NewAcademyHandlers(academyStore, trainingStore)
|
||||
whistleblowerHandlers := handlers.NewWhistleblowerHandlers(whistleblowerStore)
|
||||
iaceHandler := handlers.NewIACEHandler(iaceStore, providerRegistry)
|
||||
blockGenerator := training.NewBlockGenerator(trainingStore, contentGenerator)
|
||||
trainingHandlers := handlers.NewTrainingHandlers(trainingStore, contentGenerator, blockGenerator, ttsClient)
|
||||
ragHandlers := handlers.NewRAGHandlers(corpusVersionStore)
|
||||
|
||||
// Initialize obligations framework (v2 with TOM mapping)
|
||||
obligationsStore := ucca.NewObligationsStore(pool)
|
||||
obligationsHandlers := handlers.NewObligationsHandlersWithStore(obligationsStore)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.PUT("/assessments/:id", uccaHandlers.UpdateAssessment)
|
||||
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)
|
||||
|
||||
// Escalation management (assessment-review workflows)
|
||||
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)
|
||||
|
||||
// Obligations framework (v2 with TOM mapping)
|
||||
obligationsHandlers.RegisterRoutes(uccaRoutes)
|
||||
}
|
||||
|
||||
// RAG routes - Legal Corpus Search & Versioning
|
||||
ragRoutes := v1.Group("/rag")
|
||||
{
|
||||
ragRoutes.POST("/search", ragHandlers.Search)
|
||||
ragRoutes.GET("/regulations", ragHandlers.ListRegulations)
|
||||
ragRoutes.GET("/corpus-status", ragHandlers.CorpusStatus)
|
||||
ragRoutes.GET("/corpus-versions/:collection", ragHandlers.CorpusVersionHistory)
|
||||
ragRoutes.GET("/scroll", ragHandlers.HandleScrollChunks)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.POST("/enrollments/:id/certificate", academyHandlers.GenerateCertificate)
|
||||
|
||||
// Quiz
|
||||
academyRoutes.POST("/courses/:id/quiz", academyHandlers.SubmitQuiz)
|
||||
|
||||
// Lessons
|
||||
academyRoutes.PUT("/lessons/:id", academyHandlers.UpdateLesson)
|
||||
academyRoutes.POST("/lessons/:id/quiz-test", academyHandlers.TestQuiz)
|
||||
|
||||
// Statistics
|
||||
academyRoutes.GET("/stats", academyHandlers.GetStatistics)
|
||||
|
||||
// Course Generation from Training Modules
|
||||
academyRoutes.POST("/courses/generate", academyHandlers.GenerateCourseFromTraining)
|
||||
academyRoutes.POST("/courses/generate-all", academyHandlers.GenerateAllCourses)
|
||||
|
||||
// Certificate PDF
|
||||
academyRoutes.GET("/certificates/:id/pdf", academyHandlers.DownloadCertificatePDF)
|
||||
}
|
||||
|
||||
// Training Engine routes - Compliance Training Content Pipeline
|
||||
trainingRoutes := v1.Group("/training")
|
||||
{
|
||||
// Module CRUD
|
||||
trainingRoutes.GET("/modules", trainingHandlers.ListModules)
|
||||
trainingRoutes.GET("/modules/:id", trainingHandlers.GetModule)
|
||||
trainingRoutes.POST("/modules", trainingHandlers.CreateModule)
|
||||
trainingRoutes.PUT("/modules/:id", trainingHandlers.UpdateModule)
|
||||
trainingRoutes.DELETE("/modules/:id", trainingHandlers.DeleteModule)
|
||||
|
||||
// Compliance Training Matrix (CTM)
|
||||
trainingRoutes.GET("/matrix", trainingHandlers.GetMatrix)
|
||||
trainingRoutes.GET("/matrix/:role", trainingHandlers.GetMatrixForRole)
|
||||
trainingRoutes.POST("/matrix", trainingHandlers.SetMatrixEntry)
|
||||
trainingRoutes.DELETE("/matrix/:role/:moduleId", trainingHandlers.DeleteMatrixEntry)
|
||||
|
||||
// Assignments
|
||||
trainingRoutes.POST("/assignments/compute", trainingHandlers.ComputeAssignments)
|
||||
trainingRoutes.GET("/assignments", trainingHandlers.ListAssignments)
|
||||
trainingRoutes.GET("/assignments/:id", trainingHandlers.GetAssignment)
|
||||
trainingRoutes.POST("/assignments/:id/start", trainingHandlers.StartAssignment)
|
||||
trainingRoutes.POST("/assignments/:id/progress", trainingHandlers.UpdateAssignmentProgress)
|
||||
trainingRoutes.POST("/assignments/:id/complete", trainingHandlers.CompleteAssignment)
|
||||
trainingRoutes.PUT("/assignments/:id", trainingHandlers.UpdateAssignment)
|
||||
|
||||
// Quiz
|
||||
trainingRoutes.GET("/quiz/:moduleId", trainingHandlers.GetQuiz)
|
||||
trainingRoutes.POST("/quiz/:moduleId/submit", trainingHandlers.SubmitQuiz)
|
||||
trainingRoutes.GET("/quiz/attempts/:assignmentId", trainingHandlers.GetQuizAttempts)
|
||||
|
||||
// Content Generation (LLM)
|
||||
trainingRoutes.POST("/content/generate", trainingHandlers.GenerateContent)
|
||||
trainingRoutes.POST("/content/generate-quiz", trainingHandlers.GenerateQuiz)
|
||||
trainingRoutes.POST("/content/generate-all", trainingHandlers.GenerateAllContent)
|
||||
trainingRoutes.POST("/content/generate-all-quiz", trainingHandlers.GenerateAllQuizzes)
|
||||
trainingRoutes.GET("/content/:moduleId", trainingHandlers.GetContent)
|
||||
// PublishContent expects c.Param("id") but route uses :moduleId for Gin compatibility
|
||||
trainingRoutes.POST("/content/:moduleId/publish", func(c *gin.Context) {
|
||||
c.Params = append(c.Params, gin.Param{Key: "id", Value: c.Param("moduleId")})
|
||||
trainingHandlers.PublishContent(c)
|
||||
})
|
||||
|
||||
// Media (Audio/Video via TTS Service)
|
||||
trainingRoutes.POST("/content/:moduleId/generate-audio", trainingHandlers.GenerateAudio)
|
||||
trainingRoutes.POST("/content/:moduleId/generate-video", trainingHandlers.GenerateVideo)
|
||||
trainingRoutes.POST("/content/:moduleId/preview-script", trainingHandlers.PreviewVideoScript)
|
||||
trainingRoutes.GET("/media/module/:moduleId", trainingHandlers.GetModuleMedia)
|
||||
// Media detail routes use :mediaId consistently
|
||||
trainingRoutes.GET("/media/:mediaId/url", func(c *gin.Context) {
|
||||
c.Params = append(c.Params, gin.Param{Key: "id", Value: c.Param("mediaId")})
|
||||
trainingHandlers.GetMediaURL(c)
|
||||
})
|
||||
trainingRoutes.POST("/media/:mediaId/publish", func(c *gin.Context) {
|
||||
c.Params = append(c.Params, gin.Param{Key: "id", Value: c.Param("mediaId")})
|
||||
trainingHandlers.PublishMedia(c)
|
||||
})
|
||||
trainingRoutes.GET("/media/:mediaId/stream", func(c *gin.Context) {
|
||||
c.Params = append(c.Params, gin.Param{Key: "id", Value: c.Param("mediaId")})
|
||||
trainingHandlers.StreamMedia(c)
|
||||
})
|
||||
|
||||
// Deadlines & Escalation
|
||||
trainingRoutes.GET("/deadlines", trainingHandlers.GetDeadlines)
|
||||
trainingRoutes.GET("/deadlines/overdue", trainingHandlers.GetOverdueDeadlines)
|
||||
trainingRoutes.POST("/escalation/check", trainingHandlers.CheckEscalation)
|
||||
|
||||
// Audit & Statistics
|
||||
trainingRoutes.GET("/audit-log", trainingHandlers.GetAuditLog)
|
||||
trainingRoutes.GET("/stats", trainingHandlers.GetStats)
|
||||
|
||||
// Certificates
|
||||
trainingRoutes.POST("/certificates/generate/:assignmentId", trainingHandlers.GenerateCertificate)
|
||||
trainingRoutes.GET("/certificates", trainingHandlers.ListCertificates)
|
||||
trainingRoutes.GET("/certificates/:id/verify", trainingHandlers.VerifyCertificate)
|
||||
trainingRoutes.GET("/certificates/:id/pdf", trainingHandlers.DownloadCertificatePDF)
|
||||
|
||||
// Training Blocks — Controls → Schulungsmodule Pipeline
|
||||
trainingRoutes.GET("/blocks", trainingHandlers.ListBlockConfigs)
|
||||
trainingRoutes.POST("/blocks", trainingHandlers.CreateBlockConfig)
|
||||
trainingRoutes.GET("/blocks/:id", trainingHandlers.GetBlockConfig)
|
||||
trainingRoutes.PUT("/blocks/:id", trainingHandlers.UpdateBlockConfig)
|
||||
trainingRoutes.DELETE("/blocks/:id", trainingHandlers.DeleteBlockConfig)
|
||||
trainingRoutes.POST("/blocks/:id/preview", trainingHandlers.PreviewBlock)
|
||||
trainingRoutes.POST("/blocks/:id/generate", trainingHandlers.GenerateBlock)
|
||||
trainingRoutes.GET("/blocks/:id/controls", trainingHandlers.GetBlockControls)
|
||||
|
||||
// Canonical Controls Browsing
|
||||
trainingRoutes.GET("/canonical/controls", trainingHandlers.ListCanonicalControls)
|
||||
trainingRoutes.GET("/canonical/meta", trainingHandlers.GetCanonicalMeta)
|
||||
|
||||
// Interactive Video (Narrator + Checkpoints)
|
||||
trainingRoutes.POST("/content/:moduleId/generate-interactive", trainingHandlers.GenerateInteractiveVideo)
|
||||
trainingRoutes.GET("/content/:moduleId/interactive-manifest", trainingHandlers.GetInteractiveManifest)
|
||||
trainingRoutes.POST("/checkpoints/:checkpointId/submit", trainingHandlers.SubmitCheckpointQuiz)
|
||||
trainingRoutes.GET("/checkpoints/progress/:assignmentId", trainingHandlers.GetCheckpointProgress)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// IACE routes - Industrial AI Compliance Engine (CE-Risikobeurteilung SW/FW/KI)
|
||||
iaceRoutes := v1.Group("/iace")
|
||||
{
|
||||
// Hazard Library (project-independent)
|
||||
iaceRoutes.GET("/hazard-library", iaceHandler.ListHazardLibrary)
|
||||
// Controls Library (project-independent)
|
||||
iaceRoutes.GET("/controls-library", iaceHandler.ListControlsLibrary)
|
||||
// ISO 12100 reference data (project-independent)
|
||||
iaceRoutes.GET("/lifecycle-phases", iaceHandler.ListLifecyclePhases)
|
||||
iaceRoutes.GET("/roles", iaceHandler.ListRoles)
|
||||
iaceRoutes.GET("/evidence-types", iaceHandler.ListEvidenceTypes)
|
||||
iaceRoutes.GET("/protective-measures-library", iaceHandler.ListProtectiveMeasures)
|
||||
// Component Library & Energy Sources (Hazard Matching Engine)
|
||||
iaceRoutes.GET("/component-library", iaceHandler.ListComponentLibrary)
|
||||
iaceRoutes.GET("/energy-sources", iaceHandler.ListEnergySources)
|
||||
// Tag Taxonomy
|
||||
iaceRoutes.GET("/tags", iaceHandler.ListTags)
|
||||
// Hazard Patterns
|
||||
iaceRoutes.GET("/hazard-patterns", iaceHandler.ListHazardPatterns)
|
||||
|
||||
// Project Management
|
||||
iaceRoutes.POST("/projects", iaceHandler.CreateProject)
|
||||
iaceRoutes.GET("/projects", iaceHandler.ListProjects)
|
||||
iaceRoutes.GET("/projects/:id", iaceHandler.GetProject)
|
||||
iaceRoutes.PUT("/projects/:id", iaceHandler.UpdateProject)
|
||||
iaceRoutes.DELETE("/projects/:id", iaceHandler.ArchiveProject)
|
||||
|
||||
// Onboarding
|
||||
iaceRoutes.POST("/projects/:id/init-from-profile", iaceHandler.InitFromProfile)
|
||||
iaceRoutes.POST("/projects/:id/completeness-check", iaceHandler.CheckCompleteness)
|
||||
|
||||
// Components
|
||||
iaceRoutes.POST("/projects/:id/components", iaceHandler.CreateComponent)
|
||||
iaceRoutes.GET("/projects/:id/components", iaceHandler.ListComponents)
|
||||
iaceRoutes.PUT("/projects/:id/components/:cid", iaceHandler.UpdateComponent)
|
||||
iaceRoutes.DELETE("/projects/:id/components/:cid", iaceHandler.DeleteComponent)
|
||||
|
||||
// Regulatory Classification
|
||||
iaceRoutes.POST("/projects/:id/classify", iaceHandler.Classify)
|
||||
iaceRoutes.GET("/projects/:id/classifications", iaceHandler.GetClassifications)
|
||||
iaceRoutes.POST("/projects/:id/classify/:regulation", iaceHandler.ClassifySingle)
|
||||
|
||||
// Hazards
|
||||
iaceRoutes.POST("/projects/:id/hazards", iaceHandler.CreateHazard)
|
||||
iaceRoutes.GET("/projects/:id/hazards", iaceHandler.ListHazards)
|
||||
iaceRoutes.PUT("/projects/:id/hazards/:hid", iaceHandler.UpdateHazard)
|
||||
iaceRoutes.POST("/projects/:id/hazards/suggest", iaceHandler.SuggestHazards)
|
||||
|
||||
// Pattern Matching Engine
|
||||
iaceRoutes.POST("/projects/:id/match-patterns", iaceHandler.MatchPatterns)
|
||||
iaceRoutes.POST("/projects/:id/apply-patterns", iaceHandler.ApplyPatternResults)
|
||||
iaceRoutes.POST("/projects/:id/hazards/:hid/suggest-measures", iaceHandler.SuggestMeasuresForHazard)
|
||||
iaceRoutes.POST("/projects/:id/mitigations/:mid/suggest-evidence", iaceHandler.SuggestEvidenceForMitigation)
|
||||
|
||||
// Risk Assessment
|
||||
iaceRoutes.POST("/projects/:id/hazards/:hid/assess", iaceHandler.AssessRisk)
|
||||
iaceRoutes.GET("/projects/:id/risk-summary", iaceHandler.GetRiskSummary)
|
||||
iaceRoutes.POST("/projects/:id/hazards/:hid/reassess", iaceHandler.ReassessRisk)
|
||||
|
||||
// Mitigations
|
||||
iaceRoutes.POST("/projects/:id/hazards/:hid/mitigations", iaceHandler.CreateMitigation)
|
||||
iaceRoutes.PUT("/mitigations/:mid", iaceHandler.UpdateMitigation)
|
||||
iaceRoutes.POST("/mitigations/:mid/verify", iaceHandler.VerifyMitigation)
|
||||
iaceRoutes.POST("/projects/:id/validate-mitigation-hierarchy", iaceHandler.ValidateMitigationHierarchy)
|
||||
|
||||
// Evidence
|
||||
iaceRoutes.POST("/projects/:id/evidence", iaceHandler.UploadEvidence)
|
||||
iaceRoutes.GET("/projects/:id/evidence", iaceHandler.ListEvidence)
|
||||
|
||||
// Verification Plans
|
||||
iaceRoutes.POST("/projects/:id/verification-plan", iaceHandler.CreateVerificationPlan)
|
||||
iaceRoutes.PUT("/verification-plan/:vid", iaceHandler.UpdateVerificationPlan)
|
||||
iaceRoutes.POST("/verification-plan/:vid/complete", iaceHandler.CompleteVerification)
|
||||
|
||||
// CE Technical File
|
||||
iaceRoutes.POST("/projects/:id/tech-file/generate", iaceHandler.GenerateTechFile)
|
||||
iaceRoutes.GET("/projects/:id/tech-file", iaceHandler.ListTechFileSections)
|
||||
iaceRoutes.PUT("/projects/:id/tech-file/:section", iaceHandler.UpdateTechFileSection)
|
||||
iaceRoutes.POST("/projects/:id/tech-file/:section/approve", iaceHandler.ApproveTechFileSection)
|
||||
iaceRoutes.POST("/projects/:id/tech-file/:section/generate", iaceHandler.GenerateSingleSection)
|
||||
iaceRoutes.GET("/projects/:id/tech-file/export", iaceHandler.ExportTechFile)
|
||||
|
||||
// Monitoring
|
||||
iaceRoutes.POST("/projects/:id/monitoring", iaceHandler.CreateMonitoringEvent)
|
||||
iaceRoutes.GET("/projects/:id/monitoring", iaceHandler.ListMonitoringEvents)
|
||||
iaceRoutes.PUT("/projects/:id/monitoring/:eid", iaceHandler.UpdateMonitoringEvent)
|
||||
|
||||
// Audit Trail
|
||||
iaceRoutes.GET("/projects/:id/audit-trail", iaceHandler.GetAuditTrail)
|
||||
|
||||
// RAG Library Search (Phase 6)
|
||||
iaceRoutes.POST("/library-search", iaceHandler.SearchLibrary)
|
||||
iaceRoutes.POST("/projects/:id/tech-file/:section/enrich", iaceHandler.EnrichTechFileSection)
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
app.Run()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user