Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
161 lines
4.3 KiB
Go
161 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/api"
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/db"
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/llm"
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/rag"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
func main() {
|
|
// Load environment variables
|
|
if err := godotenv.Load(); err != nil {
|
|
log.Println("No .env file found, using environment variables")
|
|
}
|
|
|
|
// Get configuration from environment
|
|
port := getEnv("PORT", "8085")
|
|
dbURL := getEnv("DATABASE_URL", "postgres://localhost:5432/sdk_states?sslmode=disable")
|
|
qdrantURL := getEnv("QDRANT_URL", "http://localhost:6333")
|
|
anthropicKey := getEnv("ANTHROPIC_API_KEY", "")
|
|
|
|
// Initialize database connection
|
|
dbPool, err := db.NewPostgresPool(dbURL)
|
|
if err != nil {
|
|
log.Printf("Warning: Database connection failed: %v", err)
|
|
// Continue without database - use in-memory fallback
|
|
}
|
|
|
|
// Initialize RAG service
|
|
ragService, err := rag.NewService(qdrantURL)
|
|
if err != nil {
|
|
log.Printf("Warning: RAG service initialization failed: %v", err)
|
|
// Continue without RAG - will return empty results
|
|
}
|
|
|
|
// Initialize LLM service
|
|
llmService := llm.NewService(anthropicKey)
|
|
|
|
// Create Gin router
|
|
gin.SetMode(gin.ReleaseMode)
|
|
if os.Getenv("GIN_MODE") == "debug" {
|
|
gin.SetMode(gin.DebugMode)
|
|
}
|
|
|
|
router := gin.Default()
|
|
|
|
// CORS middleware
|
|
router.Use(corsMiddleware())
|
|
|
|
// Health check
|
|
router.GET("/health", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"status": "healthy",
|
|
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
|
"services": gin.H{
|
|
"database": dbPool != nil,
|
|
"rag": ragService != nil,
|
|
"llm": anthropicKey != "",
|
|
},
|
|
})
|
|
})
|
|
|
|
// API routes
|
|
v1 := router.Group("/sdk/v1")
|
|
{
|
|
// State Management
|
|
stateHandler := api.NewStateHandler(dbPool)
|
|
v1.GET("/state/:tenantId", stateHandler.GetState)
|
|
v1.POST("/state", stateHandler.SaveState)
|
|
v1.DELETE("/state/:tenantId", stateHandler.DeleteState)
|
|
|
|
// RAG Search
|
|
ragHandler := api.NewRAGHandler(ragService)
|
|
v1.GET("/rag/search", ragHandler.Search)
|
|
v1.GET("/rag/status", ragHandler.GetCorpusStatus)
|
|
v1.POST("/rag/index", ragHandler.IndexDocument)
|
|
|
|
// Document Generation
|
|
generateHandler := api.NewGenerateHandler(llmService, ragService)
|
|
v1.POST("/generate/dsfa", generateHandler.GenerateDSFA)
|
|
v1.POST("/generate/tom", generateHandler.GenerateTOM)
|
|
v1.POST("/generate/vvt", generateHandler.GenerateVVT)
|
|
v1.POST("/generate/gutachten", generateHandler.GenerateGutachten)
|
|
|
|
// Checkpoint Validation
|
|
checkpointHandler := api.NewCheckpointHandler()
|
|
v1.GET("/checkpoints", checkpointHandler.GetAll)
|
|
v1.POST("/checkpoints/validate", checkpointHandler.Validate)
|
|
}
|
|
|
|
// Create server
|
|
srv := &http.Server{
|
|
Addr: ":" + port,
|
|
Handler: router,
|
|
}
|
|
|
|
// Graceful shutdown
|
|
go func() {
|
|
log.Printf("SDK Backend starting on port %s", port)
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}()
|
|
|
|
// Wait for interrupt signal
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Println("Shutting down server...")
|
|
|
|
// Give outstanding requests 5 seconds to complete
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
log.Fatal("Server forced to shutdown:", err)
|
|
}
|
|
|
|
// Close database connection
|
|
if dbPool != nil {
|
|
dbPool.Close()
|
|
}
|
|
|
|
log.Println("Server exited")
|
|
}
|
|
|
|
func getEnv(key, defaultValue string) string {
|
|
if value := os.Getenv(key); value != "" {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func corsMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, If-Match, If-None-Match")
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
|
c.Writer.Header().Set("Access-Control-Expose-Headers", "ETag, Last-Modified")
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(204)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|