package handlers import ( "net/http" "github.com/breakpilot/edu-search-service/internal/orchestrator" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // OrchestratorHandler handles orchestrator-related HTTP requests type OrchestratorHandler struct { orchestrator *orchestrator.Orchestrator repo orchestrator.Repository } // NewOrchestratorHandler creates a new orchestrator handler func NewOrchestratorHandler(orch *orchestrator.Orchestrator, repo orchestrator.Repository) *OrchestratorHandler { return &OrchestratorHandler{ orchestrator: orch, repo: repo, } } // AddToQueueRequest represents a request to add a university to the crawl queue type AddToQueueRequest struct { UniversityID string `json:"university_id" binding:"required"` Priority int `json:"priority"` InitiatedBy string `json:"initiated_by"` } // GetStatus returns the current orchestrator status func (h *OrchestratorHandler) GetStatus(c *gin.Context) { status, err := h.orchestrator.Status(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get status", "details": err.Error()}) return } c.JSON(http.StatusOK, status) } // GetQueue returns all items in the crawl queue func (h *OrchestratorHandler) GetQueue(c *gin.Context) { items, err := h.orchestrator.GetQueue(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get queue", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "queue": items, "count": len(items), }) } // AddToQueue adds a university to the crawl queue func (h *OrchestratorHandler) AddToQueue(c *gin.Context) { var req AddToQueueRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) return } universityID, err := uuid.Parse(req.UniversityID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university_id format"}) return } // Default priority if not specified priority := req.Priority if priority == 0 { priority = 5 } initiatedBy := req.InitiatedBy if initiatedBy == "" { initiatedBy = "api" } item, err := h.orchestrator.AddUniversity(c.Request.Context(), universityID, priority, initiatedBy) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add to queue", "details": err.Error()}) return } c.JSON(http.StatusCreated, item) } // RemoveFromQueue removes a university from the crawl queue func (h *OrchestratorHandler) RemoveFromQueue(c *gin.Context) { idStr := c.Param("id") if idStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "University ID required"}) return } universityID, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university_id format"}) return } if err := h.orchestrator.RemoveUniversity(c.Request.Context(), universityID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove from queue", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"deleted": true, "university_id": idStr}) } // Start starts the orchestrator func (h *OrchestratorHandler) Start(c *gin.Context) { if err := h.orchestrator.Start(); err != nil { c.JSON(http.StatusConflict, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "status": "started", "message": "Orchestrator started successfully", }) } // Stop stops the orchestrator func (h *OrchestratorHandler) Stop(c *gin.Context) { if err := h.orchestrator.Stop(); err != nil { c.JSON(http.StatusConflict, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "status": "stopped", "message": "Orchestrator stopped successfully", }) } // PauseUniversity pauses crawling for a specific university func (h *OrchestratorHandler) PauseUniversity(c *gin.Context) { idStr := c.Param("id") if idStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "University ID required"}) return } universityID, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university_id format"}) return } if err := h.orchestrator.PauseUniversity(c.Request.Context(), universityID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to pause crawl", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "status": "paused", "university_id": idStr, }) } // ResumeUniversity resumes crawling for a paused university func (h *OrchestratorHandler) ResumeUniversity(c *gin.Context) { idStr := c.Param("id") if idStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "University ID required"}) return } universityID, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university_id format"}) return } if err := h.orchestrator.ResumeUniversity(c.Request.Context(), universityID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to resume crawl", "details": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "status": "resumed", "university_id": idStr, }) } // SetupOrchestratorRoutes configures orchestrator API routes func SetupOrchestratorRoutes(r *gin.RouterGroup, h *OrchestratorHandler) { crawl := r.Group("/crawl") { // Orchestrator control crawl.GET("/status", h.GetStatus) crawl.POST("/start", h.Start) crawl.POST("/stop", h.Stop) // Queue management crawl.GET("/queue", h.GetQueue) crawl.POST("/queue", h.AddToQueue) crawl.DELETE("/queue/:id", h.RemoveFromQueue) // Individual university control crawl.POST("/queue/:id/pause", h.PauseUniversity) crawl.POST("/queue/:id/resume", h.ResumeUniversity) } }