优化数据库,日志,命令模块
This commit is contained in:
@@ -4,70 +4,145 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sunhpc/pkg/logger"
|
||||
"sunhpc/pkg/utils"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
// ==============================================================
|
||||
// 全局变量
|
||||
// ==============================================================
|
||||
var (
|
||||
GlobalConfig *Config
|
||||
configOnce sync.Once // 确保配置只加载一次
|
||||
configMutex sync.RWMutex // 读写锁保护 GlobalConfig
|
||||
)
|
||||
|
||||
// ==============================================================
|
||||
// 目录常量 (可在 main.go 中通过 flag 覆盖默认值)
|
||||
// ==============================================================
|
||||
var (
|
||||
BaseDir string = utils.DefaultBaseDir
|
||||
TmplDir string = utils.DefaultTmplDir
|
||||
LogDir string = utils.DefaultLogDir
|
||||
)
|
||||
|
||||
// ==============================================================
|
||||
// 配置结构体
|
||||
// ==============================================================
|
||||
type Config struct {
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
Log LogConfig `yaml:"log"`
|
||||
Log logger.LogConfig `mapstructure:"log" yaml:"log"`
|
||||
Database DatabaseConfig `mapstructure:"database" yaml:"database"`
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
DSN string `yaml:"dsn"` // 数据库连接字符串
|
||||
Path string `yaml:"path"` // SQLite: 目录路径
|
||||
Name string `yaml:"name"` // SQLite: 文件名
|
||||
DSN string `mapstructure:"dsn" yaml:"dsn"` // 数据库连接字符串
|
||||
Path string `mapstructure:"path" yaml:"path"` // SQLite: 目录路径
|
||||
Name string `mapstructure:"name" yaml:"name"` // SQLite: 文件名
|
||||
Args string `mapstructure:"args" yaml:"args"` // 数据库连接参数
|
||||
}
|
||||
|
||||
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"`
|
||||
type CLIParamsType struct {
|
||||
Verbose bool // -v/--verbose
|
||||
NoColor bool // --no-color
|
||||
Config string // -c/--config
|
||||
}
|
||||
|
||||
// --------------------------------- 全局单例配置(核心) ---------------------------------
|
||||
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"
|
||||
)
|
||||
var CLIParams CLIParamsType // 命令行参数
|
||||
|
||||
// InitConfigs 初始化所有配置目录等数据
|
||||
func InitConfigs() 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查配置文件是否存在,不存在则创建默认配置
|
||||
configPath := filepath.Join(BaseDir, "sunhpc.yaml")
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
if err := createDefaultConfig(configPath); err != nil {
|
||||
return fmt.Errorf("创建默认配置文件失败: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDefaultConfig(configPath string) error {
|
||||
defaultConfig := &Config{
|
||||
Database: DatabaseConfig{
|
||||
Path: utils.DefaultDBPath,
|
||||
Name: utils.DefaultDBName,
|
||||
Args: utils.DefaultDBArgs,
|
||||
},
|
||||
Log: logger.LogConfig{
|
||||
Level: "info",
|
||||
Format: "text",
|
||||
Output: "stdout",
|
||||
Verbose: false,
|
||||
LogFile: utils.DefaultLogFile,
|
||||
ShowColor: true,
|
||||
},
|
||||
}
|
||||
|
||||
// 确保数据库目录存在
|
||||
if err := os.MkdirAll(defaultConfig.Database.Path, 0755); err != nil {
|
||||
return fmt.Errorf("创建数据库目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 序列号并写入
|
||||
data, err := yaml.Marshal(defaultConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %w", err)
|
||||
}
|
||||
return os.WriteFile(configPath, data, 0644)
|
||||
}
|
||||
|
||||
// ----------------------------------- 配置加载(只加载一次) -----------------------------------
|
||||
func LoadConfig() (*Config, error) {
|
||||
// 如果已经加载过,直接返回
|
||||
configMutex.RLock()
|
||||
if GlobalConfig != nil {
|
||||
// 如果已经加载过,直接返回
|
||||
configMutex.RUnlock()
|
||||
return GlobalConfig, nil
|
||||
}
|
||||
configMutex.RUnlock()
|
||||
configMutex.Lock()
|
||||
defer configMutex.Unlock()
|
||||
|
||||
// 双重检查
|
||||
if GlobalConfig != nil {
|
||||
// 如果已经加载过,直接返回
|
||||
return GlobalConfig, nil
|
||||
}
|
||||
|
||||
viper.SetConfigName("sunhpc")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(BaseDir)
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath(filepath.Join(os.Getenv("HOME"), "."))
|
||||
// 配置文件路径
|
||||
if CLIParams.Config != "" {
|
||||
viper.SetConfigFile(CLIParams.Config)
|
||||
} else {
|
||||
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")
|
||||
viper.SetDefault("log.log_file", utils.DefaultLogFile)
|
||||
viper.SetDefault("database.name", utils.DefaultDBName)
|
||||
viper.SetDefault("database.path", utils.DefaultDBPath)
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
// 配置文件不存在时,使用默认值
|
||||
@@ -87,10 +162,12 @@ func LoadConfig() (*Config, error) {
|
||||
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)
|
||||
// 计算派生配置 (如数据库 DSN)
|
||||
dbPath := viper.GetString("database.path")
|
||||
dbName := viper.GetString("database.name")
|
||||
dbArgs := viper.GetString("database.args")
|
||||
fullPath := filepath.Join(dbPath, dbName)
|
||||
dsn := fmt.Sprintf("%s?%s", fullPath, dbArgs)
|
||||
viper.Set("database.dsn", dsn)
|
||||
|
||||
// 解码到结构体
|
||||
@@ -104,57 +181,23 @@ func LoadConfig() (*Config, error) {
|
||||
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
|
||||
}
|
||||
// ==============================================================
|
||||
// SaveConfig - 保存全局配置到文件、运行时配置
|
||||
// ==============================================================
|
||||
func SaveConfig() error {
|
||||
configMutex.RLock()
|
||||
defer configMutex.RUnlock()
|
||||
|
||||
func (c *Config) WriteDefaultConfig(path string) error {
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("创建目录失败: %w", err)
|
||||
if GlobalConfig == nil {
|
||||
return fmt.Errorf("全局配置为空")
|
||||
}
|
||||
|
||||
// 生成默认配置
|
||||
cfg := DefaultConfig(path)
|
||||
|
||||
// 序列化为 YAML
|
||||
data, err := yaml.Marshal(cfg)
|
||||
configPath := filepath.Join(BaseDir, "config.yaml")
|
||||
data, err := yaml.Marshal(GlobalConfig)
|
||||
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,
|
||||
},
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(configPath, data, 0644)
|
||||
}
|
||||
|
||||
// ResetConfig 重置全局配置为默认值
|
||||
|
||||
Reference in New Issue
Block a user