Files
kvs-sh/config.go
ryyst 39a1d4482a feat: add persistent profile storage to ~/.kvs/config.json
Implemented roadmap #1: Configuration file for persistent profiles.

Features:
- Auto-saves profiles to ~/.kvs/config.json on add/use/remove
- Auto-loads profiles on shell startup
- File created with 0600 permissions for token security
- Shows active profile in welcome message
- Added 'profile save' and 'profile load' commands for manual control

Technical details:
- Created config.go with LoadConfig/SaveConfig functions
- Profile changes automatically trigger persistence
- ~/.kvs directory created with 0700 permissions if missing
- Gracefully handles missing config file on first run

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 23:47:13 +03:00

121 lines
2.6 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
// Config holds the persistent configuration
type Config struct {
Profiles map[string]Profile `json:"profiles"`
ActiveProfile string `json:"active_profile"`
}
// getConfigPath returns the path to the config file
func getConfigPath() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
kvsDir := filepath.Join(homeDir, ".kvs")
// Create .kvs directory if it doesn't exist
if err := os.MkdirAll(kvsDir, 0700); err != nil {
return "", err
}
return filepath.Join(kvsDir, "config.json"), nil
}
// LoadConfig loads the configuration from disk
func LoadConfig() (*Config, error) {
configPath, err := getConfigPath()
if err != nil {
return nil, err
}
// If config doesn't exist, return empty config
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return &Config{
Profiles: make(map[string]Profile),
}, nil
}
data, err := os.ReadFile(configPath)
if err != nil {
return nil, err
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, err
}
if config.Profiles == nil {
config.Profiles = make(map[string]Profile)
}
return &config, nil
}
// SaveConfig saves the configuration to disk with 0600 permissions
func SaveConfig(config *Config) error {
configPath, err := getConfigPath()
if err != nil {
return err
}
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
}
// Write with restrictive permissions (0600) to protect tokens
if err := os.WriteFile(configPath, data, 0600); err != nil {
return err
}
return nil
}
// syncConfigToClient updates client state from config
func (c *KVSClient) syncConfigToClient(config *Config) {
c.profiles = config.Profiles
c.activeProfile = config.ActiveProfile
// If there's an active profile, apply it
if c.activeProfile != "" {
if profile, ok := c.profiles[c.activeProfile]; ok {
c.currentToken = profile.Token
c.currentUser = profile.UserUUID
c.baseURL = profile.BaseURL
}
}
}
// syncClientToConfig updates config from client state
func (c *KVSClient) syncClientToConfig(config *Config) {
config.Profiles = c.profiles
config.ActiveProfile = c.activeProfile
}
// saveProfiles is a convenience method to save current client state
func (c *KVSClient) saveProfiles() error {
config, err := LoadConfig()
if err != nil {
// If we can't load, create new config
config = &Config{Profiles: make(map[string]Profile)}
}
c.syncClientToConfig(config)
if err := SaveConfig(config); err != nil {
fmt.Println(yellow("Warning: Failed to save profiles:"), err)
return err
}
return nil
}