Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions config-dist.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Example Titlechange_bot's configuration file
# Documentation can be found in internal/config/model.go

# Bot

#command-prefix: "!"

# Misc

#supinic-api-key: ""

# API

#base-url: ""
#bind-address: "localhost:2558"

# Twitch

#twitch-login: ""
#twitch-oauth: ""
#twitch-client-id: ""
#twitch-client-secret: ""
#twitch-eventsub-secret: ""

# Mongo 🥭

#mongo-username: ""
#mongo-password: ""
#mongo-port: "2017"
#mongo-database-name: "tcb2"
#mongo-auth-db: "admin"
13 changes: 1 addition & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,20 @@ require (
github.com/gempir/go-twitch-irc/v4 v4.4.1
github.com/go-chi/chi/v5 v5.2.5
github.com/nicklaw5/helix/v2 v2.32.0
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
go.mongodb.org/mongo-driver v1.17.9
go.yaml.in/yaml/v4 v4.0.0-rc.4
)

require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/montanaflynn/stats v0.8.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
41 changes: 2 additions & 39 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gempir/go-twitch-irc/v4 v4.4.1 h1:R1WxeDyOiwHpt6rn96yZcXTS+Bri30n7pNvIjTMH598=
github.com/gempir/go-twitch-irc/v4 v4.4.1/go.mod h1:QsOMMAk470uxQ7EYD9GJBGAVqM/jDrXBNbuePfTauzg=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
Expand All @@ -18,34 +12,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/montanaflynn/stats v0.8.2 h1:52wnefTJnPI5FoHif1DQh2soKRw0yYs+4AVyvtcZCH0=
github.com/montanaflynn/stats v0.8.2/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/nicklaw5/helix/v2 v2.32.0 h1:ZRPt+wRUMQqpny6yZKVY9rUGNwv+ZmIh75fSiopMXuY=
github.com/nicklaw5/helix/v2 v2.32.0/go.mod h1:KaXa2mb2kBzsDana9RbXevTgnfU95DMoSORWo2hqlWA=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
Expand All @@ -57,8 +27,8 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
Expand All @@ -76,8 +46,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -90,8 +58,3 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
128 changes: 17 additions & 111 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,125 +1,31 @@
package config

import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.yaml.in/yaml/v4"
)

const (
appName = "tcb2"
configName = "config"
)

