package config import ( "bytes" "fmt" "io/ioutil" "os" "runtime" "github.com/rs/zerolog" "github.com/spf13/viper" ) const ( // Version roughly represents the applications current version. Version = "0.2" // Title is the name of the application used throughout the configuration process. Title = "mapyweb" ) var ( // BannerOnly when toggled causes the appllication to only print the banner and version then exit. BannerOnly = false // GenConfig when toggled causes the application to write its default config to the cwd and then exit. GenConfig = false // NoColor when true will disable the banner and any colored console output. NoColor bool ) // "http" var ( // HTTPBindAddr is defined via our toml configuration file. It is the address that the http server listens on. HTTPBindAddr string // HTTPBindPort is defined via our toml configuration file. It is the port that the http server listens on. HTTPBindPort string // UseUnixSocket when toggled disables the TCP listener and listens on the given UnixSocketPath. UseUnixSocket bool // UnixSocketPath is the path of the unix socket used when UseUnixSocket is toggled. UnixSocketPath = "" // HTTPUseSSL determins whether or not we serve HTTPs HTTPUseSSL bool // SSLCertFile is our fullchain SSL Certificate SSLCertFile string // SSLKeyFile is our SSL Certificate private key SSLKeyFile string ) // "db" var ( UseEmbeddedPostgres bool SQLHost string SQLPort string SQLUsername string SQLPassword string SQLDatabase string DataPath string ) var ( // Filename returns the current location of our toml config file. Filename string ) var ( f *os.File err error noColorForce = false customconfig = false home string configLocations []string ) var ( // Debug is our global debug toggle Debug bool prefConfigLocation string snek *viper.Viper ) func init() { if home, err = os.UserHomeDir(); err != nil { panic(err) } prefConfigLocation = home + "/.config/" + Title snek = viper.New() } func writeConfig() { if runtime.GOOS != "windows" { if _, err := os.Stat(prefConfigLocation); os.IsNotExist(err) { if err = os.Mkdir(prefConfigLocation, 0755); err != nil { println("error writing new config: " + err.Error()) } } newconfig := prefConfigLocation + "/" + "config.toml" if err = snek.SafeWriteConfigAs(newconfig); err != nil { fmt.Println(err.Error()) os.Exit(1) } Filename = newconfig return } newconfig := Title snek.SetConfigName(newconfig) if err = snek.MergeInConfig(); err != nil { if err = snek.SafeWriteConfigAs(newconfig + ".toml"); err != nil { fmt.Println(err.Error()) os.Exit(1) } } Filename = newconfig } // Init will initialize our toml configuration engine and define our default configuration values which can be written to a new configuration file if desired func Init() { snek.SetConfigType("toml") snek.SetConfigName("config") argParse() if customconfig { associate() return } acquireClue() setDefaults() for _, loc := range configLocations { snek.AddConfigPath(loc) } if err = snek.MergeInConfig(); err != nil { writeConfig() } if len(Filename) < 1 { Filename = snek.ConfigFileUsed() } associate() } func setDefaults() { var ( // Add new configuration categories here configSections = []string{"logger", "http", "db"} deflogdir = home + "/.config/" + Title + "/logs/" defNoColor = false ) if runtime.GOOS == "windows" { deflogdir = "logs/" defNoColor = true } Opt := make(map[string]map[string]interface{}) Opt["logger"] = map[string]interface{}{ "debug": true, "directory": deflogdir, "nocolor": defNoColor, "use_date_filename": true, } Opt["http"] = map[string]interface{}{ "use_unix_socket": false, "unix_socket_path": "/var/run/cokeplate", "bind_addr": "127.0.0.1", "bind_port": "8080", } Opt["db"] = map[string]interface{}{ "embedded_pgsql": true, "sql_host": "localhost", "sql_port": "5432", "sql_user": "postgres", "sql_pass": "postgres", "data_path": "./data", } for _, def := range configSections { snek.SetDefault(def, Opt[def]) } if GenConfig { if err = snek.SafeWriteConfigAs("./config.toml"); err != nil { fmt.Println(err.Error()) os.Exit(1) } os.Exit(0) } } func acquireClue() { configLocations = append(configLocations, "./") if runtime.GOOS != "windows" { configLocations = append(configLocations, prefConfigLocation) configLocations = append(configLocations, "/etc/"+Title+"/") configLocations = append(configLocations, "../") configLocations = append(configLocations, "../../") } } func loadCustomConfig(path string) { if f, err = os.Open(path); err != nil { println("Error opening specified config file: " + path) panic("config file open fatal error: " + err.Error()) } buf, err := ioutil.ReadAll(f) err2 := snek.ReadConfig(bytes.NewBuffer(buf)) switch { case err != nil: fmt.Println("config file read fatal error: ", err.Error()) case err2 != nil: fmt.Println("config file read fatal error: ", err2.Error()) default: break } customconfig = true } func printUsage() { println("\n" + Title + " v" + Version + " Usage\n") println("-c - Specify config file") println("--nocolor - disable color and banner ") println("--banner - show banner + version and exit") println("--genconfig - write default config to 'default.toml' then exit") os.Exit(0) } // TODO: should probably just make a proper CLI with flags or something func argParse() { for i, arg := range os.Args { switch arg { case "-h": printUsage() case "--genconfig": GenConfig = true case "--nocolor": noColorForce = true case "--banner": BannerOnly = true case "--config": fallthrough case "-c": if len(os.Args) <= i-1 { panic("syntax error! expected file after -c") } loadCustomConfig(os.Args[i+1]) default: continue } } } func bl(key string) bool { return snek.GetBool(key) } func st(key string) string { return snek.GetString(key) } func sl(key string) []string { return snek.GetStringSlice(key) } func it(key string) int { return snek.GetInt(key) } func associate() { HTTPBindAddr = st("http.bind_addr") HTTPBindPort = st("http.bind_port") UseUnixSocket = bl("http.use_unix_socket") SQLPassword = st("db.sql_password") SQLUsername = st("db.sql_username") SQLDatabase = st("db.sql_database") SQLHost = st("db.sql_host") SQLPort = st("db.sql_port") DataPath = st("db.data_path") Debug = bl("logger.debug") logDir = st("logger.directory") NoColor = bl("logger.nocolor") if noColorForce { NoColor = true } // if UseUnixSocket { // UnixSocketPath = snek.GetString("http.unix_socket_path") // } if Debug { zerolog.SetGlobalLevel(zerolog.DebugLevel) } }