Skip to content

Commit

Permalink
fix configuration via environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
stv0g committed Jun 10, 2022
1 parent 89991b2 commit 6e1bd34
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 61 deletions.
43 changes: 19 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,30 +111,25 @@ For reference have a look at the [example configuration file](config.yaml).

All settings from the configuration file can also be set via environment variables:

| Variable | Example Value | Description |
| :-- | :-- | :-- |
| `GOSE_LISTEN` | `":8080"` | Listen address and port of Gose |
| `GOSE_BASE_URL` | `"http://localhost:8080"` | Base URL at which Gose is accessible |
| `GOSE_STATIC` | `"./dist"` | Directory of frontend assets if not bundled into the binary |
| `GOSE_BUCKET` | `gose-uploads` | Name of S3 bucket |
| `GOSE_ENDPOINT` | `s3.0l.de` | Hostname of S3 server |
| `GOSE_REGION` | `s3` | Region of S3 server |
| `GOSE_PATH_STYLE` | `true` | Prepend bucket name to path |
| `GOSE_NO_SSL` | `false` | Disable SSL encryption for S3 |
| `GOSE_ACCESS_KEY` | | S3 Access Key |
| `GOSE_SECRET_KEY` | | S3 Secret Key |
| `AWS_ACCESS_KEY_ID` | | alias for `GOSE_S3_ACCESS_KEY` |
| `AWS_SECRET_ACCESS_KEY` | | alias for `AWS_SECRET_ACCESS_KEY` |
| `GOSE_S3_MAX_UPLOAD_SIZE` | `5TB` | Maximum upload size |
| `GOSE_S3_PART_SIZE` | `5MB` | Part-size for multi-part uploads |
| `GOSE_S3_EXPIRATION_DEFAULT_CLASS` | `1week # one of the tags below` | Default expiration class |
| `GOSE_SHORTENER_ENDPOINT` | `"https://shlink-api/rest/v2/short-urls/shorten?apiKey=<your-api-token>&format=txt&longUrl={{.UrlEscaped}}"` | API Endpoint of link shortener |
| `GOSE_SHORTENER_METHOD` | `GET` | HTTP method for link shortener |
| `GOSE_SHORTENER_RESPONSE` | `raw` | Response type of link shortener |
| `GOSE_NOTIFICATION_URLS` | `pushover://shoutrrr:<api-token>@<user-key>?devices=laptop1&title=Upload` | Service URLs for [shoutrrr notifications](https://containrrr.dev/shoutrrr/) |
| `GOSE_NOTIFICATION_TEMPLATE` | `"New Upload: {{.URL}}"` | Notification message template |
| `GOSE_NOTIFICATION_MAIL_URL` | `smtp://user:password@host:port/?fromAddress=max@example.com` | Service URLs for [shoutrrr notifications](https://containrrr.dev/shoutrrr/) |
| `GOSE_NOTIFICATION_MAIL_TEMPLATE` | `"New Upload: {{.URL}}"` | Notification message template |
| Variable | Default Value | Description |
| :-- | :-- | :-- |
| `GOSE_LISTEN` | `":8080"` | Listen address and port of Gose |
| `GOSE_BASE_URL` | `"http://localhost:8080"` | Base URL at which Gose is accessible |
| `GOSE_STATIC` | `"./dist"` | Directory of frontend assets if not bundled into the binary |
| `GOSE_BUCKET` | `gose-uploads` | Name of S3 bucket |
| `GOSE_ENDPOINT` | (without `http(s)://` prefix, but with port number) | Hostname:Port of S3 server |
| `GOSE_REGION` | `us-east-1` | Region of S3 server |
| `GOSE_PATH_STYLE` | `false` | Prepend bucket name to path |
| `GOSE_NO_SSL` | `false` | Disable SSL encryption for S3 |
| `GOSE_ACCESS_KEY` | | S3 Access Key |
| `GOSE_SECRET_KEY` | | S3 Secret Key |
| `GOSE_CREATE_BUCKET` | `true` | Create S3 bucket if non-existant |
| `GOSE_MAX_UPLOAD_SIZE` | `1TB` | Maximum upload size |
| `GOSE_PART_SIZE` | `16MB` | Part-size for multi-part uploads |
| `AWS_ACCESS_KEY_ID` | | alias for `GOSE_ACCESS_KEY` |
| `AWS_SECRET_ACCESS_KEY` | | alias for `GOSE_SECRET_KEY` |

Configuration of link shortener and notifiers must be done via a [configuration file](#file).

## Author

Expand Down
121 changes: 84 additions & 37 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,34 @@ const (

// DefaultRegion is the default S3 region if not provided by the configuration
DefaultRegion = "us-east-1"

// DefaultBucket is the default S3 bucket name to use if not provided by the configuration
DefaultBucket = "gose-uploads"
)

var (
DefaultExpiration = []Expiration{
{
ID: "1day",
Title: "1day",
Days: 1,
},
{
ID: "1week",
Title: "1 week",
Days: 7,
},
{
ID: "1month",
Title: "1 month",
Days: 31,
},
{
ID: "1year",
Title: "1 year",
Days: 365,
},
}
)

type size int64
Expand All @@ -38,75 +66,78 @@ func (s *size) UnmarshalText(text []byte) error {

// Expiration describes how long files are kept before getting deleted
type Expiration struct {
ID string `mapstructure:"id" json:"id"`
Title string `mapstructure:"title" json:"title"`
ID string `json:"id" yaml:"id"`
Title string `json:"title" yaml:"title"`

Days int64 `mapstructure:"days" json:"days"`
Days int64 `json:"days" yaml:"days"`
}

// S3ServerConfig is the public part of S3Server
type S3ServerConfig struct {
ID string `mapstructure:"id" json:"id"`
Title string `mapstructure:"title" json:"title"`
ID string `json:"id" yaml:"id"`
Title string `json:"title" yaml:"title"`

MaxUploadSize size `mapstructure:"max_upload_size" json:"max_upload_size"`
PartSize size `mapstructure:"part_size" json:"part_size"`
Expiration []Expiration `mapstructure:"expiration" json:"expiration"`
MaxUploadSize size `json:"max_upload_size" yaml:"max_upload_size"`
PartSize size `json:"part_size" yaml:"part_size"`
Expiration []Expiration `json:"expiration" yaml:"expiration"`
}

// S3Server describes an S3 server
type S3Server struct {
S3ServerConfig `mapstructure:",squash"`

Endpoint string `mapstructure:"endpoint"`
Bucket string `mapstructure:"bucket"`
Region string `mapstructure:"region"`
PathStyle bool `mapstructure:"path_style"`
NoSSL bool `mapstructure:"no_ssl"`
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
CreateBucket bool `mapstructure:"create_bucket"`
S3ServerConfig `json:",squash"`

Endpoint string `json:"endpoint" yaml:"endpoint"`
Bucket string `json:"bucket" yaml:"bucket"`
Region string `json:"region" yaml:"region"`
PathStyle bool `json:"path_style" yaml:"path_style"`
NoSSL bool `json:"no_ssl" yaml:"no_ssl"`
AccessKey string `json:"access_key" yaml:"access_key"`
SecretKey string `json:"secret_key" yaml:"secret_key"`
CreateBucket bool `json:"create_bucket" yaml:"create_bucket"`
}

// ShortenerConfig contains Link-shortener specific configuration
type ShortenerConfig struct {
Endpoint string `mapstructure:"endpoint"`
Method string `mapstructure:"method"`
Response string `mapstructure:"response"`
Endpoint string `json:"endpoint" yaml:"endpoint"`
Method string `json:"method" yaml:"method"`
Response string `json:"response" yaml:"response"`
}

// NotificationConfig contains notification specific configuration
type NotificationConfig struct {
URLs []string `mapstructure:"urls"`
Template string `mapstructure:"template"`
URLs []string `json:"urls" yaml:"urls"`
Template string `json:"template" yaml:"template"`

Uploads bool `mapstructure:"uploads"`
Downloads bool `mapstructure:"downloads"`
Uploads bool `json:"uploads" yaml:"uploads"`
Downloads bool `json:"downloads" yaml:"downloads"`

Mail *struct {
URL string `mapstructure:"url"`
Template string `mapstructure:"template"`
} `mapstructure:"mail"`
URL string `json:"url" yaml:"url"`
Template string `json:"template" yaml:"template"`
} `json:"mail" yaml:"mail"`
}

// Config contains the main configuration
type Config struct {
S3Server `mapstructure:",squash"`
*viper.Viper `json:"-" yaml:"-"`

*viper.Viper `mapstructure:"-"`
// Default or single server config values
S3Server `json:",squash" yaml:"default"`

// Multiple server config values
Servers []S3Server `json:"servers" yaml:"servers,omitempty"`

// Host is the local machine IP Address to bind the HTTP Server to
Listen string `mapstructure:"listen"`
Listen string `json:"listen" yaml:"listen,omitempty"`

// Directory of frontend assets if not bundled
Static string `mapstructure:"static"`
Static string `json:"static" yaml:"static,omitempty"`

// BaseURL at which Gose is accessible
BaseURL string `mapstructure:"base_url"`
BaseURL string `json:"base_url" yaml:"base_url,omitempty"`

Servers []S3Server `mapstructure:"servers"`
Shortener *ShortenerConfig `mapstructure:"shortener"`
Notification *NotificationConfig `mapstructure:"notification"`
Shortener *ShortenerConfig `json:"shortener" yaml:"shortener,omitempty"`
Notification *NotificationConfig `json:"notification" yaml:"notification,omitempty"`
}

// NewConfig returns a new decoded Config struct
Expand All @@ -123,7 +154,15 @@ func NewConfig(configFile string) (*Config, error) {
cfg.SetDefault("notification.downloads", false)
cfg.SetDefault("max_upload_size", DefaultMaxUploadSize)
cfg.SetDefault("part_size", DefaultPartSize)
cfg.SetDefault("expiration", DefaultExpiration)
cfg.SetDefault("endpoint", "")
cfg.SetDefault("bucket", DefaultBucket)
cfg.SetDefault("region", DefaultRegion)
cfg.SetDefault("path_style", false)
cfg.SetDefault("no_ssl", false)
cfg.SetDefault("access_key", "")
cfg.SetDefault("secret_key", "")
cfg.SetDefault("create_bucket", true)

cfg.BindEnv("access_key", "AWS_ACCESS_KEY_ID")
cfg.BindEnv("secret_key", "AWS_SECRET_ACCESS_KEY")
Expand All @@ -141,10 +180,18 @@ func NewConfig(configFile string) (*Config, error) {
}
}

if err := cfg.UnmarshalExact(cfg, viper.DecodeHook(mapstructure.TextUnmarshallerHookFunc())); err != nil {
if err := cfg.UnmarshalExact(cfg, func(c *mapstructure.DecoderConfig) {
c.DecodeHook = mapstructure.TextUnmarshallerHookFunc()
c.TagName = "json"
}); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}

// Use the default values as the single server if no others are configured
if len(cfg.Servers) == 0 {
cfg.Servers = append(cfg.Servers, cfg.S3Server)
}

// Some normalization and default values for servers
for i := range cfg.Servers {
svr := &cfg.Servers[i]
Expand Down

0 comments on commit 6e1bd34

Please sign in to comment.