170 lines
4.1 KiB
Go
170 lines
4.1 KiB
Go
package config
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"github.com/spf13/viper"
|
||
"go.yaml.in/yaml/v3"
|
||
)
|
||
|
||
type Config struct {
|
||
Database DatabaseConfig `yaml:"database"`
|
||
Log LogConfig `yaml:"log"`
|
||
}
|
||
|
||
type DatabaseConfig struct {
|
||
DSN string `yaml:"dsn"` // 数据库连接字符串
|
||
Path string `yaml:"path"` // SQLite: 目录路径
|
||
Name string `yaml:"name"` // SQLite: 文件名
|
||
}
|
||
|
||
type LogConfig struct {
|
||
Level string `yaml:"level"`
|
||
Format string `yaml:"format"`
|
||
Output string `yaml:"output"`
|
||
Verbose bool `yaml:"verbose"`
|
||
LogFile string `yaml:"log_file"`
|
||
ShowColor bool `yaml:"show_color"`
|
||
}
|
||
|
||
// --------------------------------- 全局单例配置(核心) ---------------------------------
|
||
var (
|
||
// GlobalConfig 全局配置单例实例
|
||
GlobalConfig *Config
|
||
// 命令行参数配置(全局、由root命令绑定)
|
||
CLIParams = struct {
|
||
Verbose bool // -v/--verbose
|
||
NoColor bool // --no-color
|
||
Config string // -c/--config
|
||
}{}
|
||
BaseDir string = "/etc/sunhpc"
|
||
LogDir string = "/var/log/sunhpc"
|
||
TmplDir string = BaseDir + "/tmpl.d"
|
||
appName string = "sunhpc"
|
||
defaultDBPath string = "/var/lib/sunhpc"
|
||
defaultDBName string = "sunhpc.db"
|
||
)
|
||
|
||
// ----------------------------------- 配置加载(只加载一次) -----------------------------------
|
||
func LoadConfig() (*Config, error) {
|
||
// 如果已经加载过,直接返回
|
||
if GlobalConfig != nil {
|
||
return GlobalConfig, nil
|
||
}
|
||
|
||
viper.SetConfigName("sunhpc")
|
||
viper.SetConfigType("yaml")
|
||
viper.AddConfigPath(BaseDir)
|
||
viper.AddConfigPath(".")
|
||
viper.AddConfigPath(filepath.Join(os.Getenv("HOME"), "."))
|
||
|
||
// Step 1: 设置默认值(最低优先级)
|
||
viper.SetDefault("log.level", "info")
|
||
viper.SetDefault("log.format", "text")
|
||
viper.SetDefault("log.output", "stdout")
|
||
viper.SetDefault("log.verbose", false)
|
||
viper.SetDefault("log.log_file", filepath.Join(LogDir, "sunhpc.log"))
|
||
viper.SetDefault("database.name", "sunhpc.db")
|
||
viper.SetDefault("database.path", "/var/lib/sunhpc")
|
||
|
||
if err := viper.ReadInConfig(); err != nil {
|
||
// 配置文件不存在时,使用默认值
|
||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 合并命令行参数(最高优先级)
|
||
if CLIParams.Verbose {
|
||
viper.Set("log.verbose", true)
|
||
viper.Set("log.level", "debug")
|
||
}
|
||
|
||
// 合并noColor参数
|
||
if CLIParams.NoColor {
|
||
viper.Set("log.show_color", false)
|
||
}
|
||
|
||
fullPath := filepath.Join(
|
||
viper.GetString("database.path"), viper.GetString("database.name"))
|
||
dsn := fmt.Sprintf(
|
||
"%s?_foreign_keys=on&_journal_mode=WAL&_timeout=5000", fullPath)
|
||
viper.Set("database.dsn", dsn)
|
||
|
||
// 解码到结构体
|
||
var cfg Config
|
||
if err := viper.Unmarshal(&cfg); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
GlobalConfig = &cfg
|
||
|
||
return GlobalConfig, nil
|
||
}
|
||
|
||
// InitDirs 创建所有必需目录
|
||
func InitDirs() error {
|
||
dirs := []string{
|
||
BaseDir,
|
||
TmplDir,
|
||
LogDir,
|
||
}
|
||
for _, d := range dirs {
|
||
if err := os.MkdirAll(d, 0755); err != nil {
|
||
return fmt.Errorf("创建目录 %s 失败: %w", d, err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *Config) WriteDefaultConfig(path string) error {
|
||
// 确保目录存在
|
||
dir := filepath.Dir(path)
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return fmt.Errorf("创建目录失败: %w", err)
|
||
}
|
||
|
||
// 生成默认配置
|
||
cfg := DefaultConfig(path)
|
||
|
||
// 序列化为 YAML
|
||
data, err := yaml.Marshal(cfg)
|
||
if err != nil {
|
||
return fmt.Errorf("序列化配置失败: %w", err)
|
||
}
|
||
|
||
// 写入文件(0644 权限)
|
||
return os.WriteFile(path, data, 0644)
|
||
}
|
||
|
||
func DefaultConfig(path string) *Config {
|
||
return &Config{
|
||
Database: DatabaseConfig{
|
||
DSN: fmt.Sprintf("%s?_foreign_keys=on&_journal_mode=WAL&_timeout=5000",
|
||
filepath.Join(filepath.Dir(path), defaultDBName)),
|
||
Path: filepath.Dir(path),
|
||
Name: defaultDBName,
|
||
},
|
||
Log: LogConfig{
|
||
Level: "info",
|
||
Format: "text",
|
||
Output: "stdout",
|
||
LogFile: filepath.Join(filepath.Dir(path), "sunhpc.log"),
|
||
Verbose: false,
|
||
},
|
||
}
|
||
}
|
||
|
||
// ResetConfig 重置全局配置为默认值
|
||
func ResetConfig() {
|
||
GlobalConfig = nil
|
||
viper.Reset()
|
||
CLIParams = struct {
|
||
Verbose bool // -v/--verbose
|
||
NoColor bool // --no-color
|
||
Config string // -c/--config
|
||
}{}
|
||
}
|