feat(provider): add google project and location fields

Add support for Google Cloud-specific configuration in provider models.
New fields `GoogleProject` and `GoogleLocation` are now included in the
Provider DTO, database model, API handlers, and sync service snapshots.

- Extend Provider struct in model with gorm/json tags
- Update ProviderDTO with omitempty JSON tags
- Include fields in handler create/update logic with trim
- Add fields to providerSnapshot for sync operations
This commit is contained in:
zenfun
2025-12-13 20:24:44 +08:00
parent 89cb74ba6e
commit 67df74e09a
4 changed files with 78 additions and 60 deletions

View File

@@ -58,15 +58,17 @@ func (h *Handler) CreateProvider(c *gin.Context) {
} }
provider := model.Provider{ provider := model.Provider{
Name: req.Name, Name: req.Name,
Type: req.Type, Type: req.Type,
BaseURL: req.BaseURL, BaseURL: req.BaseURL,
APIKey: req.APIKey, APIKey: req.APIKey,
Group: group, GoogleProject: strings.TrimSpace(req.GoogleProject),
Models: strings.Join(req.Models, ","), GoogleLocation: strings.TrimSpace(req.GoogleLocation),
Status: status, Group: group,
AutoBan: autoBan, Models: strings.Join(req.Models, ","),
BanReason: req.BanReason, Status: status,
AutoBan: autoBan,
BanReason: req.BanReason,
} }
if !req.BanUntil.IsZero() { if !req.BanUntil.IsZero() {
tu := req.BanUntil.UTC() tu := req.BanUntil.UTC()
@@ -133,6 +135,12 @@ func (h *Handler) UpdateProvider(c *gin.Context) {
if req.APIKey != "" { if req.APIKey != "" {
update["api_key"] = req.APIKey update["api_key"] = req.APIKey
} }
if strings.TrimSpace(req.GoogleProject) != "" {
update["google_project"] = strings.TrimSpace(req.GoogleProject)
}
if strings.TrimSpace(req.GoogleLocation) != "" {
update["google_location"] = strings.TrimSpace(req.GoogleLocation)
}
if req.Models != nil { if req.Models != nil {
update["models"] = strings.Join(req.Models, ",") update["models"] = strings.Join(req.Models, ",")
} }

View File

@@ -4,16 +4,18 @@ import "time"
// ProviderDTO defines inbound payload for provider creation/update. // ProviderDTO defines inbound payload for provider creation/update.
type ProviderDTO struct { type ProviderDTO struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
BaseURL string `json:"base_url"` BaseURL string `json:"base_url"`
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
Group string `json:"group"` GoogleProject string `json:"google_project,omitempty"`
Models []string `json:"models"` // List of supported model names GoogleLocation string `json:"google_location,omitempty"`
Status string `json:"status"` Group string `json:"group"`
AutoBan *bool `json:"auto_ban,omitempty"` Models []string `json:"models"` // List of supported model names
BanReason string `json:"ban_reason,omitempty"` Status string `json:"status"`
BanUntil time.Time `json:"ban_until,omitempty"` AutoBan *bool `json:"auto_ban,omitempty"`
BanReason string `json:"ban_reason,omitempty"`
BanUntil time.Time `json:"ban_until,omitempty"`
// Optional control params // Optional control params
SkipRouting bool `json:"skip_routing,omitempty"` // if true, do not add to routing tables (e.g., disabled) SkipRouting bool `json:"skip_routing,omitempty"` // if true, do not add to routing tables (e.g., disabled)

View File

@@ -36,16 +36,18 @@ type Key struct {
// Provider remains the same. // Provider remains the same.
type Provider struct { type Provider struct {
gorm.Model gorm.Model
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"` // openai, anthropic, etc. Type string `gorm:"not null" json:"type"` // openai, anthropic, etc.
BaseURL string `json:"base_url"` BaseURL string `json:"base_url"`
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
Group string `gorm:"default:'default'" json:"group"` // routing group/tier GoogleProject string `gorm:"size:128" json:"google_project,omitempty"`
Models string `json:"models"` // comma-separated list of supported models (e.g. "gpt-4,gpt-3.5-turbo") GoogleLocation string `gorm:"size:64" json:"google_location,omitempty"`
Status string `gorm:"size:50;default:'active'" json:"status"` // active, auto_disabled, manual_disabled Group string `gorm:"default:'default'" json:"group"` // routing group/tier
AutoBan bool `gorm:"default:true" json:"auto_ban"` // whether DP-triggered disable is allowed Models string `json:"models"` // comma-separated list of supported models (e.g. "gpt-4,gpt-3.5-turbo")
BanReason string `gorm:"size:255" json:"ban_reason"` // reason for current disable Status string `gorm:"size:50;default:'active'" json:"status"` // active, auto_disabled, manual_disabled
BanUntil *time.Time `json:"ban_until"` // optional TTL for disable 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. // Model remains the same.

View File

@@ -66,16 +66,18 @@ func (s *SyncService) SyncProvider(provider *model.Provider) error {
models := strings.Split(provider.Models, ",") models := strings.Split(provider.Models, ",")
snap := providerSnapshot{ snap := providerSnapshot{
ID: provider.ID, ID: provider.ID,
Name: provider.Name, Name: provider.Name,
Type: provider.Type, Type: provider.Type,
BaseURL: provider.BaseURL, BaseURL: provider.BaseURL,
APIKey: provider.APIKey, APIKey: provider.APIKey,
Group: group, GoogleProject: provider.GoogleProject,
Models: models, GoogleLocation: provider.GoogleLocation,
Status: normalizeStatus(provider.Status), Group: group,
AutoBan: provider.AutoBan, Models: models,
BanReason: provider.BanReason, Status: normalizeStatus(provider.Status),
AutoBan: provider.AutoBan,
BanReason: provider.BanReason,
} }
if provider.BanUntil != nil { if provider.BanUntil != nil {
snap.BanUntil = provider.BanUntil.UTC().Unix() snap.BanUntil = provider.BanUntil.UTC().Unix()
@@ -124,17 +126,19 @@ func (s *SyncService) SyncModel(m *model.Model) error {
} }
type providerSnapshot struct { type providerSnapshot struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
BaseURL string `json:"base_url"` BaseURL string `json:"base_url"`
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
Group string `json:"group"` GoogleProject string `json:"google_project,omitempty"`
Models []string `json:"models"` GoogleLocation string `json:"google_location,omitempty"`
Status string `json:"status"` Group string `json:"group"`
AutoBan bool `json:"auto_ban"` Models []string `json:"models"`
BanReason string `json:"ban_reason,omitempty"` Status string `json:"status"`
BanUntil int64 `json:"ban_until,omitempty"` // unix seconds 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:* // keySnapshot is no longer needed as we write directly to auth:token:*
@@ -198,16 +202,18 @@ func (s *SyncService) SyncAll(db *gorm.DB) error {
models := strings.Split(p.Models, ",") models := strings.Split(p.Models, ",")
snap := providerSnapshot{ snap := providerSnapshot{
ID: p.ID, ID: p.ID,
Name: p.Name, Name: p.Name,
Type: p.Type, Type: p.Type,
BaseURL: p.BaseURL, BaseURL: p.BaseURL,
APIKey: p.APIKey, APIKey: p.APIKey,
Group: group, GoogleProject: p.GoogleProject,
Models: models, GoogleLocation: p.GoogleLocation,
Status: normalizeStatus(p.Status), Group: group,
AutoBan: p.AutoBan, Models: models,
BanReason: p.BanReason, Status: normalizeStatus(p.Status),
AutoBan: p.AutoBan,
BanReason: p.BanReason,
} }
if p.BanUntil != nil { if p.BanUntil != nil {
snap.BanUntil = p.BanUntil.UTC().Unix() snap.BanUntil = p.BanUntil.UTC().Unix()