cli/config/config.go

148 lines
4.2 KiB
Go

package config
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
core "github.com/Snider/Core"
"github.com/adrg/xdg"
)
const appName = "lethean"
const configFileName = "config.json"
// ErrSetupRequired is returned by ServiceStartup if config.json is missing.
var ErrSetupRequired = errors.New("setup required: config.json not found")
// Service provides access to the application's configuration.
var instance *API
// NewService creates and initializes a new configuration service.
// It loads an existing configuration or creates a default one if not found.
func Register(c *core.Core) error {
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("could not resolve user home directory: %w", err)
}
userHomeDir := filepath.Join(homeDir, appName)
configDir := filepath.Join(userHomeDir, "config")
//configPath := filepath.Join(configDir, configFileName)
instance = &API{
core: c,
UserHomeDir: userHomeDir,
ConfigDir: configDir,
DataDir: filepath.Join(userHomeDir, "data"),
WorkspacesDir: filepath.Join(userHomeDir, "workspaces"),
DefaultRoute: "/",
Features: []string{},
Language: "en",
}
err = c.RegisterModule("config", instance)
if err != nil {
return err
}
c.RegisterAction(handleActionCall)
return nil
}
func handleActionCall(c *core.Core, msg core.Message) error {
switch m := msg.(type) {
case core.ActionServiceStartup:
//err := instance.ServiceStartup(context.Background(), application.ServiceOptions{})
//if err != nil {
// return err
//}
c.App.Logger.Info("Config service started")
return nil
default:
c.App.Logger.Error("Unknown message type", "type", fmt.Sprintf("%T", m))
return nil
}
}
// newDefaultConfig creates a default configuration with resolved paths and ensures directories exist.
func newDefaultConfig() (*API, error) {
if strings.Contains(appName, "..") || strings.Contains(appName, string(filepath.Separator)) {
return nil, fmt.Errorf("invalid app name '%s': contains path traversal characters", appName)
}
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("could not resolve user home directory: %w", err)
}
userHomeDir := filepath.Join(homeDir, appName)
rootDir, err := xdg.DataFile(appName)
if err != nil {
return nil, fmt.Errorf("could not resolve data directory: %w", err)
}
cacheDir, err := xdg.CacheFile(appName)
if err != nil {
return nil, fmt.Errorf("could not resolve cache directory: %w", err)
}
instance := &API{
UserHomeDir: userHomeDir,
RootDir: rootDir,
CacheDir: cacheDir,
ConfigDir: filepath.Join(userHomeDir, "config"),
DataDir: filepath.Join(userHomeDir, "data"),
WorkspacesDir: filepath.Join(userHomeDir, "workspaces"),
DefaultRoute: "/",
Features: []string{},
Language: "en", // Hardcoded default, will be overridden if loaded or detected
}
dirs := []string{instance.RootDir, instance.ConfigDir, instance.DataDir, instance.CacheDir, instance.WorkspacesDir, instance.UserHomeDir}
for _, dir := range dirs {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return nil, fmt.Errorf("could not create directory %s: %w", dir, err)
}
}
return instance, nil
}
// Save writes the current configuration to config.json.
func (c *API) Save() error {
configPath := filepath.Join(c.ConfigDir, configFileName)
data, err := json.MarshalIndent(*c, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(configPath, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}
// IsFeatureEnabled checks if a given feature is enabled in the configuration.
func (c *API) IsFeatureEnabled(feature string) bool {
for _, f := range c.Features {
if f == feature {
return true
}
}
return false
}
// EnableFeature adds a feature to the list of enabled features and saves the config.
func (c *API) EnableFeature(feature string) error {
if c.IsFeatureEnabled(feature) {
return nil
}
c.Features = append(c.Features, feature)
if err := c.Save(); err != nil {
return fmt.Errorf("failed to save config after enabling feature %s: %w", feature, err)
}
return nil
}