Spaces:
Sleeping
Sleeping
| 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) | |
| } | |
| } |