mirror of
https://git.fightbot.fun/hxuanyu/BingPaper.git
synced 2026-02-15 05:59:32 +08:00
215 lines
5.4 KiB
Go
215 lines
5.4 KiB
Go
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
|
||
}
|