Files
gemini-banlancer/internal/domain/upstream/handler.go
2025-11-20 12:24:05 +08:00

168 lines
4.0 KiB
Go

// Filename: internal/domain/upstream/handler.go
package upstream
import (
"gemini-balancer/internal/errors"
"gemini-balancer/internal/models"
"gemini-balancer/internal/response"
"net/url"
"strconv"
"github.com/gin-gonic/gin"
)
type Handler struct {
service *Service
}
func NewHandler(service *Service) *Handler {
return &Handler{service: service}
}
// ------ DTOs and Validation ------
type CreateUpstreamRequest struct {
URL string `json:"url" binding:"required"`
Weight int `json:"weight" binding:"omitempty,gte=1,lte=1000"`
Status string `json:"status" binding:"omitempty,oneof=active inactive"`
Description string `json:"description"`
}
type UpdateUpstreamRequest struct {
URL *string `json:"url"`
Weight *int `json:"weight" binding:"omitempty,gte=1,lte=1000"`
Status *string `json:"status" binding:"omitempty,oneof=active inactive"`
Description *string `json:"description"`
}
func isValidURL(rawURL string) bool {
u, err := url.ParseRequestURI(rawURL)
if err != nil {
return false
}
return u.Scheme == "http" || u.Scheme == "https"
}
// --- Handler ---
func (h *Handler) CreateUpstream(c *gin.Context) {
var req CreateUpstreamRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
return
}
if !isValidURL(req.URL) {
response.Error(c, errors.NewAPIError(errors.ErrValidation, "Invalid URL format"))
return
}
upstream := models.UpstreamEndpoint{
URL: req.URL,
Weight: req.Weight,
Status: req.Status,
Description: req.Description,
}
if err := h.service.Create(&upstream); err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
response.Created(c, upstream)
}
func (h *Handler) ListUpstreams(c *gin.Context) {
upstreams, err := h.service.List()
if err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
response.Success(c, upstreams)
}
func (h *Handler) GetUpstream(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
return
}
upstream, err := h.service.GetByID(id)
if err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
response.Success(c, upstream)
}
func (h *Handler) UpdateUpstream(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
return
}
var req UpdateUpstreamRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
return
}
upstream, err := h.service.GetByID(id)
if err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
if req.URL != nil {
if !isValidURL(*req.URL) {
response.Error(c, errors.NewAPIError(errors.ErrValidation, "Invalid URL format"))
return
}
upstream.URL = *req.URL
}
if req.Weight != nil {
upstream.Weight = *req.Weight
}
if req.Status != nil {
upstream.Status = *req.Status
}
if req.Description != nil {
upstream.Description = *req.Description
}
if err := h.service.Update(upstream); err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
response.Success(c, upstream)
}
func (h *Handler) DeleteUpstream(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
return
}
rowsAffected, err := h.service.Delete(id)
if err != nil {
response.Error(c, errors.ParseDBError(err))
return
}
if rowsAffected == 0 {
response.Error(c, errors.ErrResourceNotFound)
return
}
response.NoContent(c)
}
// RegisterRoutes
func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) {
upstreamRoutes := rg.Group("/upstreams")
{
upstreamRoutes.POST("/", h.CreateUpstream)
upstreamRoutes.GET("/", h.ListUpstreams)
upstreamRoutes.GET("/:id", h.GetUpstream)
upstreamRoutes.PUT("/:id", h.UpdateUpstream)
upstreamRoutes.DELETE("/:id", h.DeleteUpstream)
}
}