Files
BingPaper/internal/config/config.go

215 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"fmt"
"os"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Log LogConfig `mapstructure:"log"`
API APIConfig `mapstructure:"api"`
Cron CronConfig `mapstructure:"cron"`
Retention RetentionConfig `mapstructure:"retention"`
DB DBConfig `mapstructure:"db"`
Storage StorageConfig `mapstructure:"storage"`
Admin AdminConfig `mapstructure:"admin"`
Token TokenConfig `mapstructure:"token"`
Feature FeatureConfig `mapstructure:"feature"`
Web WebConfig `mapstructure:"web"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
BaseURL string `mapstructure:"base_url"`
}
type LogConfig struct {
Level string `mapstructure:"level"`
}
type APIConfig struct {
Mode string `mapstructure:"mode"` // local | redirect
}
type CronConfig struct {
Enabled bool `mapstructure:"enabled"`
DailySpec string `mapstructure:"daily_spec"`
}
type RetentionConfig struct {
Days int `mapstructure:"days"`
}
type DBConfig struct {
Type string `mapstructure:"type"` // sqlite/mysql/postgres
DSN string `mapstructure:"dsn"`
}
type StorageConfig struct {
Type string `mapstructure:"type"` // local/s3/webdav
Local LocalConfig `mapstructure:"local"`
S3 S3Config `mapstructure:"s3"`
WebDAV WebDAVConfig `mapstructure:"webdav"`
}
type LocalConfig struct {
Root string `mapstructure:"root"`
}
type S3Config struct {
Endpoint string `mapstructure:"endpoint"`
Region string `mapstructure:"region"`
Bucket string `mapstructure:"bucket"`
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
PublicURLPrefix string `mapstructure:"public_url_prefix"`
ForcePathStyle bool `mapstructure:"force_path_style"`
}
type WebDAVConfig struct {
URL string `mapstructure:"url"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
PublicURLPrefix string `mapstructure:"public_url_prefix"`
}
type AdminConfig struct {
PasswordBcrypt string `mapstructure:"password_bcrypt"`
}
type TokenConfig struct {
DefaultTTL string `mapstructure:"default_ttl"`
}
type FeatureConfig struct {
WriteDailyFiles bool `mapstructure:"write_daily_files"`
}
type WebConfig struct {
Path string `mapstructure:"path"`
}
// Bing 默认配置 (内置)
const (
BingMkt = "zh-CN"
BingFetchN = 8
BingAPIBase = "https://www.bing.com/HPImageArchive.aspx"
)
var (
GlobalConfig *Config
configLock sync.RWMutex
v *viper.Viper
)
func Init(configPath string) error {
v = viper.New()
if configPath != "" {
v.SetConfigFile(configPath)
} else {
v.SetConfigName("config")
v.SetConfigType("yaml")
v.AddConfigPath("./data")
v.AddConfigPath(".")
}
v.SetDefault("server.port", 8080)
v.SetDefault("log.level", "info")
v.SetDefault("api.mode", "local")
v.SetDefault("cron.enabled", true)
v.SetDefault("cron.daily_spec", "0 10 * * *")
v.SetDefault("retention.days", 30)
v.SetDefault("db.type", "sqlite")
v.SetDefault("db.dsn", "data/bing_paper.db")
v.SetDefault("storage.type", "local")
v.SetDefault("storage.local.root", "data/picture")
v.SetDefault("token.default_ttl", "168h")
v.SetDefault("feature.write_daily_files", true)
v.SetDefault("web.path", "web")
v.SetDefault("admin.password_bcrypt", "$2a$10$fYHPeWHmwObephJvtlyH1O8DIgaLk5TINbi9BOezo2M8cSjmJchka") // 默认密码: admin123
if err := v.ReadInConfig(); err != nil {
// 如果指定了配置文件但读取失败(且不是找不到文件的错误),或者没指定但也没找到
_, isNotFound := err.(viper.ConfigFileNotFoundError)
// 如果显式指定了文件viper 报错可能不是 ConfigFileNotFoundError 而是 os.PathError
if !isNotFound && configPath != "" {
if _, statErr := os.Stat(configPath); os.IsNotExist(statErr) {
isNotFound = true
}
}
if !isNotFound {
return err
}
// 如果文件不存在,我们使用默认值并尝试创建一个默认配置文件
targetConfigPath := configPath
if targetConfigPath == "" {
targetConfigPath = "data/config.yaml"
}
fmt.Printf("Config file not found, creating default config at %s\n", targetConfigPath)
if err := v.SafeWriteConfigAs(targetConfigPath); err != nil {
fmt.Printf("Warning: Failed to create default config file: %v\n", err)
}
}
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return err
}
GlobalConfig = &cfg
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
var newCfg Config
if err := v.Unmarshal(&newCfg); err == nil {
configLock.Lock()
GlobalConfig = &newCfg
configLock.Unlock()
}
})
v.WatchConfig()
return nil
}
func GetConfig() *Config {
configLock.RLock()
defer configLock.RUnlock()
return GlobalConfig
}
func SaveConfig(cfg *Config) error {
v.Set("server", cfg.Server)
v.Set("log", cfg.Log)
v.Set("api", cfg.API)
v.Set("cron", cfg.Cron)
v.Set("retention", cfg.Retention)
v.Set("db", cfg.DB)
v.Set("storage", cfg.Storage)
v.Set("admin", cfg.Admin)
v.Set("token", cfg.Token)
v.Set("feature", cfg.Feature)
v.Set("web", cfg.Web)
return v.WriteConfig()
}
func GetRawViper() *viper.Viper {
return v
}
func GetTokenTTL() time.Duration {
ttl, err := time.ParseDuration(GetConfig().Token.DefaultTTL)
if err != nil {
return 168 * time.Hour
}
return ttl
}