package admin import ( "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/rose_cat707/luminary/internal/model" "github.com/rose_cat707/luminary/internal/pricing" ) func (h *Handler) ListRoutingRules(c *gin.Context) { ingressID := parseUint(c.Param("id")) c.JSON(http.StatusOK, gin.H{"data": h.cache.ListRoutingRules(ingressID)}) } func (h *Handler) CreateRoutingRule(c *gin.Context) { ingressID := parseUint(c.Param("id")) var req struct { ModelPattern string `json:"model_pattern"` ProviderID uint `json:"provider_id"` ProviderKeyID uint `json:"provider_key_id"` Priority int `json:"priority"` Enabled *bool `json:"enabled"` } if err := c.ShouldBindJSON(&req); err != nil || req.ModelPattern == "" || req.ProviderID == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } rule := model.RoutingRule{ IngressKeyID: ingressID, ModelPattern: req.ModelPattern, ProviderID: req.ProviderID, ProviderKeyID: req.ProviderKeyID, Priority: req.Priority, Enabled: true, } if req.Enabled != nil { rule.Enabled = *req.Enabled } if err := h.db.DB().Create(&rule).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } rules := h.cache.ListRoutingRules(ingressID) rules = append(rules, rule) h.cache.SetRoutingRules(ingressID, rules) c.JSON(http.StatusCreated, rule) } func (h *Handler) UpdateRoutingRule(c *gin.Context) { ruleID := parseUint(c.Param("ruleId")) var rule model.RoutingRule if err := h.db.DB().First(&rule, ruleID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } var req struct { ModelPattern string `json:"model_pattern"` ProviderID uint `json:"provider_id"` ProviderKeyID uint `json:"provider_key_id"` Priority int `json:"priority"` Enabled *bool `json:"enabled"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } if req.ModelPattern != "" { rule.ModelPattern = req.ModelPattern } if req.ProviderID > 0 { rule.ProviderID = req.ProviderID } if req.ProviderKeyID >= 0 { rule.ProviderKeyID = req.ProviderKeyID } rule.Priority = req.Priority if req.Enabled != nil { rule.Enabled = *req.Enabled } h.db.DB().Save(&rule) h.reloadRoutingRules(rule.IngressKeyID) c.JSON(http.StatusOK, rule) } func (h *Handler) DeleteRoutingRule(c *gin.Context) { ruleID := parseUint(c.Param("ruleId")) var rule model.RoutingRule if err := h.db.DB().First(&rule, ruleID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } h.db.DB().Delete(&rule) h.reloadRoutingRules(rule.IngressKeyID) c.JSON(http.StatusOK, gin.H{"ok": true}) } func (h *Handler) reloadRoutingRules(ingressID uint) { var rules []model.RoutingRule h.db.DB().Where("ingress_key_id = ?", ingressID).Find(&rules) h.cache.SetRoutingRules(ingressID, rules) } func (h *Handler) ListRequestLogs(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize <= 0 || pageSize > 50 { pageSize = 20 } q := h.db.DB().Model(&model.UsageRecord{}) if ingressID := c.Query("ingress_key_id"); ingressID != "" { q = q.Where("ingress_key_id = ?", ingressID) } if status := c.Query("status"); status != "" { q = q.Where("status = ?", status) } if endpoint := c.Query("endpoint"); endpoint != "" { q = q.Where("endpoint = ?", endpoint) } if clientIP := c.Query("client_ip"); clientIP != "" { q = q.Where("client_ip LIKE ?", "%"+clientIP+"%") } if modelName := c.Query("model"); modelName != "" { q = q.Where("model LIKE ?", "%"+modelName+"%") } var total int64 q.Count(&total) var logs []model.UsageRecord q.Order("created_at desc"). Offset((page - 1) * pageSize). Limit(pageSize). Find(&logs) type item struct { ID uint `json:"id"` IngressKeyID uint `json:"ingress_key_id"` IngressName string `json:"ingress_name"` ClientIP string `json:"client_ip"` Endpoint string `json:"endpoint"` Model string `json:"model"` LatencyMs int64 `json:"latency_ms"` Status string `json:"status"` Stream bool `json:"stream"` ErrorMsg string `json:"error_msg"` CreatedAt time.Time `json:"created_at"` } out := make([]item, 0, len(logs)) for _, log := range logs { name := "" if log.Endpoint == model.EndpointAdminLogin { name = "管理台" } else if k, ok := h.cache.GetIngressKey(log.IngressKeyID); ok { name = k.Name } out = append(out, item{ ID: log.ID, IngressKeyID: log.IngressKeyID, IngressName: name, ClientIP: log.ClientIP, Endpoint: log.Endpoint, Model: log.Model, LatencyMs: log.LatencyMs, Status: log.Status, Stream: log.Stream, ErrorMsg: log.ErrorMsg, CreatedAt: log.CreatedAt, }) } c.JSON(http.StatusOK, gin.H{ "data": out, "total": total, "page": page, "page_size": pageSize, "max_kept": model.MaxUsageLogRecords, }) } func (h *Handler) GetRequestLog(c *gin.Context) { id := parseUint(c.Param("id")) var log model.UsageRecord if err := h.db.DB().First(&log, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } name := "" if log.Endpoint == model.EndpointAdminLogin { name = "管理台" } else if k, ok := h.cache.GetIngressKey(log.IngressKeyID); ok { name = k.Name } c.JSON(http.StatusOK, gin.H{ "data": gin.H{ "id": log.ID, "ingress_key_id": log.IngressKeyID, "ingress_name": name, "client_ip": log.ClientIP, "endpoint": log.Endpoint, "model": log.Model, "latency_ms": log.LatencyMs, "status": log.Status, "stream": log.Stream, "error_msg": log.ErrorMsg, "request_body": log.RequestBody, "response_body": log.ResponseBody, "created_at": log.CreatedAt, }, }) } func (h *Handler) PricingCatalog(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"data": pricing.ListCatalog()}) }