mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
Implement the HTTP handler for managing global IP/CIDR bans. This includes endpoints for creating, listing, retrieving, updating, and deleting IP ban rules, complete with Swagger documentation and error handling.
249 lines
7.2 KiB
Go
249 lines
7.2 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/ez-api/ez-api/internal/model"
|
|
"github.com/ez-api/ez-api/internal/service"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// IPBanHandler handles IP ban CRUD operations.
|
|
type IPBanHandler struct {
|
|
ipBanService *service.IPBanService
|
|
}
|
|
|
|
// NewIPBanHandler creates a new IPBanHandler.
|
|
func NewIPBanHandler(ipBanService *service.IPBanService) *IPBanHandler {
|
|
return &IPBanHandler{ipBanService: ipBanService}
|
|
}
|
|
|
|
// CreateIPBanRequest represents a request to create an IP ban.
|
|
type CreateIPBanRequest struct {
|
|
CIDR string `json:"cidr" binding:"required"`
|
|
Reason string `json:"reason,omitempty"`
|
|
ExpiresAt *int64 `json:"expires_at,omitempty"`
|
|
}
|
|
|
|
// UpdateIPBanRequest represents a request to update an IP ban.
|
|
type UpdateIPBanRequest struct {
|
|
Reason *string `json:"reason,omitempty"`
|
|
ExpiresAt *int64 `json:"expires_at,omitempty"`
|
|
Status *string `json:"status,omitempty"`
|
|
}
|
|
|
|
// IPBanView represents the API response for an IP ban.
|
|
type IPBanView struct {
|
|
ID uint `json:"id"`
|
|
CIDR string `json:"cidr"`
|
|
Status string `json:"status"`
|
|
Reason string `json:"reason,omitempty"`
|
|
ExpiresAt *int64 `json:"expires_at,omitempty"`
|
|
HitCount int64 `json:"hit_count"`
|
|
CreatedBy string `json:"created_by,omitempty"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
UpdatedAt int64 `json:"updated_at"`
|
|
}
|
|
|
|
func toIPBanView(ban *model.IPBan) IPBanView {
|
|
return IPBanView{
|
|
ID: ban.ID,
|
|
CIDR: ban.CIDR,
|
|
Status: ban.Status,
|
|
Reason: ban.Reason,
|
|
ExpiresAt: ban.ExpiresAt,
|
|
HitCount: ban.HitCount,
|
|
CreatedBy: ban.CreatedBy,
|
|
CreatedAt: ban.CreatedAt.Unix(),
|
|
UpdatedAt: ban.UpdatedAt.Unix(),
|
|
}
|
|
}
|
|
|
|
// Create godoc
|
|
// @Summary Create an IP ban
|
|
// @Description Create a new global IP/CIDR ban rule
|
|
// @Tags admin,ip-bans
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param ban body CreateIPBanRequest true "IP Ban Info"
|
|
// @Success 201 {object} IPBanView
|
|
// @Failure 400 {object} gin.H
|
|
// @Failure 409 {object} gin.H
|
|
// @Failure 500 {object} gin.H
|
|
// @Router /admin/ip-bans [post]
|
|
func (h *IPBanHandler) Create(c *gin.Context) {
|
|
var req CreateIPBanRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Get creator from context (set by admin auth middleware)
|
|
createdBy := c.GetString("admin_user")
|
|
if createdBy == "" {
|
|
createdBy = "admin"
|
|
}
|
|
|
|
ban, err := h.ipBanService.Create(c.Request.Context(), service.CreateIPBanRequest{
|
|
CIDR: req.CIDR,
|
|
Reason: req.Reason,
|
|
ExpiresAt: req.ExpiresAt,
|
|
CreatedBy: createdBy,
|
|
})
|
|
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, service.ErrInvalidCIDR):
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid CIDR format"})
|
|
case errors.Is(err, service.ErrDuplicateCIDR):
|
|
c.JSON(http.StatusConflict, gin.H{"error": "CIDR already exists"})
|
|
case errors.Is(err, service.ErrCIDROverlap):
|
|
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
|
|
default:
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create IP ban", "details": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, toIPBanView(ban))
|
|
}
|
|
|
|
// List godoc
|
|
// @Summary List IP bans
|
|
// @Description List all global IP/CIDR ban rules
|
|
// @Tags admin,ip-bans
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param status query string false "Filter by status (active, expired)"
|
|
// @Success 200 {array} IPBanView
|
|
// @Failure 500 {object} gin.H
|
|
// @Router /admin/ip-bans [get]
|
|
func (h *IPBanHandler) List(c *gin.Context) {
|
|
status := c.Query("status")
|
|
|
|
bans, err := h.ipBanService.List(c.Request.Context(), status)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list IP bans", "details": err.Error()})
|
|
return
|
|
}
|
|
|
|
views := make([]IPBanView, len(bans))
|
|
for i, ban := range bans {
|
|
views[i] = toIPBanView(&ban)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, views)
|
|
}
|
|
|
|
// Get godoc
|
|
// @Summary Get an IP ban
|
|
// @Description Get a single global IP/CIDR ban rule by ID
|
|
// @Tags admin,ip-bans
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param id path int true "IP Ban ID"
|
|
// @Success 200 {object} IPBanView
|
|
// @Failure 404 {object} gin.H
|
|
// @Failure 500 {object} gin.H
|
|
// @Router /admin/ip-bans/{id} [get]
|
|
func (h *IPBanHandler) Get(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
ban, err := h.ipBanService.Get(c.Request.Context(), uint(id))
|
|
if err != nil {
|
|
if errors.Is(err, service.ErrIPBanNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "IP ban not found"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get IP ban", "details": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, toIPBanView(ban))
|
|
}
|
|
|
|
// Update godoc
|
|
// @Summary Update an IP ban
|
|
// @Description Update a global IP/CIDR ban rule
|
|
// @Tags admin,ip-bans
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param id path int true "IP Ban ID"
|
|
// @Param ban body UpdateIPBanRequest true "IP Ban Update"
|
|
// @Success 200 {object} IPBanView
|
|
// @Failure 400 {object} gin.H
|
|
// @Failure 404 {object} gin.H
|
|
// @Failure 500 {object} gin.H
|
|
// @Router /admin/ip-bans/{id} [put]
|
|
func (h *IPBanHandler) Update(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
var req UpdateIPBanRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ban, err := h.ipBanService.Update(c.Request.Context(), uint(id), service.UpdateIPBanRequest{
|
|
Reason: req.Reason,
|
|
ExpiresAt: req.ExpiresAt,
|
|
Status: req.Status,
|
|
})
|
|
|
|
if err != nil {
|
|
if errors.Is(err, service.ErrIPBanNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "IP ban not found"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update IP ban", "details": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, toIPBanView(ban))
|
|
}
|
|
|
|
// Delete godoc
|
|
// @Summary Delete an IP ban
|
|
// @Description Delete a global IP/CIDR ban rule
|
|
// @Tags admin,ip-bans
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security AdminAuth
|
|
// @Param id path int true "IP Ban ID"
|
|
// @Success 204
|
|
// @Failure 404 {object} gin.H
|
|
// @Failure 500 {object} gin.H
|
|
// @Router /admin/ip-bans/{id} [delete]
|
|
func (h *IPBanHandler) Delete(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
if err := h.ipBanService.Delete(c.Request.Context(), uint(id)); err != nil {
|
|
if errors.Is(err, service.ErrIPBanNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "IP ban not found"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete IP ban", "details": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|