// readFromPath reads the config values from the given path (i.e. path/${configName}.yaml) and returns its values as a map.
// This allows us to use mergeConfig cleanly
func readFromPath(path string) (values map[string]interface{}, err error) {
v := viper.New()
v.SetConfigName(configName)
v.SetConfigType("yaml")
v.AddConfigPath(path)

if err = v.ReadInConfig(); err != nil {
notFoundError := &viper.ConfigFileNotFoundError{}
if errors.As(err, notFoundError) {
err = nil
return
}
return
func New() *TCBConfig {
// Read the config file
data, err := os.ReadFile("config.yaml")
if err != nil {
panic(fmt.Errorf("failed to read config file: %w", err))
}

_ = v.Unmarshal(&values)

return
}

// mergeConfig uses viper.MergeConfigMap to read config values in the unix
// standard, so you start furthest down with reading the system config file,
// merge those values into the main config map, then read the home directory
// config files, and merge any set values from there, and lastly the config
// file in the cwd and merge those in. If a value is not set in the cwd config
// file, but one is set in the system config file then the system config file
// value will be used
func mergeConfig(v *viper.Viper, configPaths []string) {
for _, configPath := range configPaths {
configMap, err := readFromPath(configPath)
if err != nil {
fmt.Printf("Error reading config file from %s.yaml: %s\n", filepath.Join(configPath, configName), err)
return
}
_ = v.MergeConfigMap(configMap)
// Parse the config file (initialize the object with default values)
cfg := &TCBConfig{
CommandPrefix: "!",
BindAddress: "localhost:2558",
MongoPort: "27017",
MongoDatabaseName: "tcb2",
MongoAuthDB: "admin",
}
}

func init() {
// Define command-line flags and default values

// Bot
pflag.String("command-prefix", "!", "Command prefix to which bot will respond to")

// Misc
pflag.String("supinic-api-key", "", `titlechange_bot's key to the Supinic's API, in the format: "SupibotID:APIKey" without quotation marks`)

// API
pflag.StringP("base-url", "b", "", "Base URL of the API to which clients will make their requests. Useful if the API is proxied through reverse proxy like nginx. Value needs to contain full URL with protocol scheme, e.g. https://braize.pajlada.com/chatterino")
pflag.StringP("bind-address", "l", ":2558", "Address to which API will bind and start listening on")

// Twitch
pflag.String("twitch-login", "titlechange_bot", "Twitch login of the account on which bot will Log in to Twitch IRC")
pflag.String("twitch-oauth", "", `OAuth token of the account on which bot will Log in to IRC. Should not have the "oauth:" part in the beginning.`)
pflag.String("twitch-client-id", "", "Twitch Client ID")
pflag.String("twitch-client-secret", "", "Twitch Client secret")
pflag.String("twitch-eventsub-secret", "", "Twitch EventSub secret used to create subscriptions and verify incoming notifications. Must be between 10 and 100 characters long")

// Mongo 🥭
pflag.String("mongo-username", "", "Username for the MongoDB user")
pflag.String("mongo-password", "", "Password for the MongoDB user")
pflag.String("mongo-port", "27017", "Port to which connection will try to connect. Note that you can only connect to localhost due to security concerns (use ssh port tunneling while testing/developing)")
pflag.String("mongo-database-name", "tcb2", "Name of the database that should be used by the bot")
pflag.String("mongo-auth-db", "admin", "Name of the authentication database, used as AuthSource while creating a new mongo.Connection. This should usually be left unchanged")

pflag.Parse()
}

func New() (cfg *TCBConfig) {
v := viper.New()

_ = v.BindPFlags(pflag.CommandLine)

// figure out XDG_DATA_CONFIG to be compliant with the standard
xdgConfigHome, exists := os.LookupEnv("XDG_CONFIG_HOME")
if !exists || xdgConfigHome == "" {
// on Windows, we use appdata since that's the closest equivalent
if runtime.GOOS == "windows" {
xdgConfigHome = "$APPDATA"
} else {
xdgConfigHome = filepath.Join("$HOME", ".config")
}
err = yaml.Unmarshal(data, &cfg)
if err != nil {
panic(fmt.Errorf("failed to unmarshal config file: %w", err))
}

// config paths to read from, in order of least importance
var configPaths []string
if runtime.GOOS != "windows" {
configPaths = append(configPaths, filepath.Join("/etc", appName))
}
configPaths = append(configPaths, filepath.Join(xdgConfigHome, appName))
configPaths = append(configPaths, ".")

mergeConfig(v, configPaths)

// Environment
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.SetEnvPrefix(appName)
v.AutomaticEnv()

_ = v.UnmarshalExact(&cfg)

// fmt.Printf("%# v\n", cfg) // uncomment for debugging purposes
return
return cfg
}
76 changes: 48 additions & 28 deletions internal/config/model.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
package config

type TCBConfig struct {
// Bot

CommandPrefix string `mapstructure:"command-prefix"`

// Misc

SupinicAPIKey string `mapstructure:"supinic-api-key"`

// API

BaseURL string `mapstructure:"base-url"`
BindAddress string `mapstructure:"bind-address"`

// Twitch

TwitchLogin string `mapstructure:"twitch-login"`
TwitchOAuth string `mapstructure:"twitch-oauth"`
TwitchClientID string `mapstructure:"twitch-client-id"`
TwitchClientSecret string `mapstructure:"twitch-client-secret"`
TwitchEventSubSecret string `mapstructure:"twitch-eventsub-secret"`

// Mongo 🥭

MongoUsername string `mapstructure:"mongo-username"`
MongoPassword string `mapstructure:"mongo-password"`
MongoPort string `mapstructure:"mongo-port"`
MongoDatabaseName string `mapstructure:"mongo-database-name"`
MongoAuthDB string `mapstructure:"mongo-auth-db"`
/// Bot

// CommandPrefix prefix to which bot will respond to (chat commands)
CommandPrefix string `yaml:"command-prefix"`

/// Misc

// SupinicAPIKey bot's key to the Supinic's API, in the format: "SupibotID:APIKey" without quotation marks
SupinicAPIKey string `yaml:"supinic-api-key"`

/// API

// BaseURL url of the API to which clients will make their requests. Useful if the API is proxied through reverse proxy like nginx.
// Its value needs to contain full URL with protocol scheme, e.g. https://braize.pajlada.com/chatterino
BaseURL string `yaml:"base-url"`
// BindAddress address to which API will bind and start listening on
BindAddress string `yaml:"bind-address"`

/// Twitch

// TwitchLogin login name of the account on which bot will log into Twitch's IRC on the Write connection
TwitchLogin string `yaml:"twitch-login"`
// TwitchOAuth OAuth token of the account on which bot will log into Twitch's IRC on the Write connection
// It should not have the "oauth:" prefix - that is added once bot attempts to authenticate
TwitchOAuth string `yaml:"twitch-oauth"`
// TwitchClientID
TwitchClientID string `yaml:"twitch-client-id"`
// TwitchClientSecret
TwitchClientSecret string `yaml:"twitch-client-secret"`
// TwitchEventSubSecret secret used to create subscriptions and verify incoming notifications
// Must be between 10 and 100 characters long
TwitchEventSubSecret string `yaml:"twitch-eventsub-secret"`

/// Mongo 🥭

// MongoUsername
MongoUsername string `yaml:"mongo-username"`
// MongoPassword
MongoPassword string `yaml:"mongo-password"`
// MongoPort port to which connection will try to connect
// Host is hardcoded to localhost due to security concerns (use ssh port tunneling while testing/developing on a remote machine)
MongoPort string `yaml:"mongo-port"`
// MongoDatabaseNamename name of the database used by the bot
// It's treated as a prefix to allow using separate databases while testing/developing
MongoDatabaseName string `yaml:"mongo-database-name"`
// MongoAuthDB name of authentication databse, used as AuthSource while creating a new mongo.Connection
// This should usually be left unchanged
MongoAuthDB string `yaml:"mongo-auth-db"`
}
Loading