mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
- Introduce `SyncOutboxService` and model to retry failed CP-to-Redis sync operations - Update `SyncService` to handle sync failures by enqueuing tasks to the outbox - Centralize provider group and API key validation logic into `ProviderGroupManager` - Refactor API handlers to utilize the new manager and robust sync methods - Add configuration options for sync outbox (interval, batch size, retries)
92 lines
2.4 KiB
Go
92 lines
2.4 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ez-api/ez-api/internal/model"
|
|
"github.com/ez-api/foundation/provider"
|
|
)
|
|
|
|
// ProviderGroupManager centralizes ProviderGroup defaults and validation.
|
|
type ProviderGroupManager struct{}
|
|
|
|
func NewProviderGroupManager() *ProviderGroupManager {
|
|
return &ProviderGroupManager{}
|
|
}
|
|
|
|
// NormalizeGroup applies type-specific defaults and validates required fields.
|
|
func (m *ProviderGroupManager) NormalizeGroup(group model.ProviderGroup) (model.ProviderGroup, error) {
|
|
name := strings.TrimSpace(group.Name)
|
|
if name == "" {
|
|
return model.ProviderGroup{}, fmt.Errorf("name required")
|
|
}
|
|
group.Name = name
|
|
|
|
ptypeRaw := strings.TrimSpace(group.Type)
|
|
ptype := provider.NormalizeType(ptypeRaw)
|
|
if ptype == "" {
|
|
return model.ProviderGroup{}, fmt.Errorf("type required")
|
|
}
|
|
group.Type = ptypeRaw
|
|
|
|
group.BaseURL = strings.TrimSpace(group.BaseURL)
|
|
group.GoogleProject = strings.TrimSpace(group.GoogleProject)
|
|
group.GoogleLocation = strings.TrimSpace(group.GoogleLocation)
|
|
|
|
switch ptype {
|
|
case provider.TypeOpenAI:
|
|
if group.BaseURL == "" {
|
|
group.BaseURL = "https://api.openai.com/v1"
|
|
}
|
|
case provider.TypeAnthropic, provider.TypeClaude:
|
|
if group.BaseURL == "" {
|
|
group.BaseURL = "https://api.anthropic.com"
|
|
}
|
|
case provider.TypeCompatible:
|
|
if group.BaseURL == "" {
|
|
return model.ProviderGroup{}, fmt.Errorf("base_url required for compatible providers")
|
|
}
|
|
default:
|
|
if provider.IsVertexFamily(ptype) {
|
|
if group.GoogleLocation == "" {
|
|
group.GoogleLocation = provider.DefaultGoogleLocation(ptype, "")
|
|
}
|
|
} else if provider.IsGoogleFamily(ptype) {
|
|
// Google SDK (gemini/google/aistudio) ignores base_url.
|
|
group.BaseURL = strings.TrimSpace(group.BaseURL)
|
|
}
|
|
}
|
|
|
|
if group.Status == "" {
|
|
group.Status = "active"
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
// ValidateAPIKey enforces provider-type requirements for APIKey entries.
|
|
func (m *ProviderGroupManager) ValidateAPIKey(group model.ProviderGroup, key model.APIKey) error {
|
|
ptype := provider.NormalizeType(group.Type)
|
|
if ptype == "" {
|
|
return fmt.Errorf("provider group type required")
|
|
}
|
|
apiKey := strings.TrimSpace(key.APIKey)
|
|
|
|
switch {
|
|
case provider.IsVertexFamily(ptype):
|
|
// Vertex uses ADC; api_key can be empty.
|
|
return nil
|
|
case provider.IsGoogleFamily(ptype):
|
|
if apiKey == "" {
|
|
return fmt.Errorf("api_key required for gemini api providers")
|
|
}
|
|
return nil
|
|
default:
|
|
if apiKey == "" {
|
|
return fmt.Errorf("api_key required")
|
|
}
|
|
return nil
|
|
}
|
|
}
|