package config import ( "errors" "fmt" "net/url" "os" "runtime" "strconv" "strings" ) type Environment int const ( EnvDevelopment Environment = iota EnvProduction ) type FileStore int const ( FileSystemStore = iota S3Store ) 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 } func NewFromEnv() (Config, error) { envString := os.Getenv("ENV") var env Environment switch envString { case "production": env = EnvProduction case "development": env = EnvDevelopment case "": return Config{}, errors.New("ENV not set") default: return Config{}, fmt.Errorf("invalid ENV value: %s", envString) } bindAddr := os.Getenv("BIND_ADDR") if bindAddr == "" { bindAddr = "localhost:8888" } tlsCertFile := os.Getenv("TLS_CERT_FILE") tlsKeyFile := os.Getenv("TLS_KEY_FILE") if (tlsCertFile == "" && tlsKeyFile != "") || (tlsCertFile != "" && tlsKeyFile == "") { return Config{}, errors.New("both TLS_CERT_FILE and TLS_KEY_FILE must be set") } databaseURL := os.Getenv("DATABASE_URL") if databaseURL == "" { return Config{}, errors.New("DATABASE_URL not set") } fileStoreString := os.Getenv("FILE_STORE") var fileStore FileStore switch os.Getenv("FILE_STORE") { case "s3": fileStore = S3Store case "filesystem", "": fileStore = FileSystemStore default: return Config{}, fmt.Errorf("invalid FILE_STORE value: %s", fileStoreString) } fileStoreHTTPBaseURLString := os.Getenv("FILE_STORE_HTTP_BASE_URL") if !strings.HasSuffix(fileStoreHTTPBaseURLString, "/") { fileStoreHTTPBaseURLString += "/" } fileStoreHTTPBaseURL, err := url.Parse(fileStoreHTTPBaseURLString) if err != nil { return Config{}, fmt.Errorf("invalid FILE_STORE_HTTP_BASE_URL: %v", err) } 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 = os.Getenv("S3_BUCKET") if s3Bucket == "" { return Config{}, errors.New("S3_BUCKET not set") } } else { if fileStoreHTTPRoot = os.Getenv("FILE_STORE_HTTP_ROOT"); fileStoreHTTPRoot == "" { return Config{}, errors.New("FILE_STORE_HTTP_ROOT not set") } } assetsHTTPRoot := os.Getenv("ASSETS_HTTP_ROOT") ffmpegWorkerPoolSize := runtime.NumCPU() if s := os.Getenv("FFMPEG_WORKER_POOL_SIZE"); s != "" { if n, err := strconv.Atoi(s); err != nil { return Config{}, fmt.Errorf("invalid FFMPEG_WORKER_POOL_SIZE value: %s", s) } else { ffmpegWorkerPoolSize = n } } 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, }, nil }