mirror of
https://github.com/Sosokker/go-chi-oapi-codegen-todolist.git
synced 2025-12-19 05:54:07 +01:00
142 lines
4.6 KiB
Go
142 lines
4.6 KiB
Go
package config
|
|
|
|
import (
|
|
"log/slog"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig
|
|
Database DatabaseConfig
|
|
Log LogConfig
|
|
JWT JWTConfig
|
|
OAuth OAuthConfig
|
|
Cache CacheConfig
|
|
Storage StorageConfig
|
|
Frontend FrontendConfig
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Port int `mapstructure:"port"`
|
|
ReadTimeout time.Duration `mapstructure:"readTimeout"`
|
|
WriteTimeout time.Duration `mapstructure:"writeTimeout"`
|
|
IdleTimeout time.Duration `mapstructure:"idleTimeout"`
|
|
BasePath string `mapstructure:"basePath"`
|
|
}
|
|
|
|
type StorageConfig struct {
|
|
Type string `mapstructure:"type"` // "local", "gcs"
|
|
Local LocalStorageConfig `mapstructure:"local"`
|
|
GCS GCSStorageConfig `mapstructure:"gcs"`
|
|
}
|
|
|
|
type LocalStorageConfig struct {
|
|
Path string `mapstructure:"path"`
|
|
}
|
|
|
|
type GCSStorageConfig struct {
|
|
BucketName string `mapstructure:"bucketName"`
|
|
CredentialsFile string `mapstructure:"credentialsFile"`
|
|
BaseDir string `mapstructure:"baseDir"`
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
URL string `mapstructure:"url"`
|
|
}
|
|
|
|
type LogConfig struct {
|
|
Level string `mapstructure:"level"`
|
|
Format string `mapstructure:"format"`
|
|
}
|
|
|
|
type JWTConfig struct {
|
|
Secret string `mapstructure:"secret"`
|
|
ExpiryMinutes int `mapstructure:"expiryMinutes"`
|
|
CookieName string `mapstructure:"cookieName"`
|
|
CookieDomain string `mapstructure:"cookieDomain"`
|
|
CookiePath string `mapstructure:"cookiePath"`
|
|
CookieSecure bool `mapstructure:"cookieSecure"`
|
|
CookieHttpOnly bool `mapstructure:"cookieHttpOnly"`
|
|
CookieSameSite string `mapstructure:"cookieSameSite"` // None, Lax, Strict
|
|
}
|
|
|
|
type GoogleOAuthConfig struct {
|
|
ClientID string `mapstructure:"clientId"`
|
|
ClientSecret string `mapstructure:"clientSecret"`
|
|
RedirectURL string `mapstructure:"redirectUrl"`
|
|
Scopes []string `mapstructure:"scopes"`
|
|
StateSecret string `mapstructure:"stateSecret"` // For signing state cookie
|
|
}
|
|
|
|
type OAuthConfig struct {
|
|
Google GoogleOAuthConfig `mapstructure:"google"`
|
|
}
|
|
|
|
type CacheConfig struct {
|
|
DefaultExpiration time.Duration `mapstructure:"defaultExpiration"`
|
|
CleanupInterval time.Duration `mapstructure:"cleanupInterval"`
|
|
}
|
|
|
|
type FrontendConfig struct {
|
|
Url string `mapstructure:"url"`
|
|
}
|
|
|
|
func LoadConfig(path string) (*Config, error) {
|
|
viper.SetConfigName("config") // name of config file (without extension)
|
|
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
|
|
viper.AddConfigPath(path) // optionally look for config in the working directory or specified path
|
|
viper.AddConfigPath(".") // look for config in the working directory
|
|
|
|
viper.AutomaticEnv()
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
// Set defaults
|
|
viper.SetDefault("server.port", 8080)
|
|
viper.SetDefault("server.readTimeout", 15*time.Second)
|
|
viper.SetDefault("server.writeTimeout", 15*time.Second)
|
|
viper.SetDefault("server.idleTimeout", 60*time.Second)
|
|
viper.SetDefault("log.level", "info")
|
|
viper.SetDefault("log.format", "json")
|
|
viper.SetDefault("jwt.expiryMinutes", 60)
|
|
viper.SetDefault("jwt.cookieName", "jwt_token")
|
|
viper.SetDefault("jwt.cookieHttpOnly", true)
|
|
viper.SetDefault("jwt.cookieSameSite", "Lax")
|
|
viper.SetDefault("cache.defaultExpiration", 5*time.Minute)
|
|
viper.SetDefault("cache.cleanupInterval", 10*time.Minute)
|
|
viper.SetDefault("storage.type", "local") // Default to local storage
|
|
viper.SetDefault("storage.local.path", "./uploads")
|
|
viper.SetDefault("frontend.url", "http://localhost:3000")
|
|
|
|
err := viper.ReadInConfig()
|
|
if err != nil {
|
|
slog.Warn("Error reading config file, using defaults/env vars", "error", err)
|
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
var cfg Config
|
|
err = viper.Unmarshal(&cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cfg.JWT.Secret == "" || strings.Contains(cfg.JWT.Secret, "unsafe") {
|
|
slog.Warn("JWT_SECRET environment variable not set or is using unsafe default. THIS IS INSECURE FOR PRODUCTION.")
|
|
}
|
|
if cfg.OAuth.Google.ClientID == "" || strings.Contains(cfg.OAuth.Google.ClientID, "_ENV") {
|
|
slog.Warn("OAUTH_GOOGLE_CLIENTID environment variable not set or is using placeholder.")
|
|
}
|
|
if cfg.OAuth.Google.ClientSecret == "" || strings.Contains(cfg.OAuth.Google.ClientSecret, "_ENV") {
|
|
slog.Warn("OAUTH_GOOGLE_CLIENTSECRET environment variable not set or is using placeholder.")
|
|
}
|
|
if cfg.OAuth.Google.StateSecret == "" || strings.Contains(cfg.OAuth.Google.StateSecret, "unsafe") {
|
|
slog.Warn("OAUTH_GOOGLE_STATESECRET environment variable not set or is using unsafe default. THIS IS INSECURE FOR PRODUCTION.")
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|