diff --git a/internal/api/handler.go b/internal/api/handler.go index 54bbfb9..e7fbff2 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -48,13 +48,29 @@ func (h *Handler) CreateProvider(c *gin.Context) { group = "default" } + status := strings.TrimSpace(req.Status) + if status == "" { + status = "active" + } + autoBan := true + if req.AutoBan != nil { + autoBan = *req.AutoBan + } + provider := model.Provider{ - Name: req.Name, - Type: req.Type, - BaseURL: req.BaseURL, - APIKey: req.APIKey, - Group: group, - Models: strings.Join(req.Models, ","), + Name: req.Name, + Type: req.Type, + BaseURL: req.BaseURL, + APIKey: req.APIKey, + Group: group, + Models: strings.Join(req.Models, ","), + Status: status, + AutoBan: autoBan, + BanReason: req.BanReason, + } + if !req.BanUntil.IsZero() { + tu := req.BanUntil.UTC() + provider.BanUntil = &tu } if err := h.db.Create(&provider).Error; err != nil { diff --git a/internal/dto/provider.go b/internal/dto/provider.go index 9c33c3c..b48a4de 100644 --- a/internal/dto/provider.go +++ b/internal/dto/provider.go @@ -1,11 +1,17 @@ package dto +import "time" + // ProviderDTO defines inbound payload for provider creation/update. type ProviderDTO struct { - Name string `json:"name"` - Type string `json:"type"` - BaseURL string `json:"base_url"` - APIKey string `json:"api_key"` - Group string `json:"group"` - Models []string `json:"models"` // List of supported model names + Name string `json:"name"` + Type string `json:"type"` + BaseURL string `json:"base_url"` + APIKey string `json:"api_key"` + Group string `json:"group"` + Models []string `json:"models"` // List of supported model names + Status string `json:"status"` + AutoBan *bool `json:"auto_ban,omitempty"` + BanReason string `json:"ban_reason,omitempty"` + BanUntil time.Time `json:"ban_until,omitempty"` } diff --git a/internal/model/models.go b/internal/model/models.go index cdbb1f5..4d0f7db 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -1,6 +1,8 @@ package model import ( + "time" + "gorm.io/gorm" ) @@ -34,12 +36,16 @@ type Key struct { // Provider remains the same. type Provider struct { gorm.Model - Name string `gorm:"not null" json:"name"` - Type string `gorm:"not null" json:"type"` // openai, anthropic, etc. - BaseURL string `json:"base_url"` - APIKey string `json:"api_key"` - Group string `gorm:"default:'default'" json:"group"` // routing group/tier - Models string `json:"models"` // comma-separated list of supported models (e.g. "gpt-4,gpt-3.5-turbo") + Name string `gorm:"not null" json:"name"` + Type string `gorm:"not null" json:"type"` // openai, anthropic, etc. + BaseURL string `json:"base_url"` + APIKey string `json:"api_key"` + Group string `gorm:"default:'default'" json:"group"` // routing group/tier + Models string `json:"models"` // comma-separated list of supported models (e.g. "gpt-4,gpt-3.5-turbo") + Status string `gorm:"size:50;default:'active'" json:"status"` // active, auto_disabled, manual_disabled + AutoBan bool `gorm:"default:true" json:"auto_ban"` // whether DP-triggered disable is allowed + BanReason string `gorm:"size:255" json:"ban_reason"` // reason for current disable + BanUntil *time.Time `json:"ban_until"` // optional TTL for disable } // Model remains the same. diff --git a/internal/service/sync.go b/internal/service/sync.go index 07a8588..58871e9 100644 --- a/internal/service/sync.go +++ b/internal/service/sync.go @@ -65,13 +65,19 @@ func (s *SyncService) SyncProvider(provider *model.Provider) error { models := strings.Split(provider.Models, ",") snap := providerSnapshot{ - ID: provider.ID, - Name: provider.Name, - Type: provider.Type, - BaseURL: provider.BaseURL, - APIKey: provider.APIKey, - Group: group, - Models: models, + ID: provider.ID, + Name: provider.Name, + Type: provider.Type, + BaseURL: provider.BaseURL, + APIKey: provider.APIKey, + Group: group, + Models: models, + Status: normalizeStatus(provider.Status), + AutoBan: provider.AutoBan, + BanReason: provider.BanReason, + } + if provider.BanUntil != nil { + snap.BanUntil = provider.BanUntil.UTC().Unix() } // 1. Update Provider Config @@ -111,13 +117,17 @@ func (s *SyncService) SyncModel(m *model.Model) error { } type providerSnapshot struct { - ID uint `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - BaseURL string `json:"base_url"` - APIKey string `json:"api_key"` - Group string `json:"group"` - Models []string `json:"models"` + ID uint `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + BaseURL string `json:"base_url"` + APIKey string `json:"api_key"` + Group string `json:"group"` + Models []string `json:"models"` + Status string `json:"status"` + AutoBan bool `json:"auto_ban"` + BanReason string `json:"ban_reason,omitempty"` + BanUntil int64 `json:"ban_until,omitempty"` // unix seconds } // keySnapshot is no longer needed as we write directly to auth:token:* @@ -181,13 +191,19 @@ func (s *SyncService) SyncAll(db *gorm.DB) error { models := strings.Split(p.Models, ",") snap := providerSnapshot{ - ID: p.ID, - Name: p.Name, - Type: p.Type, - BaseURL: p.BaseURL, - APIKey: p.APIKey, - Group: group, - Models: models, + ID: p.ID, + Name: p.Name, + Type: p.Type, + BaseURL: p.BaseURL, + APIKey: p.APIKey, + Group: group, + Models: models, + Status: normalizeStatus(p.Status), + AutoBan: p.AutoBan, + BanReason: p.BanReason, + } + if p.BanUntil != nil { + snap.BanUntil = p.BanUntil.UTC().Unix() } payload, err := sonic.Marshal(snap) if err != nil { @@ -273,3 +289,16 @@ func normalizeGroup(group string) string { } return group } + +func normalizeStatus(status string) string { + st := strings.ToLower(strings.TrimSpace(status)) + if st == "" { + return "active" + } + switch st { + case "active", "auto_disabled", "manual_disabled": + return st + default: + return st + } +}