package handlers import ( "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/llamalink/llamalink/internal/api/middleware" "github.com/llamalink/llamalink/internal/auth" ) type KeysHandler struct { authService *auth.Service } func NewKeysHandler(authService *auth.Service) *KeysHandler { return &KeysHandler{authService: authService} } type CreateKeyRequest struct { Name string `json:"name" binding:"required"` Scopes []string `json:"scopes"` TokensLimit *int `json:"tokens_limit"` WebhookURL *string `json:"webhook_url"` OwnerLabel *string `json:"owner_label"` } func (h *KeysHandler) ListKeys(c *gin.Context) { includeInactive := c.Query("include_inactive") == "true" keys, err := h.authService.List(includeInactive) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Don't expose key hash safeKeys := make([]gin.H, len(keys)) for i, k := range keys { safeKeys[i] = gin.H{ "id": k.ID, "name": k.Name, "key_prefix": k.KeyPrefix, "scopes": k.Scopes, "is_active": k.IsActive, "is_admin": k.IsAdmin, "owner_label": k.OwnerLabel, "created_at": k.CreatedAt, "last_used_at": k.LastUsedAt, } } c.JSON(http.StatusOK, safeKeys) } func (h *KeysHandler) CreateKey(c *gin.Context) { var req CreateKeyRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Scopes == nil { req.Scopes = []string{"chat", "models", "usage"} } apiKey, rawKey, err := h.authService.Create(auth.CreateKeyRequest{ Name: req.Name, Scopes: req.Scopes, TokensLimit: req.TokensLimit, WebhookURL: req.WebhookURL, OwnerLabel: req.OwnerLabel, }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{ "id": apiKey.ID, "name": apiKey.Name, "key": rawKey, "key_prefix": apiKey.KeyPrefix, "scopes": apiKey.Scopes, "is_admin": apiKey.IsAdmin, "created_at": apiKey.CreatedAt, }) } func (h *KeysHandler) RevokeKey(c *gin.Context) { idStr := c.Param("id") id, err := uuid.Parse(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid key id"}) return } // Can't revoke own key currentKey := middleware.GetAPIKey(c) if currentKey.ID == id { c.JSON(http.StatusBadRequest, gin.H{ "error": gin.H{ "code": "validation_error", "message": "Cannot revoke your own admin key", }, }) return } if err := h.authService.Revoke(id); err != nil { if err == auth.ErrKeyNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "key not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.Status(http.StatusNoContent) }