2026-03-12 20:51:38 -07:00

108 lines
3.1 KiB
Go

package hosting
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
)
// ProviderConfigStore manages saved provider configurations on disk.
// Each provider's config is stored as a separate JSON file with restrictive
// permissions (0600) since the files contain API keys.
type ProviderConfigStore struct {
configDir string
}
// NewConfigStore creates a new store rooted at configDir. The directory is
// created on first write if it does not already exist.
func NewConfigStore(configDir string) *ProviderConfigStore {
return &ProviderConfigStore{configDir: configDir}
}
// configPath returns the file path for a provider's config file.
func (s *ProviderConfigStore) configPath(providerName string) string {
return filepath.Join(s.configDir, providerName+".json")
}
// ensureDir creates the config directory if it does not exist.
func (s *ProviderConfigStore) ensureDir() error {
return os.MkdirAll(s.configDir, 0700)
}
// Save writes a provider configuration to disk. It overwrites any existing
// config for the same provider.
func (s *ProviderConfigStore) Save(providerName string, cfg ProviderConfig) error {
if providerName == "" {
return fmt.Errorf("hosting: provider name must not be empty")
}
if err := s.ensureDir(); err != nil {
return fmt.Errorf("hosting: create config dir: %w", err)
}
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return fmt.Errorf("hosting: marshal config for %s: %w", providerName, err)
}
path := s.configPath(providerName)
if err := os.WriteFile(path, data, 0600); err != nil {
return fmt.Errorf("hosting: write config for %s: %w", providerName, err)
}
return nil
}
// Load reads a provider configuration from disk.
func (s *ProviderConfigStore) Load(providerName string) (*ProviderConfig, error) {
path := s.configPath(providerName)
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("hosting: no config found for provider %q", providerName)
}
return nil, fmt.Errorf("hosting: read config for %s: %w", providerName, err)
}
var cfg ProviderConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("hosting: parse config for %s: %w", providerName, err)
}
return &cfg, nil
}
// Delete removes a provider's saved configuration.
func (s *ProviderConfigStore) Delete(providerName string) error {
path := s.configPath(providerName)
if err := os.Remove(path); err != nil {
if os.IsNotExist(err) {
return nil // already gone
}
return fmt.Errorf("hosting: delete config for %s: %w", providerName, err)
}
return nil
}
// ListConfigured returns the names of all providers that have saved configs.
func (s *ProviderConfigStore) ListConfigured() ([]string, error) {
entries, err := os.ReadDir(s.configDir)
if err != nil {
if os.IsNotExist(err) {
return nil, nil // no directory means no configs
}
return nil, fmt.Errorf("hosting: list configs: %w", err)
}
var names []string
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
if strings.HasSuffix(name, ".json") {
names = append(names, strings.TrimSuffix(name, ".json"))
}
}
return names, nil
}