feat(provider): add status and banning mechanism

Introduce fields for managing provider status (active, auto_disabled,
manual_disabled) and banning logic.

- Add Status, AutoBan, BanReason, and BanUntil to Provider model and DTO
- Update CreateProvider handler to process new fields with defaults
- Extend sync service to propagate status and ban information in snapshots
- Serialize BanUntil as Unix timestamp for sync compatibility
This commit is contained in:
zenfun
2025-12-12 23:40:18 +08:00
parent eaf99e1582
commit 2407afe0e6
4 changed files with 96 additions and 39 deletions

View File

@@ -48,6 +48,15 @@ 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,
@@ -55,6 +64,13 @@ func (h *Handler) CreateProvider(c *gin.Context) {
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 {

View File

@@ -1,5 +1,7 @@
package dto
import "time"
// ProviderDTO defines inbound payload for provider creation/update.
type ProviderDTO struct {
Name string `json:"name"`
@@ -8,4 +10,8 @@ type ProviderDTO struct {
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"`
}

View File

@@ -1,6 +1,8 @@
package model
import (
"time"
"gorm.io/gorm"
)
@@ -40,6 +42,10 @@ type Provider struct {
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.

View File

@@ -72,6 +72,12 @@ func (s *SyncService) SyncProvider(provider *model.Provider) error {
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
@@ -118,6 +124,10 @@ type providerSnapshot struct {
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:*
@@ -188,6 +198,12 @@ func (s *SyncService) SyncAll(db *gorm.DB) error {
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
}
}