package handlers import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" "github.com/kms/api-key-service/internal/domain" "github.com/kms/api-key-service/internal/services" ) // TokenHandler handles token-related HTTP requests type TokenHandler struct { tokenService services.TokenService authService services.AuthenticationService logger *zap.Logger } // NewTokenHandler creates a new token handler func NewTokenHandler( tokenService services.TokenService, authService services.AuthenticationService, logger *zap.Logger, ) *TokenHandler { return &TokenHandler{ tokenService: tokenService, authService: authService, logger: logger, } } // Create handles POST /applications/:id/tokens func (h *TokenHandler) Create(c *gin.Context) { appID := c.Param("id") if appID == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Bad Request", "message": "Application ID is required", }) return } var req domain.CreateStaticTokenRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Warn("Invalid request body", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{ "error": "Bad Request", "message": "Invalid request body: " + err.Error(), }) return } // Set app ID from URL parameter req.AppID = appID // Get user ID from context userID, exists := c.Get("user_id") if !exists { h.logger.Error("User ID not found in context") c.JSON(http.StatusInternalServerError, gin.H{ "error": "Internal Server Error", "message": "Authentication context not found", }) return } token, err := h.tokenService.CreateStaticToken(c.Request.Context(), &req, userID.(string)) if err != nil { h.logger.Error("Failed to create token", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Internal Server Error", "message": "Failed to create token", }) return } h.logger.Info("Token created", zap.String("token_id", token.ID.String())) c.JSON(http.StatusCreated, token) } // ListByApp handles GET /applications/:id/tokens func (h *TokenHandler) ListByApp(c *gin.Context) { appID := c.Param("id") if appID == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Bad Request", "message": "Application ID is required", }) return } // Parse pagination parameters limit := 50 offset := 0 if l := c.Query("limit"); l != "" { if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 { limit = parsed } } if o := c.Query("offset"); o != "" { if parsed, err := strconv.Atoi(o); err == nil && parsed >= 0 { offset = parsed } } tokens, err := h.tokenService.ListByApp(c.Request.Context(), appID, limit, offset) if err != nil { h.logger.Error("Failed to list tokens", zap.Error(err), zap.String("app_id", appID)) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Internal Server Error", "message": "Failed to list tokens", }) return } c.JSON(http.StatusOK, gin.H{ "data": tokens, "limit": limit, "offset": offset, "count": len(tokens), }) } // Delete handles DELETE /tokens/:id func (h *TokenHandler) Delete(c *gin.Context) { tokenIDStr := c.Param("id") if tokenIDStr == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Bad Request", "message": "Token ID is required", }) return } tokenID, err := uuid.Parse(tokenIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Bad Request", "message": "Invalid token ID format", }) return } // Get user ID from context userID, exists := c.Get("user_id") if !exists { h.logger.Error("User ID not found in context") c.JSON(http.StatusInternalServerError, gin.H{ "error": "Internal Server Error", "message": "Authentication context not found", }) return } err = h.tokenService.Delete(c.Request.Context(), tokenID, userID.(string)) if err != nil { h.logger.Error("Failed to delete token", zap.Error(err), zap.String("token_id", tokenID.String())) c.JSON(http.StatusInternalServerError, gin.H{ "error": "Internal Server Error", "message": "Failed to delete token", }) return } h.logger.Info("Token deleted", zap.String("token_id", tokenID.String())) c.JSON(http.StatusNoContent, nil) }