2019-05-06 23:18:10 +02:00
|
|
|
package config
|
2018-11-17 06:21:39 +01:00
|
|
|
|
|
|
|
import (
|
2019-05-04 05:25:00 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"reflect"
|
|
|
|
|
2018-11-17 06:21:39 +01:00
|
|
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
2019-05-06 23:18:10 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/util"
|
2018-11-17 06:21:39 +01:00
|
|
|
"github.com/urfave/cli"
|
2019-05-04 05:25:00 +02:00
|
|
|
"gopkg.in/yaml.v2"
|
2018-11-17 06:21:39 +01:00
|
|
|
)
|
|
|
|
|
2018-12-18 18:38:30 +01:00
|
|
|
const (
|
2018-12-21 08:44:13 +01:00
|
|
|
DbTiDB = "internal"
|
2018-12-18 18:38:30 +01:00
|
|
|
DbMySQL = "mysql"
|
|
|
|
)
|
|
|
|
|
2019-05-06 23:18:10 +02:00
|
|
|
// Params provides a struct in which application configuration is stored.
|
2018-11-17 06:21:39 +01:00
|
|
|
// Application code must use functions to get config values, for two reasons:
|
|
|
|
//
|
|
|
|
// 1. Some values are computed and we don't want to leak implementation details (aims at reducing refactoring overhead).
|
|
|
|
//
|
|
|
|
// 2. Paths might actually be dynamic later (if we build a multi-user version).
|
|
|
|
//
|
|
|
|
// See https://github.com/photoprism/photoprism/issues/50#issuecomment-433856358
|
2019-05-06 23:18:10 +02:00
|
|
|
type Params struct {
|
2019-05-03 18:57:28 +02:00
|
|
|
Name string
|
|
|
|
Version string
|
|
|
|
Copyright string
|
2019-05-04 05:25:00 +02:00
|
|
|
Debug bool `yaml:"debug" flag:"debug"`
|
2019-05-04 09:11:33 +02:00
|
|
|
ReadOnly bool `yaml:"read-only" flag:"read-only"`
|
2019-11-11 21:10:41 +01:00
|
|
|
Public bool `yaml:"public" flag:"public"`
|
2019-11-08 06:53:40 +01:00
|
|
|
AdminPassword string `yaml:"admin-password" flag:"admin-password"`
|
2019-05-04 05:25:00 +02:00
|
|
|
LogLevel string `yaml:"log-level" flag:"log-level"`
|
2019-05-03 18:57:28 +02:00
|
|
|
ConfigFile string
|
2019-06-05 18:25:20 +02:00
|
|
|
ConfigPath string `yaml:"config-path" flag:"config-path"`
|
2019-05-04 05:25:00 +02:00
|
|
|
AssetsPath string `yaml:"assets-path" flag:"assets-path"`
|
2019-06-05 18:25:20 +02:00
|
|
|
ResourcesPath string `yaml:"resources-path" flag:"resources-path"`
|
2019-05-04 05:25:00 +02:00
|
|
|
CachePath string `yaml:"cache-path" flag:"cache-path"`
|
|
|
|
OriginalsPath string `yaml:"originals-path" flag:"originals-path"`
|
|
|
|
ImportPath string `yaml:"import-path" flag:"import-path"`
|
|
|
|
ExportPath string `yaml:"export-path" flag:"export-path"`
|
|
|
|
SqlServerHost string `yaml:"sql-host" flag:"sql-host"`
|
|
|
|
SqlServerPort uint `yaml:"sql-port" flag:"sql-port"`
|
|
|
|
SqlServerPath string `yaml:"sql-path" flag:"sql-path"`
|
|
|
|
SqlServerPassword string `yaml:"sql-password" flag:"sql-password"`
|
|
|
|
HttpServerHost string `yaml:"http-host" flag:"http-host"`
|
|
|
|
HttpServerPort int `yaml:"http-port" flag:"http-port"`
|
|
|
|
HttpServerMode string `yaml:"http-mode" flag:"http-mode"`
|
|
|
|
HttpServerPassword string `yaml:"http-password" flag:"http-password"`
|
|
|
|
DatabaseDriver string `yaml:"database-driver" flag:"database-driver"`
|
|
|
|
DatabaseDsn string `yaml:"database-dsn" flag:"database-dsn"`
|
2019-06-06 14:44:29 +02:00
|
|
|
SipsBin string `yaml:"sips-bin" flag:"sips-bin"`
|
|
|
|
DarktableBin string `yaml:"darktable-bin" flag:"darktable-bin"`
|
|
|
|
ExifToolBin string `yaml:"exiftool-bin" flag:"exiftool-bin"`
|
|
|
|
HeifConvertBin string `yaml:"heifconvert-bin" flag:"heifconvert-bin"`
|
2019-06-30 05:38:39 +02:00
|
|
|
PIDFilename string `yaml:"pid-filename" flag:"pid-filename"`
|
|
|
|
LogFilename string `yaml:"log-filename" flag:"log-filename"`
|
|
|
|
DetachServer bool `yaml:"detach-server" flag:"detach-server"`
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-06 23:18:10 +02:00
|
|
|
// NewParams() creates a new configuration entity by using two methods:
|
2019-05-04 05:25:00 +02:00
|
|
|
//
|
|
|
|
// 1. SetValuesFromFile: This will initialize values from a yaml config file.
|
|
|
|
//
|
|
|
|
// 2. SetValuesFromCliContext: Which comes after SetValuesFromFile and overrides
|
|
|
|
// any previous values giving an option two override file configs through the CLI.
|
2019-05-06 23:18:10 +02:00
|
|
|
func NewParams(ctx *cli.Context) *Params {
|
|
|
|
c := &Params{}
|
2018-12-26 11:40:01 +01:00
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
c.Name = ctx.App.Name
|
|
|
|
c.Copyright = ctx.App.Copyright
|
|
|
|
c.Version = ctx.App.Version
|
2019-06-30 05:38:39 +02:00
|
|
|
c.ConfigFile = util.ExpandedFilename(ctx.GlobalString("config-file"))
|
2018-11-17 06:21:39 +01:00
|
|
|
|
2019-06-30 05:38:39 +02:00
|
|
|
if err := c.SetValuesFromFile(c.ConfigFile); err != nil {
|
2019-05-04 05:25:00 +02:00
|
|
|
log.Debug(err)
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
if err := c.SetValuesFromCliContext(ctx); err != nil {
|
|
|
|
log.Error(err)
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
c.expandFilenames()
|
2018-11-17 06:21:39 +01:00
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
return c
|
|
|
|
}
|
2018-11-17 06:21:39 +01:00
|
|
|
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Params) expandFilenames() {
|
2019-06-05 18:25:20 +02:00
|
|
|
c.ConfigPath = util.ExpandedFilename(c.ConfigPath)
|
|
|
|
c.ResourcesPath = util.ExpandedFilename(c.ResourcesPath)
|
2019-05-06 23:18:10 +02:00
|
|
|
c.AssetsPath = util.ExpandedFilename(c.AssetsPath)
|
|
|
|
c.CachePath = util.ExpandedFilename(c.CachePath)
|
|
|
|
c.OriginalsPath = util.ExpandedFilename(c.OriginalsPath)
|
|
|
|
c.ImportPath = util.ExpandedFilename(c.ImportPath)
|
|
|
|
c.ExportPath = util.ExpandedFilename(c.ExportPath)
|
|
|
|
c.SqlServerPath = util.ExpandedFilename(c.SqlServerPath)
|
2019-06-30 05:38:39 +02:00
|
|
|
c.PIDFilename = util.ExpandedFilename(c.PIDFilename)
|
|
|
|
c.LogFilename = util.ExpandedFilename(c.LogFilename)
|
2019-05-04 05:25:00 +02:00
|
|
|
}
|
2018-11-17 06:21:39 +01:00
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
// SetValuesFromFile uses a yaml config file to initiate the configuration entity.
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Params) SetValuesFromFile(fileName string) error {
|
|
|
|
if !util.Exists(fileName) {
|
2019-05-04 05:25:00 +02:00
|
|
|
return errors.New(fmt.Sprintf("config file not found: \"%s\"", fileName))
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
yamlConfig, err := ioutil.ReadFile(fileName)
|
2018-11-17 06:21:39 +01:00
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 05:25:00 +02:00
|
|
|
return yaml.Unmarshal(yamlConfig, c)
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetValuesFromCliContext uses values from the CLI to setup configuration overrides
|
|
|
|
// for the entity.
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Params) SetValuesFromCliContext(ctx *cli.Context) error {
|
2019-05-04 05:25:00 +02:00
|
|
|
v := reflect.ValueOf(c).Elem()
|
|
|
|
|
|
|
|
// Iterate through all config fields
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
|
|
fieldValue := v.Field(i)
|
|
|
|
|
|
|
|
tagValue := v.Type().Field(i).Tag.Get("flag")
|
|
|
|
|
|
|
|
// Automatically assign values to fields with "flag" tag
|
|
|
|
if tagValue != "" {
|
|
|
|
switch t := fieldValue.Interface().(type) {
|
|
|
|
case int, int64:
|
|
|
|
// Only if explicitly set or current value is empty (use default)
|
2019-06-30 05:38:39 +02:00
|
|
|
if ctx.IsSet(tagValue) {
|
|
|
|
f := ctx.Int64(tagValue)
|
|
|
|
fieldValue.SetInt(f)
|
|
|
|
} else if ctx.GlobalIsSet(tagValue) || fieldValue.Int() == 0 {
|
2019-05-04 05:25:00 +02:00
|
|
|
f := ctx.GlobalInt64(tagValue)
|
|
|
|
fieldValue.SetInt(f)
|
|
|
|
}
|
|
|
|
case uint, uint64:
|
|
|
|
// Only if explicitly set or current value is empty (use default)
|
2019-06-30 05:38:39 +02:00
|
|
|
if ctx.IsSet(tagValue) {
|
|
|
|
f := ctx.Uint64(tagValue)
|
|
|
|
fieldValue.SetUint(f)
|
|
|
|
} else if ctx.GlobalIsSet(tagValue) || fieldValue.Uint() == 0 {
|
2019-05-04 05:25:00 +02:00
|
|
|
f := ctx.GlobalUint64(tagValue)
|
|
|
|
fieldValue.SetUint(f)
|
|
|
|
}
|
|
|
|
case string:
|
|
|
|
// Only if explicitly set or current value is empty (use default)
|
2019-06-30 05:38:39 +02:00
|
|
|
if ctx.IsSet(tagValue) {
|
|
|
|
f := ctx.String(tagValue)
|
|
|
|
fieldValue.SetString(f)
|
|
|
|
} else if ctx.GlobalIsSet(tagValue) || fieldValue.String() == "" {
|
2019-05-04 05:25:00 +02:00
|
|
|
f := ctx.GlobalString(tagValue)
|
|
|
|
fieldValue.SetString(f)
|
|
|
|
}
|
|
|
|
case bool:
|
2019-06-30 05:38:39 +02:00
|
|
|
if ctx.IsSet(tagValue) {
|
|
|
|
f := ctx.Bool(tagValue)
|
|
|
|
fieldValue.SetBool(f)
|
|
|
|
} else if ctx.GlobalIsSet(tagValue) {
|
2019-05-04 05:25:00 +02:00
|
|
|
f := ctx.GlobalBool(tagValue)
|
|
|
|
fieldValue.SetBool(f)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
log.Warnf("can't assign value of type %s from cli flag %s", t, tagValue)
|
|
|
|
}
|
|
|
|
}
|
2018-11-17 06:21:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|