veza/veza-backend-api/internal/handlers/api_key_handler.go
senke 32348bebce feat(developer): add API keys backend (Lot C)
- Migration 082: api_keys table (user_id, name, prefix, hashed_key, scopes, last_used_at, expires_at)
- APIKey model, APIKeyService (Create, List, Delete, ValidateAPIKey)
- APIKeyHandler: GET/POST/DELETE /api/v1/developer/api-keys
- AuthMiddleware: X-API-Key and Bearer vza_* accepted as alternative to JWT
- CSRF: skip for API key auth (stateless)
- Key format: vza_ prefix, SHA-256 hashed storage
2026-02-20 00:18:36 +01:00

74 lines
2 KiB
Go

package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
// APIKeyHandler handles developer API key HTTP requests
type APIKeyHandler struct {
apiKeyService *services.APIKeyService
logger *zap.Logger
}
// NewAPIKeyHandler creates a new APIKeyHandler
func NewAPIKeyHandler(apiKeyService *services.APIKeyService, logger *zap.Logger) *APIKeyHandler {
return &APIKeyHandler{apiKeyService: apiKeyService, logger: logger}
}
// ListAPIKeys returns the user's API keys
func (h *APIKeyHandler) ListAPIKeys(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
keys, err := h.apiKeyService.List(c.Request.Context(), userID)
if err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to list API keys", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"keys": keys})
}
// CreateAPIKey creates a new API key
func (h *APIKeyHandler) CreateAPIKey(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
var req services.CreateAPIKeyRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid request body"))
return
}
resp, err := h.apiKeyService.Create(c.Request.Context(), userID, &req)
if err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to create API key", err))
return
}
RespondSuccess(c, http.StatusCreated, resp)
}
// DeleteAPIKey deletes an API key
func (h *APIKeyHandler) DeleteAPIKey(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
keyID, err := uuid.Parse(c.Param("id"))
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid key ID"))
return
}
if err := h.apiKeyService.Delete(c.Request.Context(), userID, keyID); err != nil {
RespondWithAppError(c, apperrors.NewNotFoundError("API key not found"))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "API key revoked"})
}