package handler import ( "net/http" "strconv" "github.com/gin-gonic/gin" "uzdb/internal/models" "uzdb/internal/services" "uzdb/internal/utils" ) // QueryHandler handles query-related HTTP requests type QueryHandler struct { connectionSvc *services.ConnectionService querySvc *services.QueryService } // NewQueryHandler creates a new query handler func NewQueryHandler( connectionSvc *services.ConnectionService, querySvc *services.QueryService, ) *QueryHandler { return &QueryHandler{ connectionSvc: connectionSvc, querySvc: querySvc, } } // ExecuteQuery handles POST /api/query func (h *QueryHandler) ExecuteQuery(c *gin.Context) { ctx := c.Request.Context() var req struct { ConnectionID string `json:"connection_id" binding:"required"` SQL string `json:"sql" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Invalid request body") return } result, err := h.connectionSvc.ExecuteQuery(ctx, req.ConnectionID, req.SQL) if err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, err, "Query execution failed") return } utils.SuccessResponse(c, gin.H{ "result": result, }) } // GetTables handles GET /api/connections/:id/tables func (h *QueryHandler) GetTables(c *gin.Context) { ctx := c.Request.Context() connectionID := c.Param("id") if connectionID == "" { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Connection ID is required") return } tables, err := h.connectionSvc.GetTables(ctx, connectionID) if err != nil { if err == models.ErrNotFound { utils.ErrorResponse(c, http.StatusNotFound, err, "Connection not found") return } utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to get tables") return } utils.SuccessResponse(c, gin.H{ "tables": tables, }) } // GetTableData handles GET /api/connections/:id/tables/:name/data func (h *QueryHandler) GetTableData(c *gin.Context) { ctx := c.Request.Context() connectionID := c.Param("id") tableName := c.Param("name") if connectionID == "" || tableName == "" { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Connection ID and table name are required") return } // Parse limit and offset limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100")) offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0")) result, err := h.connectionSvc.GetTableData(ctx, connectionID, tableName, limit, offset) if err != nil { if err == models.ErrNotFound { utils.ErrorResponse(c, http.StatusNotFound, err, "Connection not found") return } utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to get table data") return } utils.SuccessResponse(c, gin.H{ "result": result, "table": tableName, "limit": limit, "offset": offset, }) } // GetTableStructure handles GET /api/connections/:id/tables/:name/structure func (h *QueryHandler) GetTableStructure(c *gin.Context) { ctx := c.Request.Context() connectionID := c.Param("id") tableName := c.Param("name") if connectionID == "" || tableName == "" { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Connection ID and table name are required") return } structure, err := h.connectionSvc.GetTableStructure(ctx, connectionID, tableName) if err != nil { if err == models.ErrNotFound { utils.ErrorResponse(c, http.StatusNotFound, err, "Connection not found") return } utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to get table structure") return } utils.SuccessResponse(c, gin.H{ "structure": structure, }) } // GetQueryHistory handles GET /api/history func (h *QueryHandler) GetQueryHistory(c *gin.Context) { ctx := c.Request.Context() connectionID := c.Query("connection_id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) history, total, err := h.querySvc.GetQueryHistory(ctx, connectionID, page, pageSize) if err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to get query history") return } totalPages := int(total) / pageSize if int(total)%pageSize > 0 { totalPages++ } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "history": history, "total": total, "page": page, "page_size": pageSize, "total_pages": totalPages, }, }) } // GetSavedQueries handles GET /api/saved-queries func (h *QueryHandler) GetSavedQueries(c *gin.Context) { ctx := c.Request.Context() connectionID := c.Query("connection_id") queries, err := h.querySvc.GetSavedQueries(ctx, connectionID) if err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to get saved queries") return } utils.SuccessResponse(c, gin.H{ "queries": queries, }) } // CreateSavedQuery handles POST /api/saved-queries func (h *QueryHandler) CreateSavedQuery(c *gin.Context) { ctx := c.Request.Context() var req models.CreateSavedQueryRequest if err := c.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Invalid request body") return } query, err := h.querySvc.CreateSavedQuery(ctx, &req) if err != nil { utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to save query") return } utils.CreatedResponse(c, gin.H{ "query": query, }) } // UpdateSavedQuery handles PUT /api/saved-queries/:id func (h *QueryHandler) UpdateSavedQuery(c *gin.Context) { ctx := c.Request.Context() idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Invalid query ID") return } var req models.UpdateSavedQueryRequest if err := c.ShouldBindJSON(&req); err != nil { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Invalid request body") return } query, err := h.querySvc.UpdateSavedQuery(ctx, uint(id), &req) if err != nil { if err == models.ErrNotFound { utils.ErrorResponse(c, http.StatusNotFound, err, "Saved query not found") return } utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to update saved query") return } utils.SuccessResponse(c, gin.H{ "query": query, }) } // DeleteSavedQuery handles DELETE /api/saved-queries/:id func (h *QueryHandler) DeleteSavedQuery(c *gin.Context) { ctx := c.Request.Context() idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { utils.ErrorResponse(c, http.StatusBadRequest, models.ErrValidationFailed, "Invalid query ID") return } if err := h.querySvc.DeleteSavedQuery(ctx, uint(id)); err != nil { if err == models.ErrNotFound { utils.ErrorResponse(c, http.StatusNotFound, err, "Saved query not found") return } utils.ErrorResponse(c, http.StatusInternalServerError, err, "Failed to delete saved query") return } utils.SuccessResponse(c, gin.H{ "message": "Saved query deleted successfully", }) } // RegisterRoutes registers query routes func (h *QueryHandler) RegisterRoutes(router *gin.RouterGroup) { // Query execution router.POST("/query", h.ExecuteQuery) // Table operations router.GET("/connections/:id/tables", h.GetTables) router.GET("/connections/:id/tables/:name/data", h.GetTableData) router.GET("/connections/:id/tables/:name/structure", h.GetTableStructure) // Query history router.GET("/history", h.GetQueryHistory) // Saved queries savedQueries := router.Group("/saved-queries") { savedQueries.GET("", h.GetSavedQueries) savedQueries.POST("", h.CreateSavedQuery) savedQueries.PUT("/:id", h.UpdateSavedQuery) savedQueries.DELETE("/:id", h.DeleteSavedQuery) } }