package admin import ( "crypto/rand" "encoding/hex" "net/http" "time" "github.com/gin-gonic/gin" "github.com/rose_cat707/luminary/internal/auth" "github.com/rose_cat707/luminary/internal/model" "github.com/rose_cat707/luminary/internal/store/cache" ) func (h *Handler) ListIngressKeys(c *gin.Context) { keys := h.cache.ListIngressKeys() c.JSON(http.StatusOK, gin.H{"data": keys}) } func (h *Handler) CreateIngressKey(c *gin.Context) { var req struct { Name string `json:"name"` Protocol model.IngressKeyProtocol `json:"protocol"` BudgetLimit float64 `json:"budget_limit"` RateLimitRPM int `json:"rate_limit_rpm"` RateLimitTPM int `json:"rate_limit_tpm"` AllowedProviders []string `json:"allowed_providers"` AllowedModels []string `json:"allowed_models"` ExpiresAt *time.Time `json:"expires_at"` } if err := c.ShouldBindJSON(&req); err != nil || req.Name == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } protocol := req.Protocol if protocol == "" { protocol = model.IngressProtocolText } if !protocol.Valid() { c.JSON(http.StatusBadRequest, gin.H{"error": "protocol must be text or image"}) return } plainKey, err := generateIngressKey() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "key generation failed"}) return } hash := auth.HashIngressKey(plainKey) providersJSON := `["*"]` modelsJSON := `["*"]` if req.AllowedProviders != nil { if len(req.AllowedProviders) == 0 { providersJSON = `["*"]` } else { providersJSON = cache.ModelsToJSON(req.AllowedProviders) } } if req.AllowedModels != nil { if len(req.AllowedModels) == 0 { modelsJSON = `["*"]` } else { modelsJSON = cache.ModelsToJSON(req.AllowedModels) } } k := model.IngressKey{ Name: req.Name, KeyHash: hash, Prefix: plainKey[:12] + "...", Protocol: protocol, Enabled: true, BudgetLimit: req.BudgetLimit, RateLimitRPM: req.RateLimitRPM, RateLimitTPM: req.RateLimitTPM, AllowedProvidersJSON: providersJSON, AllowedModelsJSON: modelsJSON, ExpiresAt: req.ExpiresAt, } if err := h.db.DB().Create(&k).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.cache.SetIngressKey(k) c.JSON(http.StatusCreated, gin.H{"key": k, "plain_key": plainKey}) } func (h *Handler) GetIngressKey(c *gin.Context) { id := parseUint(c.Param("id")) k, ok := h.cache.GetIngressKey(id) if !ok { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } c.JSON(http.StatusOK, k) } func (h *Handler) UpdateIngressKey(c *gin.Context) { id := parseUint(c.Param("id")) k, ok := h.cache.GetIngressKey(id) if !ok { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } var req struct { Name string `json:"name"` Protocol *model.IngressKeyProtocol `json:"protocol"` Enabled *bool `json:"enabled"` BudgetLimit *float64 `json:"budget_limit"` RateLimitRPM *int `json:"rate_limit_rpm"` RateLimitTPM *int `json:"rate_limit_tpm"` AllowedProviders []string `json:"allowed_providers"` AllowedModels []string `json:"allowed_models"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } if req.Protocol != nil { if !req.Protocol.Valid() { c.JSON(http.StatusBadRequest, gin.H{"error": "protocol must be text or image"}) return } k.Protocol = *req.Protocol } if req.Name != "" { k.Name = req.Name } if req.Enabled != nil { k.Enabled = *req.Enabled } if req.BudgetLimit != nil { k.BudgetLimit = *req.BudgetLimit } if req.RateLimitRPM != nil { k.RateLimitRPM = *req.RateLimitRPM } if req.RateLimitTPM != nil { k.RateLimitTPM = *req.RateLimitTPM } if req.AllowedProviders != nil { if len(req.AllowedProviders) == 0 { k.AllowedProvidersJSON = `["*"]` } else { k.AllowedProvidersJSON = cache.ModelsToJSON(req.AllowedProviders) } } if req.AllowedModels != nil { if len(req.AllowedModels) == 0 { k.AllowedModelsJSON = `["*"]` } else { k.AllowedModelsJSON = cache.ModelsToJSON(req.AllowedModels) } } h.db.DB().Save(&k) h.cache.SetIngressKey(k) c.JSON(http.StatusOK, k) } func (h *Handler) DeleteIngressKey(c *gin.Context) { id := parseUint(c.Param("id")) h.db.DB().Where("ingress_key_id = ?", id).Delete(&model.RoutingRule{}) h.db.DB().Delete(&model.IngressKey{}, id) h.cache.DeleteIngressKey(id) h.cache.DeleteRoutingRulesForIngress(id) c.JSON(http.StatusOK, gin.H{"ok": true}) } func (h *Handler) IngressUsage(c *gin.Context) { id := parseUint(c.Param("id")) var records []model.UsageRecord h.db.DB().Where("ingress_key_id = ?", id).Order("created_at desc").Limit(50).Find(&records) k, _ := h.cache.GetIngressKey(id) c.JSON(http.StatusOK, gin.H{"key": k, "recent": records}) } func generateIngressKey() (string, error) { b := make([]byte, 24) if _, err := rand.Read(b); err != nil { return "", err } return "sk-lum-" + hex.EncodeToString(b), nil }