mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-03 20:26:36 +08:00
Improve large library storage backends and scans
This commit is contained in:
@@ -33,6 +33,7 @@ type Config struct {
|
||||
Secrets SecretsConfig `mapstructure:"secrets"`
|
||||
Logging LoggingConfig `mapstructure:"logging"`
|
||||
Cache CacheConfig `mapstructure:"cache"`
|
||||
Search SearchConfig `mapstructure:"search"`
|
||||
Media MediaConfig `mapstructure:"media"`
|
||||
Transcoder TranscoderConfig `mapstructure:"transcoder"`
|
||||
AI AIConfig `mapstructure:"ai"`
|
||||
@@ -86,9 +87,12 @@ type AppConfig struct {
|
||||
ServerURL string `mapstructure:"server_url"`
|
||||
}
|
||||
|
||||
// DatabaseConfig 配置 GORM + SQLite。
|
||||
// DatabaseConfig 配置 GORM 数据库。默认 auto:
|
||||
// Docker Compose 主线会注入 PostgreSQL DSN;裸机/旧部署没有 DSN 时回退 SQLite。
|
||||
type DatabaseConfig struct {
|
||||
Type string `mapstructure:"type"`
|
||||
DBPath string `mapstructure:"db_path"`
|
||||
DSN string `mapstructure:"dsn"`
|
||||
WALMode bool `mapstructure:"wal_mode"`
|
||||
BusyTimeout int `mapstructure:"busy_timeout"`
|
||||
CacheSize int `mapstructure:"cache_size"`
|
||||
@@ -128,6 +132,17 @@ type CacheConfig struct {
|
||||
TTLHours int `mapstructure:"ttl_hours"`
|
||||
AutoCleanup bool `mapstructure:"auto_cleanup"`
|
||||
CleanupIntervalMin int `mapstructure:"cleanup_interval_min"`
|
||||
RedisURL string `mapstructure:"redis_url"`
|
||||
RedisPrefix string `mapstructure:"redis_prefix"`
|
||||
MediaTTLSeconds int `mapstructure:"media_ttl_seconds"`
|
||||
}
|
||||
|
||||
type SearchConfig struct {
|
||||
Backend string `mapstructure:"backend"`
|
||||
OpenSearchURL string `mapstructure:"opensearch_url"`
|
||||
Index string `mapstructure:"index"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
// MediaConfig 保存默认库位置(用于引导库)。
|
||||
@@ -229,7 +244,9 @@ func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("app.cors_origins", []string{})
|
||||
v.SetDefault("app.server_url", "")
|
||||
|
||||
v.SetDefault("database.type", "auto")
|
||||
v.SetDefault("database.db_path", "./data/mediastation.db")
|
||||
v.SetDefault("database.dsn", "")
|
||||
v.SetDefault("database.wal_mode", true)
|
||||
v.SetDefault("database.busy_timeout", 5000)
|
||||
v.SetDefault("database.cache_size", -20000)
|
||||
@@ -246,6 +263,15 @@ func setDefaults(v *viper.Viper) {
|
||||
|
||||
v.SetDefault("cache.cache_dir", "./cache")
|
||||
v.SetDefault("cache.cleanup_interval_min", 60)
|
||||
v.SetDefault("cache.redis_url", "")
|
||||
v.SetDefault("cache.redis_prefix", "mediastationgo")
|
||||
v.SetDefault("cache.media_ttl_seconds", 15)
|
||||
|
||||
v.SetDefault("search.backend", "")
|
||||
v.SetDefault("search.opensearch_url", "")
|
||||
v.SetDefault("search.index", "mediastation_media")
|
||||
v.SetDefault("search.username", "")
|
||||
v.SetDefault("search.password", "")
|
||||
|
||||
v.SetDefault("ai.enabled", false)
|
||||
v.SetDefault("ai.provider", "openai")
|
||||
@@ -310,6 +336,9 @@ func (c *Config) normalize() error {
|
||||
if c.Database.DBPath == "" {
|
||||
c.Database.DBPath = filepath.Join(c.App.DataDir, "mediastation.db")
|
||||
}
|
||||
if c.Database.Type == "" {
|
||||
c.Database.Type = "auto"
|
||||
}
|
||||
if c.App.MaxCPUThreads < 1 {
|
||||
c.App.MaxCPUThreads = 1
|
||||
}
|
||||
@@ -328,6 +357,16 @@ func (c *Config) normalize() error {
|
||||
if c.Cache.CacheDir == "" {
|
||||
c.Cache.CacheDir = filepath.Join(c.App.DataDir, "cache")
|
||||
}
|
||||
if c.Cache.RedisPrefix == "" {
|
||||
c.Cache.RedisPrefix = "mediastationgo"
|
||||
}
|
||||
if c.Cache.MediaTTLSeconds < 1 {
|
||||
c.Cache.MediaTTLSeconds = 15
|
||||
}
|
||||
c.Search.Backend = strings.ToLower(strings.TrimSpace(c.Search.Backend))
|
||||
if c.Search.Index == "" {
|
||||
c.Search.Index = "mediastation_media"
|
||||
}
|
||||
if c.Secrets.JWTSecret == "" {
|
||||
// 持久化自动生成的密钥以在操作员忘记配置时保持会话稳定。
|
||||
path := filepath.Join(c.App.DataDir, ".jwt_secret")
|
||||
|
||||
@@ -31,9 +31,21 @@ func TestLoadDefaults(t *testing.T) {
|
||||
if cfg.Database.DBPath == "" {
|
||||
t.Fatalf("expected non-empty DBPath")
|
||||
}
|
||||
if cfg.Database.Type != "auto" {
|
||||
t.Fatalf("expected default database type auto, got %q", cfg.Database.Type)
|
||||
}
|
||||
if cfg.Database.MaxOpenConns != defaultDatabaseMaxOpenConns {
|
||||
t.Fatalf("expected default MaxOpenConns %d, got %d", defaultDatabaseMaxOpenConns, cfg.Database.MaxOpenConns)
|
||||
}
|
||||
if cfg.Cache.RedisPrefix != "mediastationgo" {
|
||||
t.Fatalf("expected default redis prefix, got %q", cfg.Cache.RedisPrefix)
|
||||
}
|
||||
if cfg.Cache.MediaTTLSeconds != 15 {
|
||||
t.Fatalf("expected default media cache ttl 15, got %d", cfg.Cache.MediaTTLSeconds)
|
||||
}
|
||||
if cfg.Search.Index != "mediastation_media" {
|
||||
t.Fatalf("expected default search index, got %q", cfg.Search.Index)
|
||||
}
|
||||
if cfg.Database.MaxIdleConns != defaultDatabaseMaxIdleConns {
|
||||
t.Fatalf("expected default MaxIdleConns %d, got %d", defaultDatabaseMaxIdleConns, cfg.Database.MaxIdleConns)
|
||||
}
|
||||
@@ -66,6 +78,12 @@ func TestEnvOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Setenv("MEDIASTATION_APP_PORT", "9090")
|
||||
t.Setenv("MEDIASTATION_DATABASE_TYPE", "postgres")
|
||||
t.Setenv("MEDIASTATION_DATABASE_DSN", "postgres://msgo:secret@postgres:5432/msgo?sslmode=disable")
|
||||
t.Setenv("MEDIASTATION_CACHE_REDIS_URL", "redis://redis:6379/0")
|
||||
t.Setenv("MEDIASTATION_CACHE_MEDIA_TTL_SECONDS", "30")
|
||||
t.Setenv("MEDIASTATION_SEARCH_BACKEND", "opensearch")
|
||||
t.Setenv("MEDIASTATION_SEARCH_OPENSEARCH_URL", "http://opensearch:9200")
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
@@ -73,6 +91,15 @@ func TestEnvOverride(t *testing.T) {
|
||||
if cfg.App.Port != 9090 {
|
||||
t.Fatalf("expected port 9090 from env, got %d", cfg.App.Port)
|
||||
}
|
||||
if cfg.Database.Type != "postgres" || cfg.Database.DSN == "" {
|
||||
t.Fatalf("expected postgres database config from env, got type=%q dsn=%q", cfg.Database.Type, cfg.Database.DSN)
|
||||
}
|
||||
if cfg.Cache.RedisURL != "redis://redis:6379/0" || cfg.Cache.MediaTTLSeconds != 30 {
|
||||
t.Fatalf("expected redis cache config from env, got url=%q ttl=%d", cfg.Cache.RedisURL, cfg.Cache.MediaTTLSeconds)
|
||||
}
|
||||
if cfg.Search.Backend != "opensearch" || cfg.Search.OpenSearchURL != "http://opensearch:9200" {
|
||||
t.Fatalf("expected opensearch config from env, got backend=%q url=%q", cfg.Search.Backend, cfg.Search.OpenSearchURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAllowsExplicitSingleConnectionDatabaseConfig(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user