33947816b7
- Task 1: Project setup (go.mod, Makefile, .gitignore, main.go) - Task 2: Core data models (Host, KeyPair, Snippet, AppConfig) + JSON storage layer - Task 3: Configuration management with cross-platform path support (macOS/Linux/Windows) - Updated PROJECT_STATE.md with progress tracking
164 lines
3.9 KiB
Go
164 lines
3.9 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
|
|
"git.tukangketik.id/swanadiva/hostkeeper/internal/models"
|
|
)
|
|
|
|
// Config manages application configuration and paths
|
|
type Config struct {
|
|
appName string
|
|
configDir string
|
|
dataDir string
|
|
appConfig *models.AppConfig
|
|
}
|
|
|
|
// New creates a new Config instance
|
|
func New() (*Config, error) {
|
|
appName := "hostkeeper"
|
|
|
|
configDir, err := getConfigDir(appName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get config directory: %w", err)
|
|
}
|
|
|
|
dataDir := filepath.Join(configDir, "data")
|
|
|
|
c := &Config{
|
|
appName: appName,
|
|
configDir: configDir,
|
|
dataDir: dataDir,
|
|
}
|
|
|
|
// Create directories
|
|
if err := os.MkdirAll(configDir, 0700); err != nil {
|
|
return nil, fmt.Errorf("failed to create config directory: %w", err)
|
|
}
|
|
if err := os.MkdirAll(dataDir, 0700); err != nil {
|
|
return nil, fmt.Errorf("failed to create data directory: %w", err)
|
|
}
|
|
|
|
// Load or create config
|
|
if err := c.load(); err != nil {
|
|
return nil, fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// getConfigDir returns the OS-appropriate config directory for the app
|
|
func getConfigDir(appName string) (string, error) {
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, "Library", "Application Support", appName), nil
|
|
case "linux":
|
|
if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
|
|
return filepath.Join(xdg, appName), nil
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, ".config", appName), nil
|
|
case "windows":
|
|
appData := os.Getenv("APPDATA")
|
|
if appData != "" {
|
|
return filepath.Join(appData, appName), nil
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, "AppData", "Roaming", appName), nil
|
|
default:
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, "."+appName), nil
|
|
}
|
|
}
|
|
|
|
// load reads the config file, or creates a default one if it doesn't exist
|
|
func (c *Config) load() error {
|
|
configPath := c.GetConfigFilePath()
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
// Create default config
|
|
c.appConfig = models.DefaultConfig()
|
|
return c.Save()
|
|
}
|
|
return err
|
|
}
|
|
|
|
c.appConfig = models.DefaultConfig()
|
|
if err := json.Unmarshal(data, c.appConfig); err != nil {
|
|
return fmt.Errorf("failed to parse config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Save writes the current configuration to disk
|
|
func (c *Config) Save() error {
|
|
configPath := c.GetConfigFilePath()
|
|
|
|
data, err := json.MarshalIndent(c.appConfig, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config: %w", err)
|
|
}
|
|
|
|
return os.WriteFile(configPath, data, 0600)
|
|
}
|
|
|
|
// GetAppConfig returns the application configuration
|
|
func (c *Config) GetAppConfig() *models.AppConfig {
|
|
return c.appConfig
|
|
}
|
|
|
|
// UpdateAppConfig updates the application configuration
|
|
func (c *Config) UpdateAppConfig(cfg *models.AppConfig) error {
|
|
c.appConfig = cfg
|
|
return c.Save()
|
|
}
|
|
|
|
// GetConfigDir returns the configuration directory path
|
|
func (c *Config) GetConfigDir() string {
|
|
return c.configDir
|
|
}
|
|
|
|
// GetDataDir returns the data directory path
|
|
func (c *Config) GetDataDir() string {
|
|
return c.dataDir
|
|
}
|
|
|
|
// GetConfigFilePath returns the full path to the config JSON file
|
|
func (c *Config) GetConfigFilePath() string {
|
|
return filepath.Join(c.configDir, "config.json")
|
|
}
|
|
|
|
// GetHostsFilePath returns the full path to the hosts JSON file
|
|
func (c *Config) GetHostsFilePath() string {
|
|
return filepath.Join(c.dataDir, "hosts.json")
|
|
}
|
|
|
|
// GetKeysFilePath returns the full path to the keys JSON file
|
|
func (c *Config) GetKeysFilePath() string {
|
|
return filepath.Join(c.dataDir, "keys.json")
|
|
}
|
|
|
|
// GetSnippetsFilePath returns the full path to the snippets JSON file
|
|
func (c *Config) GetSnippetsFilePath() string {
|
|
return filepath.Join(c.dataDir, "snippets.json")
|
|
} |