mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-12-22 17:14:22 +00:00
115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// LoadConfig loads configuration with the following precedence:
|
|
// 1. Hardcoded defaults
|
|
// 2. Config file
|
|
// 3. Environment variables
|
|
func LoadConfig(configPath string) (AppConfig, error) {
|
|
// 1. Start with defaults
|
|
defaultDataDir := getDefaultDataDir()
|
|
cfg := getDefaultConfig(defaultDataDir)
|
|
|
|
// 2. Load from config file
|
|
if err := loadConfigFile(&cfg, configPath); err != nil {
|
|
return cfg, err
|
|
}
|
|
|
|
// If local node is not defined in nodes, add it with default config
|
|
if _, ok := cfg.Nodes[cfg.LocalNode]; !ok {
|
|
cfg.Nodes[cfg.LocalNode] = NodeConfig{}
|
|
}
|
|
|
|
// 3. Override with environment variables
|
|
loadEnvVars(&cfg)
|
|
|
|
// Log warning if deprecated inference keys are present
|
|
if len(cfg.Auth.InferenceKeys) > 0 {
|
|
log.Println("⚠️ Config-based inference keys are no longer supported and will be ignored.")
|
|
log.Println(" Please create inference keys in web UI or via management API.")
|
|
}
|
|
|
|
// Set default directories if not specified
|
|
if cfg.Instances.InstancesDir == "" {
|
|
cfg.Instances.InstancesDir = filepath.Join(cfg.DataDir, "instances")
|
|
} else {
|
|
// Log deprecation warning if using custom instances dir
|
|
log.Println("⚠️ Instances directory is deprecated and will be removed in future versions. Instances are persisted in the database.")
|
|
}
|
|
if cfg.Instances.LogsDir == "" {
|
|
cfg.Instances.LogsDir = filepath.Join(cfg.DataDir, "logs")
|
|
}
|
|
if cfg.Database.Path == "" {
|
|
cfg.Database.Path = filepath.Join(cfg.DataDir, "llamactl.db")
|
|
}
|
|
|
|
// Validate port range
|
|
if cfg.Instances.PortRange[0] <= 0 || cfg.Instances.PortRange[1] <= 0 || cfg.Instances.PortRange[0] >= cfg.Instances.PortRange[1] {
|
|
return AppConfig{}, fmt.Errorf("invalid port range: %v", cfg.Instances.PortRange)
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
// loadConfigFile attempts to load config from file with fallback locations
|
|
func loadConfigFile(cfg *AppConfig, configPath string) error {
|
|
var configLocations []string
|
|
|
|
// If specific config path provided, use only that
|
|
if configPath != "" {
|
|
configLocations = []string{configPath}
|
|
} else {
|
|
// Default config file locations (in order of precedence)
|
|
configLocations = getDefaultConfigLocations()
|
|
}
|
|
|
|
for _, path := range configLocations {
|
|
if data, err := os.ReadFile(path); err == nil {
|
|
if err := yaml.Unmarshal(data, cfg); err != nil {
|
|
return err
|
|
}
|
|
log.Printf("Read config at %s", path)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SanitizedCopy returns a copy of the AppConfig with sensitive information removed
|
|
func (cfg *AppConfig) SanitizedCopy() (AppConfig, error) {
|
|
// Deep copy via JSON marshal/unmarshal to avoid concurrent map access
|
|
data, err := json.Marshal(cfg)
|
|
if err != nil {
|
|
log.Printf("Failed to marshal config for sanitization: %v", err)
|
|
return AppConfig{}, err
|
|
}
|
|
|
|
var sanitized AppConfig
|
|
if err := json.Unmarshal(data, &sanitized); err != nil {
|
|
log.Printf("Failed to unmarshal config for sanitization: %v", err)
|
|
return AppConfig{}, err
|
|
}
|
|
|
|
// Clear sensitive information
|
|
sanitized.Auth.InferenceKeys = []string{}
|
|
sanitized.Auth.ManagementKeys = []string{}
|
|
|
|
// Clear API keys from nodes
|
|
for nodeName, node := range sanitized.Nodes {
|
|
node.APIKey = ""
|
|
sanitized.Nodes[nodeName] = node
|
|
}
|
|
|
|
return sanitized, nil
|
|
}
|