From 6d0c0ced1b1fe5eb457338cd0fb389bdc970e558 Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 25 Apr 2026 03:28:51 +0300 Subject: [PATCH] feat: Add charging handler with credit management endpoints for IP-based operations - Add ChargingHandler struct with ChargingEngineClient dependency - Add NewChargingHandler constructor - Add CheckCredit endpoint to validate sufficient credit for requested bytes - Add GetBalance endpoint to retrieve current balance for an IP - Add AddCredit endpoint to add bytes to an IP's credit - Add DeductCredit endpoint to deduct bytes from an IP's credit - Add IP parameter validation and JSON request binding --- .../internal/handlers/charging_handler.go | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 apps/api-server/internal/handlers/charging_handler.go diff --git a/apps/api-server/internal/handlers/charging_handler.go b/apps/api-server/internal/handlers/charging_handler.go new file mode 100644 index 0000000..711779a --- /dev/null +++ b/apps/api-server/internal/handlers/charging_handler.go @@ -0,0 +1,112 @@ +package handlers + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/nutcas3/telecom-platform/apps/api-server/internal/services" +) + +// ChargingHandler handles charging engine integration +type ChargingHandler struct { + engineClient *services.ChargingEngineClient +} + +// NewChargingHandler creates a new charging handler +func NewChargingHandler(engineClient *services.ChargingEngineClient) *ChargingHandler { + return &ChargingHandler{ + engineClient: engineClient, + } +} + +// CheckCredit checks if an IP has sufficient credit +func (h *ChargingHandler) CheckCredit(c *gin.Context) { + ip := c.Param("ip") + if ip == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "IP address is required"}) + return + } + + var req struct { + BytesRequested uint64 `json:"bytes_requested" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := h.engineClient.CheckCredit(c.Request.Context(), ip, req.BytesRequested) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} + +// GetBalance gets the current balance for an IP +func (h *ChargingHandler) GetBalance(c *gin.Context) { + ip := c.Param("ip") + if ip == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "IP address is required"}) + return + } + + result, err := h.engineClient.GetBalance(c.Request.Context(), ip) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} + +// AddCredit adds credit to an IP +func (h *ChargingHandler) AddCredit(c *gin.Context) { + ip := c.Param("ip") + if ip == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "IP address is required"}) + return + } + + var req struct { + BytesToAdd uint64 `json:"bytes_to_add" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err := h.engineClient.AddCredit(c.Request.Context(), ip, req.BytesToAdd) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "success"}) +} + +// DeductCredit deducts credit from an IP +func (h *ChargingHandler) DeductCredit(c *gin.Context) { + ip := c.Param("ip") + if ip == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "IP address is required"}) + return + } + + var req struct { + BytesUsed uint64 `json:"bytes_used" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err := h.engineClient.DeductCredit(c.Request.Context(), ip, req.BytesUsed) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "success"}) +}