diff --git a/README.md b/README.md index 54f7bb4..358297a 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,16 @@ MySQL server, use that.) Ensure a user in your database is granted SELECT, INSERT, UPDATE, and DELETE privileges to all tables in your new database. -The database URL for this database can be specified on the Amsterdam command line with the `-d` or `--database` parameters, -or with the `AMSTERDAM_DATABASE_URL` environment variable. The URL is specified as `mysql:dsn`, where "dsn" is the complete -datasource name (`user:password@tcp(hostname)/amsterdam?parseTime=true&loc=UTC`). +The database may be specified to Amsterdam with the following command line options or environment variables: + +* Host name: Command line options `-t` or `--database-host`, or environment variable `AMSTERDAM_DATABASE_HOST` +* User name: Command line options `-u` or `--database-user`, or environment variable `AMSTERDAM_DATABASE_USER` +* Password: Command line options `-p` or `--database-password`, or environment variable `AMSTERDAM_DATABASE_PASSWORD` +* Database name: Command line options `-d` or `--database-name`, or environment variable `AMSTERDAM_DATABASE_NAME` + +All these options may also be specified via the configuration file (see below). + +Amsterdam also requires access to a local SMTP server, as it sends out E-mail messages such as account verification, +password reminders, subscribed posts, and messages from conference or community hosts. It may be specified to Amsterdam +with the following command line options or environment variables: + diff --git a/config/config.go b/config/config.go index 48cadd6..4e20245 100644 --- a/config/config.go +++ b/config/config.go @@ -19,8 +19,8 @@ import ( "os" "path/filepath" "reflect" - "strings" + "git.erbosoft.com/amy/amsterdam/util" argparse "github.com/alexflint/go-arg" "github.com/dustin/go-humanize" log "github.com/sirupsen/logrus" @@ -45,8 +45,11 @@ type AmCLI struct { Debug bool `arg:"-D,--debug,env:AMSTERDAM_DEBUG" help:"Force Amsterdam to run in debug mode."` LogLevel string `arg:"-L,--level,--loglevel,env:AMSTERDAM_LOG_LEVEL" help:"Set the log level for the server."` Production bool `arg:"-P,--production,env:AMSTERDAM_PROD" help:"Force Amsterdam to run in production mode."` - DatabaseURL string `arg:"-d,--database,env:AMSTERDAM_DATABASE_URL" help:"Database URL for Amsterdam to connect to."` - Listen string `arg:"-l,--listen,env:AMSTERDAM_LISTEN" help:"Specifies the local address and port for Amsterdam to listen on."` + DatabaseName string `arg:"-d,--database-name,env:AMSTERDAM_DATABASE_NAME" help:"Database name to use on the database server."` + Listen string `arg:"-l,--listen,env:AMSTERDAM_LISTEN" help:"The local address and port for Amsterdam to listen on."` + DatabasePassword string `arg:"-p,--database-password,env:AMSTERDAM_DATABASE_PASSWORD" help:"Password for the database server."` + DatabaseHost string `arg:"-t,--database-host,env:AMSTERDAM_DATABASE_HOST" help:"Hostname for the database server."` + DatabaseUser string `arg:"-u,--database-user,env:AMSTERDAM_DATABASE_USER" help:"User name for the database server."` DebugPanic bool `arg:"--debug-panic" help:"Development Only - disable Echo panic recovery"` BuggyAttachments bool `arg:"--buggy-attachments" help:"Some attachments may be buggy - truncate data if necessary"` } @@ -101,8 +104,11 @@ type AmConfig struct { } `yaml:"rateLimit"` } `yaml:"site"` Database struct { - Driver string `yaml:"driver"` - Dsn string `yaml:"dsn"` + Driver string `yaml:"driver"` + HostName string `yaml:"hostName"` + User string `yaml:"user"` + Password string `yaml:"password"` + DatabaseName string `yaml:"databaseName"` } `yaml:"database"` Defaults struct { Language string `yaml:"language"` @@ -191,7 +197,10 @@ type AmConfigComputed struct { LogLevel string // the logging level Listen string // listen address DatabaseDriver string // name of database driver - DatabaseDSN string // DSN for the database + DatabaseHost string // hostname for database + DatabaseUser string // user name for database + DatabasePassword string // password for database + DatabaseName string // database name UploadMaxSize int32 // maximum upload size in bytes UploadNoCompress map[string]bool // which upload types are not compressed? } @@ -361,27 +370,13 @@ func SetupConfig() { } else { GlobalComputedConfig.DebugMode = !GlobalConfig.Site.Production } - if CommandLine.LogLevel != "" { - GlobalComputedConfig.LogLevel = CommandLine.LogLevel - } else { - GlobalComputedConfig.LogLevel = GlobalConfig.Logging.LogLevel - } - if CommandLine.Listen != "" { - GlobalComputedConfig.Listen = CommandLine.Listen - } else { - GlobalComputedConfig.Listen = GlobalConfig.Site.Listen - } - if CommandLine.DatabaseURL != "" { - p := strings.Index(CommandLine.DatabaseURL, ":") - if p < 0 { - panic("Invalid database URL on command line") - } - GlobalComputedConfig.DatabaseDriver = CommandLine.DatabaseURL[:p] - GlobalComputedConfig.DatabaseDSN = CommandLine.DatabaseURL[p+1:] - } else { - GlobalComputedConfig.DatabaseDriver = GlobalConfig.Database.Driver - GlobalComputedConfig.DatabaseDSN = GlobalConfig.Database.Dsn - } + GlobalComputedConfig.LogLevel = util.IIF(CommandLine.LogLevel != "", CommandLine.LogLevel, GlobalConfig.Logging.LogLevel) + GlobalComputedConfig.Listen = util.IIF(CommandLine.Listen != "", CommandLine.Listen, GlobalConfig.Site.Listen) + GlobalComputedConfig.DatabaseDriver = GlobalConfig.Database.Driver // FUTURE: allow configuration + GlobalComputedConfig.DatabaseHost = util.IIF(CommandLine.DatabaseHost != "", CommandLine.DatabaseHost, GlobalConfig.Database.HostName) + GlobalComputedConfig.DatabaseUser = util.IIF(CommandLine.DatabaseUser != "", CommandLine.DatabaseUser, GlobalConfig.Database.User) + GlobalComputedConfig.DatabasePassword = util.IIF(CommandLine.DatabasePassword != "", CommandLine.DatabasePassword, GlobalConfig.Database.Password) + GlobalComputedConfig.DatabaseName = util.IIF(CommandLine.DatabaseName != "", CommandLine.DatabaseName, GlobalConfig.Database.DatabaseName) tmp, err := humanize.ParseBytes(GlobalConfig.Posting.Uploads.MaxSize) if err != nil { panic(err.Error()) diff --git a/config/default.yaml b/config/default.yaml index 7ff2112..32d1a65 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -36,7 +36,10 @@ site: expireMinutes: 3 database: driver: "mysql" - dsn: "amsdb:x00yes2k@tcp(localhost)/amsterdam?parseTime=true&loc=UTC" + hostName: "localhost" + user: "amsdb" + password: "x00yes2k" + databaseName: "amsterdam" defaults: language: "en-US" timezone: "America/Denver" diff --git a/database/base.go b/database/base.go index 69944b1..20c4ce0 100644 --- a/database/base.go +++ b/database/base.go @@ -11,6 +11,7 @@ package database import ( "context" + "fmt" "slices" "git.erbosoft.com/amy/amsterdam/config" @@ -22,10 +23,19 @@ import ( // amdb is the reference to the Amsterdam database. var amdb *sqlx.DB +// buildMysqlDSN builds the MySQL DSN for the driver. +func buildMysqlDSN() string { + return fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&loc=UTC", + config.GlobalComputedConfig.DatabaseUser, + config.GlobalComputedConfig.DatabasePassword, + config.GlobalComputedConfig.DatabaseHost, + config.GlobalComputedConfig.DatabaseName) +} + // SetupDb sets up the database and associated items. func SetupDb() (func(), error) { exitfns := make([]func(), 0, 2) - db, err := sqlx.Connect(config.GlobalComputedConfig.DatabaseDriver, config.GlobalComputedConfig.DatabaseDSN) + db, err := sqlx.Connect(config.GlobalComputedConfig.DatabaseDriver, buildMysqlDSN()) if err == nil { amdb = db g, err := AmGlobals(context.Background()) diff --git a/ui/templates.go b/ui/templates.go index 2c39ce8..70298f4 100644 --- a/ui/templates.go +++ b/ui/templates.go @@ -321,7 +321,7 @@ func setupTemplates() { views.AddGlobalFunc("DisplayExpandCat", displayExpandCat) views.AddGlobalFunc("GetCountryList", func(jet.Arguments) reflect.Value { - return reflect.ValueOf(util.AmCountryList()) + return reflect.ValueOf(util.AmCountryList(config.GlobalConfig.Rendering.CountryList.Prioritize)) }) views.AddGlobalFunc("GetLanguageList", func(jet.Arguments) reflect.Value { return reflect.ValueOf(util.AmLanguageList()) diff --git a/util/countrylist.go b/util/countrylist.go index 01dbd0d..a473be8 100644 --- a/util/countrylist.go +++ b/util/countrylist.go @@ -15,7 +15,6 @@ import ( "strings" "sync" - "git.erbosoft.com/amy/amsterdam/config" "github.com/biter777/countries" ) @@ -26,7 +25,7 @@ var cachedCountryList []countries.CountryCode = nil var countryListMutex sync.Mutex // AmCountryList is a wrapper around countries.All() that sorts it by country name. -func AmCountryList() []countries.CountryCode { +func AmCountryList(prioritize string) []countries.CountryCode { countryListMutex.Lock() defer countryListMutex.Unlock() if cachedCountryList == nil { @@ -34,9 +33,9 @@ func AmCountryList() []countries.CountryCode { slices.SortFunc(countryList, func(a countries.CountryCode, b countries.CountryCode) int { return strings.Compare(a.Info().Name, b.Info().Name) }) - if config.GlobalConfig.Rendering.CountryList.Prioritize != "" { + if prioritize != "" { for i, c := range countryList { - if c.Info().Alpha2 == config.GlobalConfig.Rendering.CountryList.Prioritize { + if c.Info().Alpha2 == prioritize { newList := make([]countries.CountryCode, len(countryList)) newList[0] = c copy(newList[1:], countryList[:i]) @@ -49,8 +48,3 @@ func AmCountryList() []countries.CountryCode { } return cachedCountryList } - -// init preloads the country list. -func init() { - go AmCountryList() -}