API / internal /infrastructure /persistence /config_repository.go
sshinmen's picture
Update HF Spaces deployment with latest changes
2e6b65c
Raw
History Blame Contribute Delete
3.53 kB
// Package persistence provides repository implementations for the infrastructure layer.
package persistence
import (
"context"
"os"
"path/filepath"
"sync"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
"github.com/router-for-me/CLIProxyAPI/v6/internal/domain/errors"
"github.com/router-for-me/CLIProxyAPI/v6/internal/domain/ports"
)
// ConfigRepository implements the ports.ConfigRepository interface
// using file system storage.
type ConfigRepository struct {
configPath string
mu sync.RWMutex
}
// NewConfigRepository creates a new ConfigRepository
func NewConfigRepository(configPath string) *ConfigRepository {
return &ConfigRepository{
configPath: configPath,
}
}
// Load retrieves the current configuration from file
func (r *ConfigRepository) Load(ctx context.Context) (*config.Config, error) {
r.mu.RLock()
defer r.mu.RUnlock()
if r.configPath == "" {
return nil, errors.ErrConfigNotFound
}
cfg, err := config.LoadConfig(r.configPath)
if err != nil {
if os.IsNotExist(err) {
return nil, errors.Wrap(errors.NotFound, "configuration file not found", err)
}
return nil, errors.Wrap(errors.InternalError, "failed to load configuration", err)
}
return cfg, nil
}
// Save persists the configuration to file
func (r *ConfigRepository) Save(ctx context.Context, cfg *config.Config) error {
return r.SaveWithPath(ctx, r.configPath, cfg)
}
// SaveWithPath persists the configuration to a specific path
func (r *ConfigRepository) SaveWithPath(ctx context.Context, path string, cfg *config.Config) error {
r.mu.Lock()
defer r.mu.Unlock()
if path == "" {
return errors.New(errors.InvalidInput, "config path is empty")
}
if cfg == nil {
return errors.New(errors.InvalidInput, "config is nil")
}
// Validate before saving
if err := r.Validate(ctx, cfg); err != nil {
return err
}
// Ensure directory exists
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil {
return errors.Wrap(errors.InternalError, "failed to create config directory", err)
}
// Save with comment preservation
if err := config.SaveConfigPreserveComments(path, cfg); err != nil {
return errors.Wrap(errors.InternalError, "failed to save configuration", err)
}
return nil
}
// Validate validates the configuration without saving
func (r *ConfigRepository) Validate(ctx context.Context, cfg *config.Config) error {
if cfg == nil {
return errors.ErrInvalidConfig.WithDetail("reason", "configuration is nil")
}
// Create temporary file for validation
tmpDir := filepath.Dir(r.configPath)
if tmpDir == "" {
tmpDir = os.TempDir()
}
tmpFile, err := os.CreateTemp(tmpDir, "config-validate-*.yaml")
if err != nil {
return errors.Wrap(errors.InternalError, "failed to create temp file for validation", err)
}
tmpPath := tmpFile.Name()
// Cleanup
_ = tmpFile.Close()
defer os.Remove(tmpPath)
// Validate by loading
_, err = config.LoadConfigOptional(tmpPath, false)
if err != nil {
return errors.Wrap(errors.ValidationFailed, "configuration validation failed", err)
}
return nil
}
// GetConfigPath returns the current configuration file path
func (r *ConfigRepository) GetConfigPath() string {
r.mu.RLock()
defer r.mu.RUnlock()
return r.configPath
}
// SetConfigPath updates the configuration file path
func (r *ConfigRepository) SetConfigPath(path string) {
r.mu.Lock()
defer r.mu.Unlock()
r.configPath = path
}
// Ensure ConfigRepository implements the interface
var _ ports.ConfigRepository = (*ConfigRepository)(nil)