package config import ( "encoding/json" "fmt" "os" "path/filepath" ) type Config struct { Server ServerConfig `json:"server"` Database DatabaseConfig `json:"database"` Content ContentConfig `json:"content"` Web WebConfig `json:"web"` LogLevel string `json:"logLevel"` } type ServerConfig struct { Addr string `json:"addr"` } type DatabaseConfig struct { Path string `json:"path"` PrimaryURL string `json:"primaryUrl"` AuthToken string `json:"authToken"` } type ContentConfig struct { SourceDir string `json:"sourceDir"` StoreDir string `json:"storeDir"` } type WebConfig struct { DistDir string `json:"distDir"` DevViteURL string `json:"devViteUrl"` } func Default() Config { return Config{ Server: ServerConfig{ Addr: ":8080", }, Database: DatabaseConfig{ Path: filepath.Join("..", "..", "data", "db.sqlite"), }, Content: ContentConfig{ SourceDir: filepath.Join("..", "..", "content"), StoreDir: filepath.Join("..", "..", "data", "files"), }, Web: WebConfig{ DistDir: filepath.Join("..", "web", "dist"), DevViteURL: os.Getenv("MD_HUB_DEV_VITE_URL"), }, LogLevel: "INFO", } } func Load() (Config, error) { cfg := Default() if configPath := os.Getenv("MD_HUB_CONFIG"); configPath != "" { if err := loadFile(configPath, &cfg); err != nil { return Config{}, err } } overrideString(&cfg.Server.Addr, "MD_HUB_SERVER_ADDR") overrideString(&cfg.Database.Path, "MD_HUB_DATABASE_PATH") overrideString(&cfg.Database.PrimaryURL, "MD_HUB_DATABASE_PRIMARY_URL") overrideString(&cfg.Database.AuthToken, "MD_HUB_DATABASE_AUTH_TOKEN") overrideString(&cfg.Content.SourceDir, "MD_HUB_CONTENT_SOURCE_DIR") overrideString(&cfg.Content.StoreDir, "MD_HUB_CONTENT_STORE_DIR") overrideString(&cfg.Web.DistDir, "MD_HUB_WEB_DIST_DIR") overrideString(&cfg.Web.DevViteURL, "MD_HUB_DEV_VITE_URL") overrideString(&cfg.LogLevel, "MD_HUB_LOG_LEVEL") if cfg.Server.Addr == "" { return Config{}, fmt.Errorf("server.addr must not be empty") } return cfg, nil } func loadFile(path string, cfg *Config) error { file, err := os.Open(path) if err != nil { return fmt.Errorf("open config file %q: %w", path, err) } defer file.Close() if err := json.NewDecoder(file).Decode(cfg); err != nil { return fmt.Errorf("decode config file %q: %w", path, err) } return nil } func overrideString(target *string, key string) { if value := os.Getenv(key); value != "" { *target = value } }