package config import ( "errors" "fmt" "net/url" "os" "runtime" "strconv" "strings" ) type Environment int const ( EnvDevelopment Environment = iota EnvProduction ) type FileStore int const ( FileSystemStore FileStore = iota S3Store ) const DefaultBindAddr = "localhost:8888" type Config struct { Environment Environment BindAddr string TLSCertFile string TLSKeyFile string DatabaseURL string FileStore FileStore FileStoreHTTPRoot string FileStoreHTTPBaseURL *url.URL AWSAccessKeyID string AWSSecretAccessKey string AWSRegion string S3Bucket string AssetsHTTPRoot string FFmpegWorkerPoolSize int CORSAllowedOrigins []string } const Prefix = "CLIPPER_" func envPrefix(k string) string { return Prefix + k } func getenvPrefix(k string) string { return os.Getenv(Prefix + k) } func NewFromEnv() (Config, error) { envVarName := envPrefix("ENV") envString := os.Getenv(envVarName) var env Environment switch envString { case "production": env = EnvProduction case "development", "": env = EnvDevelopment default: return Config{}, fmt.Errorf("invalid %s value: %s", envVarName, envString) } bindAddr := getenvPrefix("BIND_ADDR") if bindAddr == "" { bindAddr = DefaultBindAddr } tlsCertFileName := envPrefix("TLS_CERT_FILE") tlsKeyFileName := envPrefix("TLS_KEY_FILE") tlsCertFile := os.Getenv(tlsCertFileName) tlsKeyFile := os.Getenv(tlsKeyFileName) if (tlsCertFile == "" && tlsKeyFile != "") || (tlsCertFile != "" && tlsKeyFile == "") { return Config{}, fmt.Errorf("both %s and %s must be set", tlsCertFileName, tlsKeyFileName) } databaseURLName := envPrefix("DATABASE_URL") databaseURL := os.Getenv(databaseURLName) if databaseURL == "" { return Config{}, fmt.Errorf("%s not set", databaseURLName) } fileStoreName := envPrefix("FILE_STORE") fileStoreString := os.Getenv(fileStoreName) var fileStore FileStore switch getenvPrefix("FILE_STORE") { case "s3": fileStore = S3Store case "filesystem", "": fileStore = FileSystemStore default: return Config{}, fmt.Errorf("invalid %s value: %s", fileStoreName, fileStoreString) } fileStoreHTTPBaseURLName := envPrefix("FILE_STORE_HTTP_BASE_URL") fileStoreHTTPBaseURLString := os.Getenv(fileStoreHTTPBaseURLName) if !strings.HasSuffix(fileStoreHTTPBaseURLString, "/") { fileStoreHTTPBaseURLString += "/" } fileStoreHTTPBaseURL, err := url.Parse(fileStoreHTTPBaseURLString) if err != nil { return Config{}, fmt.Errorf("invalid %s: %v", fileStoreHTTPBaseURLName, fileStoreHTTPBaseURLString) } var awsAccessKeyID, awsSecretAccessKey, awsRegion, s3Bucket, fileStoreHTTPRoot string if fileStore == S3Store { awsAccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID") if awsAccessKeyID == "" { return Config{}, errors.New("AWS_ACCESS_KEY_ID not set") } awsSecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY") if awsSecretAccessKey == "" { return Config{}, errors.New("AWS_SECRET_ACCESS_KEY not set") } awsRegion = os.Getenv("AWS_REGION") if awsRegion == "" { return Config{}, errors.New("AWS_REGION not set") } s3Bucket = getenvPrefix("S3_BUCKET") if s3Bucket == "" { return Config{}, errors.New("S3_BUCKET not set") } } else { if fileStoreHTTPRoot = getenvPrefix("FILE_STORE_HTTP_ROOT"); fileStoreHTTPRoot == "" { return Config{}, errors.New("FILE_STORE_HTTP_ROOT not set") } } assetsHTTPRoot := getenvPrefix("ASSETS_HTTP_ROOT") ffmpegWorkerPoolSize := runtime.NumCPU() ffmpegWorkerPoolSizeName := envPrefix("FFMPEG_WORKER_POOL_SIZE") if s := os.Getenv(ffmpegWorkerPoolSizeName); s != "" { if n, err := strconv.Atoi(s); err != nil { return Config{}, fmt.Errorf("invalid %s value: %s", ffmpegWorkerPoolSizeName, s) } else { ffmpegWorkerPoolSize = n } } var corsAllowedOrigins []string corsAllowedOriginsName := envPrefix("CORS_ALLOWED_ORIGINS") if s := os.Getenv(corsAllowedOriginsName); s != "" { corsAllowedOrigins = strings.Split(s, ",") } return Config{ Environment: env, BindAddr: bindAddr, TLSCertFile: tlsCertFile, TLSKeyFile: tlsKeyFile, DatabaseURL: databaseURL, FileStore: fileStore, AWSAccessKeyID: awsAccessKeyID, AWSSecretAccessKey: awsSecretAccessKey, AWSRegion: awsRegion, S3Bucket: s3Bucket, AssetsHTTPRoot: assetsHTTPRoot, FileStoreHTTPRoot: fileStoreHTTPRoot, FileStoreHTTPBaseURL: fileStoreHTTPBaseURL, FFmpegWorkerPoolSize: ffmpegWorkerPoolSize, CORSAllowedOrigins: corsAllowedOrigins, }, nil }