- 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
74 lines
2 KiB
Go
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"})
|
|
}
|