package server import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "github.com/yourusername/repo-diagram/backend/internal/analyzer" "github.com/yourusername/repo-diagram/backend/internal/diagram" "github.com/yourusername/repo-diagram/backend/pkg/models" "github.com/yourusername/repo-diagram/backend/pkg/utils" ) type Server struct { router *gin.Engine analyzer *analyzer.Service diagramSvc *diagram.Service port string } func NewServer() *Server { // Initialize services analyzerSvc := analyzer.NewService() diagramSvc := diagram.NewService() // Setup router router := gin.Default() // Setup CORS router.Use(func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() }) // Initialize server server := &Server{ router: router, analyzer: analyzerSvc, diagramSvc: diagramSvc, port: getEnv("PORT", "8080"), } // Setup routes server.setupRoutes() return server } func (s *Server) setupRoutes() { api := s.router.Group("/api") { api.POST("/analyze", s.handleAnalyze) api.GET("/diagram/:id", s.handleGetDiagram) api.GET("/status/:id", s.handleGetStatus) api.GET("/export/:id", s.handleExport) api.GET("/health", s.handleHealth) } } func (s *Server) handleAnalyze(c *gin.Context) { var req models.AnalysisRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()}) return } // Validate repository URL if !utils.IsValidGitHubURL(req.RepoURL) { c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "Invalid GitHub repository URL"}) return } // Start analysis in background go func() { ctx := context.Background() result, err := s.analyzer.AnalyzeRepository(ctx, req) if err != nil { log.Printf("Analysis failed: %v", err) return } // Generate diagram diagram, err := s.diagramSvc.Generate(ctx, result) if err != nil { log.Printf("Diagram generation failed: %v", err) return } // Store result (in production, use database) utils.SaveResult(result.ID, diagram) }() c.JSON(http.StatusAccepted, models.AnalysisResponse{ ID: utils.GenerateID(), Status: "processing", Message: "Analysis started", }) } func (s *Server) handleGetDiagram(c *gin.Context) { id := c.Param("id") diagram := utils.GetDiagram(id) if diagram == nil { c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "Diagram not found"}) return } c.JSON(http.StatusOK, diagram) } func (s *Server) handleGetStatus(c *gin.Context) { id := c.Param("id") status := utils.GetStatus(id) if status == nil { c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "Analysis not found"}) return } c.JSON(http.StatusOK, status) } func (s *Server) handleExport(c *gin.Context) { id := c.Param("id") format := c.DefaultQuery("format", "svg") diagram := utils.GetDiagram(id) if diagram == nil { c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "Diagram not found"}) return } switch format { case "svg": c.Data(http.StatusOK, "image/svg+xml", diagram.SVG) case "json": c.JSON(http.StatusOK, diagram) default: c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "Unsupported format"}) } } func (s *Server) handleHealth(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "healthy", "time": time.Now().Unix(), }) } func (s *Server) Start() error { return s.router.Run(":" + s.port) } func (s *Server) Shutdown(ctx context.Context) error { // Graceful shutdown logic here return nil } func getEnv(key, defaultValue string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultValue } func main() { server := NewServer() // Setup signal handling for graceful shutdown quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) go func() { <-quit log.Println("Shutting down server...") // Add cleanup logic here os.Exit(0) }() log.Printf("Server starting on port %s", server.port) if err := server.Start(); err != nil { log.Fatalf("Failed to start server: %v", err) } }