Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
317
backend/sdk/cliproxy/builder.go
Normal file
317
backend/sdk/cliproxy/builder.go
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
// Package cliproxy provides the core service implementation for the CLI Proxy API.
|
||||
// It includes service lifecycle management, authentication handling, file watching,
|
||||
// and integration with various AI service providers through a unified interface.
|
||||
package cliproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
configaccess "github.com/router-for-me/CLIProxyAPI/v7/internal/access/config_access"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/api"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/pluginhost"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/watcher"
|
||||
sdkaccess "github.com/router-for-me/CLIProxyAPI/v7/sdk/access"
|
||||
sdkAuth "github.com/router-for-me/CLIProxyAPI/v7/sdk/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
// Builder constructs a Service instance with customizable providers.
|
||||
// It provides a fluent interface for configuring all aspects of the service
|
||||
// including authentication, file watching, HTTP server options, and lifecycle hooks.
|
||||
type Builder struct {
|
||||
// cfg holds the application configuration.
|
||||
cfg *config.Config
|
||||
|
||||
// configPath is the path to the configuration file.
|
||||
configPath string
|
||||
|
||||
// tokenProvider handles loading token-based clients.
|
||||
tokenProvider TokenClientProvider
|
||||
|
||||
// apiKeyProvider handles loading API key-based clients.
|
||||
apiKeyProvider APIKeyClientProvider
|
||||
|
||||
// watcherFactory creates file watcher instances.
|
||||
watcherFactory WatcherFactory
|
||||
|
||||
// hooks provides lifecycle callbacks.
|
||||
hooks Hooks
|
||||
|
||||
// authManager handles legacy authentication operations.
|
||||
authManager *sdkAuth.Manager
|
||||
|
||||
// accessManager handles request authentication providers.
|
||||
accessManager *sdkaccess.Manager
|
||||
|
||||
// coreManager handles core authentication and execution.
|
||||
coreManager *coreauth.Manager
|
||||
|
||||
// cooldownStateStore overrides runtime cooldown persistence.
|
||||
cooldownStateStore coreauth.CooldownStateStore
|
||||
|
||||
// pluginHost owns dynamic plugin lifecycle and adapters.
|
||||
pluginHost *pluginhost.Host
|
||||
|
||||
// postAuthHook is called after auth record creation and before persistence.
|
||||
postAuthHook coreauth.PostAuthHook
|
||||
|
||||
// serverOptions contains additional server configuration options.
|
||||
serverOptions []api.ServerOption
|
||||
}
|
||||
|
||||
// Hooks allows callers to plug into service lifecycle stages.
|
||||
// These callbacks provide opportunities to perform custom initialization
|
||||
// and cleanup operations during service startup and shutdown.
|
||||
type Hooks struct {
|
||||
// OnBeforeStart is called before the service starts, allowing configuration
|
||||
// modifications or additional setup.
|
||||
OnBeforeStart func(*config.Config)
|
||||
|
||||
// OnAfterStart is called after the service has started successfully,
|
||||
// providing access to the service instance for additional operations.
|
||||
OnAfterStart func(*Service)
|
||||
}
|
||||
|
||||
// NewBuilder creates a Builder with default dependencies left unset.
|
||||
// Use the fluent interface methods to configure the service before calling Build().
|
||||
//
|
||||
// Returns:
|
||||
// - *Builder: A new builder instance ready for configuration
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{}
|
||||
}
|
||||
|
||||
// WithConfig sets the configuration instance used by the service.
|
||||
//
|
||||
// Parameters:
|
||||
// - cfg: The application configuration
|
||||
//
|
||||
// Returns:
|
||||
// - *Builder: The builder instance for method chaining
|
||||
func (b *Builder) WithConfig(cfg *config.Config) *Builder {
|
||||
b.cfg = cfg
|
||||
return b
|
||||
}
|
||||
|
||||
// WithConfigPath sets the absolute configuration file path used for reload watching.
|
||||
//
|
||||
// Parameters:
|
||||
// - path: The absolute path to the configuration file
|
||||
//
|
||||
// Returns:
|
||||
// - *Builder: The builder instance for method chaining
|
||||
func (b *Builder) WithConfigPath(path string) *Builder {
|
||||
b.configPath = path
|
||||
return b
|
||||
}
|
||||
|
||||
// WithTokenClientProvider overrides the provider responsible for token-backed clients.
|
||||
func (b *Builder) WithTokenClientProvider(provider TokenClientProvider) *Builder {
|
||||
b.tokenProvider = provider
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAPIKeyClientProvider overrides the provider responsible for API key-backed clients.
|
||||
func (b *Builder) WithAPIKeyClientProvider(provider APIKeyClientProvider) *Builder {
|
||||
b.apiKeyProvider = provider
|
||||
return b
|
||||
}
|
||||
|
||||
// WithWatcherFactory allows customizing the watcher factory that handles reloads.
|
||||
func (b *Builder) WithWatcherFactory(factory WatcherFactory) *Builder {
|
||||
b.watcherFactory = factory
|
||||
return b
|
||||
}
|
||||
|
||||
// WithHooks registers lifecycle hooks executed around service startup.
|
||||
func (b *Builder) WithHooks(h Hooks) *Builder {
|
||||
b.hooks = h
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAuthManager overrides the authentication manager used for token lifecycle operations.
|
||||
func (b *Builder) WithAuthManager(mgr *sdkAuth.Manager) *Builder {
|
||||
b.authManager = mgr
|
||||
return b
|
||||
}
|
||||
|
||||
// WithRequestAccessManager overrides the request authentication manager.
|
||||
func (b *Builder) WithRequestAccessManager(mgr *sdkaccess.Manager) *Builder {
|
||||
b.accessManager = mgr
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCoreAuthManager overrides the runtime auth manager responsible for request execution.
|
||||
func (b *Builder) WithCoreAuthManager(mgr *coreauth.Manager) *Builder {
|
||||
b.coreManager = mgr
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCooldownStateStore overrides the store used for runtime cooldown persistence.
|
||||
func (b *Builder) WithCooldownStateStore(store coreauth.CooldownStateStore) *Builder {
|
||||
b.cooldownStateStore = store
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPluginHost overrides the dynamic plugin host used by the service.
|
||||
func (b *Builder) WithPluginHost(host *pluginhost.Host) *Builder {
|
||||
b.pluginHost = host
|
||||
return b
|
||||
}
|
||||
|
||||
// WithServerOptions appends server configuration options used during construction.
|
||||
func (b *Builder) WithServerOptions(opts ...api.ServerOption) *Builder {
|
||||
b.serverOptions = append(b.serverOptions, opts...)
|
||||
return b
|
||||
}
|
||||
|
||||
// WithLocalManagementPassword configures a password that is only accepted from localhost management requests.
|
||||
func (b *Builder) WithLocalManagementPassword(password string) *Builder {
|
||||
if password == "" {
|
||||
return b
|
||||
}
|
||||
b.serverOptions = append(b.serverOptions, api.WithLocalManagementPassword(password))
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPostAuthHook registers a hook to be called after an Auth record is created
|
||||
// but before it is persisted to storage.
|
||||
func (b *Builder) WithPostAuthHook(hook coreauth.PostAuthHook) *Builder {
|
||||
if hook == nil {
|
||||
return b
|
||||
}
|
||||
b.postAuthHook = hook
|
||||
return b
|
||||
}
|
||||
|
||||
// Build validates inputs, applies defaults, and returns a ready-to-run service.
|
||||
func (b *Builder) Build() (*Service, error) {
|
||||
if b.cfg == nil {
|
||||
return nil, fmt.Errorf("cliproxy: configuration is required")
|
||||
}
|
||||
if b.configPath == "" {
|
||||
return nil, fmt.Errorf("cliproxy: configuration path is required")
|
||||
}
|
||||
if errValidate := b.cfg.ValidateCredentialWeights(); errValidate != nil {
|
||||
return nil, fmt.Errorf("cliproxy: validate credential weights: %w", errValidate)
|
||||
}
|
||||
b.cfg.NormalizePluginsConfig()
|
||||
if errResolvePluginsDir := b.cfg.ResolvePluginsDir(); errResolvePluginsDir != nil && b.cfg.Plugins.Enabled {
|
||||
return nil, fmt.Errorf("cliproxy: %w", errResolvePluginsDir)
|
||||
}
|
||||
|
||||
tokenProvider := b.tokenProvider
|
||||
if tokenProvider == nil {
|
||||
tokenProvider = NewFileTokenClientProvider()
|
||||
}
|
||||
|
||||
apiKeyProvider := b.apiKeyProvider
|
||||
if apiKeyProvider == nil {
|
||||
apiKeyProvider = NewAPIKeyClientProvider()
|
||||
}
|
||||
|
||||
watcherFactory := b.watcherFactory
|
||||
if watcherFactory == nil {
|
||||
watcherFactory = defaultWatcherFactory
|
||||
}
|
||||
|
||||
authManager := b.authManager
|
||||
if authManager == nil {
|
||||
authManager = newDefaultAuthManager()
|
||||
}
|
||||
|
||||
accessManager := b.accessManager
|
||||
if accessManager == nil {
|
||||
accessManager = sdkaccess.NewManager()
|
||||
}
|
||||
|
||||
configaccess.Register(&b.cfg.SDKConfig)
|
||||
pluginHost := b.pluginHost
|
||||
if pluginHost == nil {
|
||||
pluginHost = pluginhost.New()
|
||||
}
|
||||
if b.cfg != nil {
|
||||
pluginHost.ApplyConfig(context.Background(), b.cfg)
|
||||
pluginHost.RegisterFrontendAuthProviders()
|
||||
}
|
||||
accessManager.SetProviders(sdkaccess.RegisteredProviders())
|
||||
|
||||
coreManager := b.coreManager
|
||||
cooldownStateStore := b.cooldownStateStore
|
||||
var appliedRoutingState *routingRuntimeState
|
||||
if coreManager == nil {
|
||||
tokenStore := sdkAuth.GetTokenStore()
|
||||
if dirSetter, ok := tokenStore.(interface{ SetBaseDir(string) }); ok && b.cfg != nil {
|
||||
dirSetter.SetBaseDir(b.cfg.AuthDir)
|
||||
}
|
||||
if cooldownStateStore == nil {
|
||||
if provider, ok := tokenStore.(coreauth.CooldownStateStoreProvider); ok {
|
||||
cooldownStateStore = provider.CooldownStateStore()
|
||||
}
|
||||
}
|
||||
|
||||
routingState := normalizedRoutingRuntimeState(b.cfg)
|
||||
coreManager = coreauth.NewManager(tokenStore, newRoutingSelector(routingState), nil)
|
||||
appliedRoutingState = &routingState
|
||||
}
|
||||
// Attach a default RoundTripper provider so providers can opt-in per-auth transports.
|
||||
coreManager.SetRoundTripperProvider(newDefaultRoundTripperProvider())
|
||||
coreManager.SetConfig(b.cfg)
|
||||
coreManager.SetOAuthModelAlias(b.cfg.OAuthModelAlias)
|
||||
if pluginHost != nil {
|
||||
coreManager.SetPluginScheduler(pluginHost)
|
||||
}
|
||||
|
||||
service := &Service{
|
||||
cfg: b.cfg,
|
||||
configPath: b.configPath,
|
||||
tokenProvider: tokenProvider,
|
||||
apiKeyProvider: apiKeyProvider,
|
||||
watcherFactory: watcherFactory,
|
||||
hooks: b.hooks,
|
||||
authManager: authManager,
|
||||
accessManager: accessManager,
|
||||
coreManager: coreManager,
|
||||
cooldownStateStore: cooldownStateStore,
|
||||
pluginHost: pluginHost,
|
||||
appliedRoutingState: appliedRoutingState,
|
||||
serverOptions: append([]api.ServerOption(nil), b.serverOptions...),
|
||||
}
|
||||
if b.postAuthHook != nil {
|
||||
service.serverOptions = append(service.serverOptions, api.WithPostAuthHook(b.postAuthHook))
|
||||
}
|
||||
service.serverOptions = append(service.serverOptions,
|
||||
api.WithPostAuthPersistHook(service.runtimeAuthSyncHook()),
|
||||
api.WithPluginHost(pluginHost),
|
||||
api.WithConfigReloadHook(func(_ context.Context, _ *config.Config) {
|
||||
service.reloadConfigFromWatcher()
|
||||
}),
|
||||
)
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (s *Service) runtimeAuthSyncHook() coreauth.PostAuthHook {
|
||||
return func(ctx context.Context, auth *coreauth.Auth) error {
|
||||
if s == nil || auth == nil || auth.ID == "" {
|
||||
return nil
|
||||
}
|
||||
action := watcher.AuthUpdateActionAdd
|
||||
if s.coreManager != nil {
|
||||
if _, ok := s.coreManager.GetByID(auth.ID); ok {
|
||||
action = watcher.AuthUpdateActionModify
|
||||
}
|
||||
}
|
||||
update := watcher.AuthUpdate{
|
||||
Action: action,
|
||||
ID: auth.ID,
|
||||
Auth: auth,
|
||||
}
|
||||
if s.watcher != nil && s.watcher.DispatchPersistedAuthUpdate(update) {
|
||||
return nil
|
||||
}
|
||||
s.handleAuthUpdate(coreauth.WithSkipPersist(ctx), update)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue