Files
uzdb/internal/handler/query.go
loveuer 9874561410 refactor: Flatten directory structure
Move project files from uzdb/ subdirectory to root directory for cleaner project structure.

Changes:
- Move frontend/ to root
- Move internal/ to root
- Move build/ to root
- Move all config files (go.mod, wails.json, etc.) to root
- Remove redundant uzdb/ subdirectory nesting

Project structure is now:
├── frontend/        # React application
├── internal/        # Go backend
├── build/           # Wails build assets
├── doc/             # Design documentation
├── main.go          # Entry point
└── ...

🤖 Generated with Qoder
2026-04-04 07:14:00 -07:00

288 lines
7.7 KiB
Go

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)
}
}