214 lines
6.5 KiB
Go
214 lines
6.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"FanslySync/structs"
|
|
"FanslySync/utils"
|
|
|
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
)
|
|
|
|
// ConfigManager defines methods for accessing and persisting application configuration.
|
|
type ConfigManager interface {
|
|
// GetConfig returns the current configuration. If forceReload is true,
|
|
// it re-reads the file from disk even if a cached copy exists.
|
|
GetConfig(forceReload bool) (*structs.Config, error)
|
|
|
|
// LoadConfigOrCreate loads the config or, if the file doesn't exist,
|
|
// creates a default config, saves it, and returns it.
|
|
LoadConfigOrCreate() (*structs.Config, error)
|
|
|
|
// SaveConfig writes the given config to disk and updates the in-memory cache.
|
|
SaveConfig(config *structs.Config) error
|
|
|
|
// GetConfigPath returns the filesystem path where the config is stored.
|
|
GetConfigPath() string
|
|
|
|
// ShouldMigrateOldAppConfig checks if the old app config file exists
|
|
// and returns true if it should be migrated.
|
|
ShouldMigrateOldAppConfig() (bool, error)
|
|
|
|
// MigrateOldAppConfig migrates the old app config file to the new format.
|
|
MigrateOldAppConfig() error
|
|
}
|
|
|
|
// FileConfigManager implements ConfigManager using a JSON file on disk
|
|
// with in-memory caching and custom logging.
|
|
type FileConfigManager struct {
|
|
path string
|
|
config *structs.Config
|
|
log logger.Logger
|
|
}
|
|
|
|
func NewFileConfigManager(path string) (ConfigManager, error) {
|
|
// Create our logger
|
|
fileLogger, loggerCreateErr := utils.NewLogger("ConfigManager")
|
|
if loggerCreateErr != nil {
|
|
// Log the error and return nil
|
|
return nil, loggerCreateErr
|
|
}
|
|
|
|
return &FileConfigManager{
|
|
path: path,
|
|
log: fileLogger,
|
|
}, nil
|
|
}
|
|
|
|
// GetConfigPath returns the path to the config file.
|
|
func (mgr *FileConfigManager) GetConfigPath() string {
|
|
return mgr.path
|
|
}
|
|
|
|
func GetConfigPathForRuntime() (string, error) {
|
|
dir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, "FanslySync", "appconfig.json"), nil
|
|
}
|
|
|
|
// ShouldMigrateOldAppConfig checks for an existing legacy config.json and logs the result.
|
|
func (mgr *FileConfigManager) ShouldMigrateOldAppConfig() (bool, error) {
|
|
mgr.log.Info("Checking for old config file")
|
|
dir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error getting user config dir: %v", err))
|
|
return false, err
|
|
}
|
|
|
|
oldConfigPath := filepath.Join(dir, "FanslySync", "config.json")
|
|
if _, err := os.Stat(oldConfigPath); os.IsNotExist(err) {
|
|
mgr.log.Info(fmt.Sprintf("No old config at %s", oldConfigPath))
|
|
return false, nil
|
|
} else if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error checking old config: %v", err))
|
|
return false, err
|
|
}
|
|
|
|
mgr.log.Info(fmt.Sprintf("Old config exists at %s", oldConfigPath))
|
|
return true, nil
|
|
}
|
|
|
|
// MigrateOldAppConfig reads the legacy config.json, converts it, saves the new format,
|
|
// and removes the old file, logging each step.
|
|
func (mgr *FileConfigManager) MigrateOldAppConfig() error {
|
|
mgr.log.Info("Migrating old config file")
|
|
dir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error getting user config dir: %v", err))
|
|
return err
|
|
}
|
|
|
|
oldConfigPath := filepath.Join(dir, "FanslySync", "config.json")
|
|
if _, err := os.Stat(oldConfigPath); os.IsNotExist(err) {
|
|
mgr.log.Info("No old config to migrate")
|
|
return nil
|
|
} else if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error checking old config: %v", err))
|
|
return err
|
|
}
|
|
|
|
data, err := os.ReadFile(oldConfigPath)
|
|
if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error reading old config: %v", err))
|
|
return err
|
|
}
|
|
|
|
var oldCfg structs.OldConfig
|
|
if err := json.Unmarshal(data, &oldCfg); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error unmarshaling old config: %v", err))
|
|
return err
|
|
}
|
|
|
|
newCfg := structs.NewConfigFromOld(&oldCfg)
|
|
if err := mgr.SaveConfig(newCfg); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error saving new config: %v", err))
|
|
return err
|
|
}
|
|
|
|
if err := os.Remove(oldConfigPath); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error removing old config: %v", err))
|
|
return fmt.Errorf("could not remove old config file: %w", err)
|
|
}
|
|
|
|
mgr.log.Info("Migration complete; old config removed")
|
|
return nil
|
|
}
|
|
|
|
// GetConfig loads the config from disk if forceReload is true or no cache exists.
|
|
// It logs each step and errors encountered.
|
|
func (mgr *FileConfigManager) GetConfig(forceReload bool) (*structs.Config, error) {
|
|
mgr.log.Debug(fmt.Sprintf("GetConfig(forceReload=%v)", forceReload))
|
|
if mgr.config != nil && !forceReload {
|
|
mgr.log.Debug("Returning cached config")
|
|
return mgr.config, nil
|
|
}
|
|
|
|
data, err := os.ReadFile(mgr.path)
|
|
if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error reading config file: %v", err))
|
|
return nil, err
|
|
}
|
|
|
|
var cfg structs.Config
|
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error unmarshaling config: %v", err))
|
|
return nil, err
|
|
}
|
|
|
|
mgr.config = &cfg
|
|
mgr.log.Info("Config loaded from disk. Cache updated.")
|
|
mgr.log.Debug(fmt.Sprintf("Config: %+v", cfg))
|
|
return mgr.config, nil
|
|
}
|
|
|
|
// LoadConfigOrCreate loads an existing config or creates/saves a default if none exists.
|
|
func (mgr *FileConfigManager) LoadConfigOrCreate() (*structs.Config, error) {
|
|
cfg, err := mgr.GetConfig(false)
|
|
if err == nil {
|
|
mgr.log.Info("Existing config loaded")
|
|
return cfg, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
mgr.log.Warning("Config missing; creating default")
|
|
defaultCfg := structs.NewConfig()
|
|
if saveErr := mgr.SaveConfig(defaultCfg); saveErr != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error saving default config: %v", saveErr))
|
|
return nil, saveErr
|
|
}
|
|
mgr.log.Info("Default config created and saved")
|
|
return defaultCfg, nil
|
|
}
|
|
mgr.log.Error(fmt.Sprintf("Error loading config: %v", err))
|
|
return nil, err
|
|
}
|
|
|
|
// SaveConfig writes the config to disk, updates cache, and logs the process.
|
|
func (mgr *FileConfigManager) SaveConfig(cfg *structs.Config) error {
|
|
mgr.log.Info(fmt.Sprintf("Saving config to %s", mgr.path))
|
|
dir := filepath.Dir(mgr.path)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error creating config directory: %v", err))
|
|
return fmt.Errorf("could not create config directory: %w", err)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error marshaling config: %v", err))
|
|
return fmt.Errorf("could not marshal config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(mgr.path, data, 0o644); err != nil {
|
|
mgr.log.Error(fmt.Sprintf("Error writing config file: %v", err))
|
|
return fmt.Errorf("could not write config file: %w", err)
|
|
}
|
|
|
|
mgr.config = cfg
|
|
mgr.log.Info("Config saved and cache updated")
|
|
return nil
|
|
}
|