2020-09-29 19:55:07 +03:00
|
|
|
package config
|
2019-05-05 23:36:49 +03:00
|
|
|
|
2020-05-24 18:32:26 +03:00
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-10-04 11:37:38 +03:00
|
|
|
"flag"
|
2020-11-01 22:14:10 +03:00
|
|
|
"fmt"
|
2020-05-24 18:32:26 +03:00
|
|
|
"github.com/gorilla/securecookie"
|
2020-10-04 11:37:38 +03:00
|
|
|
"github.com/jinzhu/configor"
|
2020-09-29 19:55:07 +03:00
|
|
|
"github.com/muety/wakapi/models"
|
2020-05-30 21:41:27 +03:00
|
|
|
migrate "github.com/rubenv/sql-migrate"
|
2020-11-01 22:14:10 +03:00
|
|
|
"gorm.io/driver/mysql"
|
|
|
|
"gorm.io/driver/postgres"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
|
|
"gorm.io/gorm"
|
2020-05-24 18:32:26 +03:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2020-10-04 12:14:44 +03:00
|
|
|
const (
|
|
|
|
defaultConfigPath = "config.yml"
|
|
|
|
defaultConfigPathLegacy = "config.ini"
|
|
|
|
defaultEnvConfigPathLegacy = ".env"
|
2020-11-06 19:20:26 +03:00
|
|
|
|
|
|
|
SQLDialectMysql = "mysql"
|
|
|
|
SQLDialectPostgres = "postgres"
|
|
|
|
SQLDialectSqlite = "sqlite3"
|
2020-10-04 12:14:44 +03:00
|
|
|
)
|
|
|
|
|
2020-11-08 12:12:49 +03:00
|
|
|
var cfg *Config
|
|
|
|
var cFlag = flag.String("config", defaultConfigPath, "config file location")
|
2020-05-24 14:41:19 +03:00
|
|
|
|
2020-10-04 12:14:44 +03:00
|
|
|
type appConfig struct {
|
2020-10-16 17:21:13 +03:00
|
|
|
AggregationTime string `yaml:"aggregation_time" default:"02:15" env:"WAKAPI_AGGREGATION_TIME"`
|
2020-10-04 12:14:44 +03:00
|
|
|
CustomLanguages map[string]string `yaml:"custom_languages"`
|
|
|
|
LanguageColors map[string]string `yaml:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type securityConfig struct {
|
|
|
|
// this is actually a pepper (https://en.wikipedia.org/wiki/Pepper_(cryptography))
|
|
|
|
PasswordSalt string `yaml:"password_salt" default:"" env:"WAKAPI_PASSWORD_SALT"`
|
|
|
|
InsecureCookies bool `yaml:"insecure_cookies" default:"false" env:"WAKAPI_INSECURE_COOKIES"`
|
|
|
|
SecureCookie *securecookie.SecureCookie `yaml:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type dbConfig struct {
|
|
|
|
Host string `env:"WAKAPI_DB_HOST"`
|
|
|
|
Port uint `env:"WAKAPI_DB_PORT"`
|
|
|
|
User string `env:"WAKAPI_DB_USER"`
|
|
|
|
Password string `env:"WAKAPI_DB_PASSWORD"`
|
|
|
|
Name string `default:"wakapi_db.db" env:"WAKAPI_DB_NAME"`
|
|
|
|
Dialect string `default:"sqlite3" env:"WAKAPI_DB_TYPE"`
|
|
|
|
MaxConn uint `yaml:"max_conn" default:"2" env:"WAKAPI_DB_MAX_CONNECTIONS"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type serverConfig struct {
|
2020-10-04 12:35:23 +03:00
|
|
|
Port int `default:"3000" env:"WAKAPI_PORT"`
|
|
|
|
ListenIpV4 string `yaml:"listen_ipv4" default:"127.0.0.1" env:"WAKAPI_LISTEN_IPV4"`
|
|
|
|
BasePath string `yaml:"base_path" default:"/" env:"WAKAPI_BASE_PATH"`
|
2020-10-04 12:14:44 +03:00
|
|
|
}
|
|
|
|
|
2019-05-05 23:36:49 +03:00
|
|
|
type Config struct {
|
2020-10-04 12:14:44 +03:00
|
|
|
Env string `default:"dev" env:"ENVIRONMENT"`
|
|
|
|
Version string `yaml:"-"`
|
|
|
|
App appConfig
|
|
|
|
Security securityConfig
|
|
|
|
Db dbConfig
|
|
|
|
Server serverConfig
|
2019-05-05 23:36:49 +03:00
|
|
|
}
|
2020-02-20 17:39:56 +03:00
|
|
|
|
|
|
|
func (c *Config) IsDev() bool {
|
2020-05-30 13:11:21 +03:00
|
|
|
return IsDev(c.Env)
|
|
|
|
}
|
|
|
|
|
2020-09-29 19:55:07 +03:00
|
|
|
func (c *Config) GetMigrationFunc(dbDialect string) models.MigrationFunc {
|
2020-05-30 21:41:27 +03:00
|
|
|
switch dbDialect {
|
|
|
|
default:
|
|
|
|
return func(db *gorm.DB) error {
|
2020-09-29 19:55:07 +03:00
|
|
|
db.AutoMigrate(&models.User{})
|
2020-11-03 12:02:39 +03:00
|
|
|
db.AutoMigrate(&models.KeyStringValue{})
|
|
|
|
db.AutoMigrate(&models.Alias{})
|
2020-11-01 22:14:10 +03:00
|
|
|
db.AutoMigrate(&models.Heartbeat{})
|
2020-11-03 12:02:39 +03:00
|
|
|
db.AutoMigrate(&models.Summary{})
|
2020-11-01 22:14:10 +03:00
|
|
|
db.AutoMigrate(&models.SummaryItem{})
|
|
|
|
db.AutoMigrate(&models.LanguageMapping{})
|
2020-05-30 21:41:27 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 19:55:07 +03:00
|
|
|
func (c *Config) GetFixturesFunc(dbDialect string) models.MigrationFunc {
|
2020-05-30 21:41:27 +03:00
|
|
|
return func(db *gorm.DB) error {
|
|
|
|
migrations := &migrate.FileMigrationSource{
|
|
|
|
Dir: "migrations/common/fixtures",
|
|
|
|
}
|
|
|
|
|
|
|
|
migrate.SetIgnoreUnknown(true)
|
2020-11-01 22:14:10 +03:00
|
|
|
sqlDb, _ := db.DB()
|
|
|
|
n, err := migrate.Exec(sqlDb, dbDialect, migrations, migrate.Up)
|
2020-05-30 21:41:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("applied %d fixtures\n", n)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-01 22:14:10 +03:00
|
|
|
func (c *dbConfig) GetDialector() gorm.Dialector {
|
|
|
|
switch c.Dialect {
|
2020-11-06 19:20:26 +03:00
|
|
|
case SQLDialectMysql:
|
2020-11-01 22:14:10 +03:00
|
|
|
return mysql.New(mysql.Config{
|
|
|
|
DriverName: c.Dialect,
|
|
|
|
DSN: mysqlConnectionString(c),
|
|
|
|
})
|
2020-11-06 19:20:26 +03:00
|
|
|
case SQLDialectPostgres:
|
2020-11-01 22:14:10 +03:00
|
|
|
return postgres.New(postgres.Config{
|
2020-11-03 10:30:11 +03:00
|
|
|
DSN: postgresConnectionString(c),
|
2020-11-01 22:14:10 +03:00
|
|
|
})
|
2020-11-06 19:20:26 +03:00
|
|
|
case SQLDialectSqlite:
|
2020-11-01 22:14:10 +03:00
|
|
|
return sqlite.Open(sqliteConnectionString(c))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func mysqlConnectionString(config *dbConfig) string {
|
|
|
|
//location, _ := time.LoadLocation("Local")
|
|
|
|
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=true&loc=%s&sql_mode=ANSI_QUOTES",
|
|
|
|
config.User,
|
|
|
|
config.Password,
|
|
|
|
config.Host,
|
|
|
|
config.Port,
|
|
|
|
config.Name,
|
|
|
|
"Local",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func postgresConnectionString(config *dbConfig) string {
|
|
|
|
return fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=disable",
|
|
|
|
config.Host,
|
|
|
|
config.Port,
|
|
|
|
config.User,
|
|
|
|
config.Name,
|
|
|
|
config.Password,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func sqliteConnectionString(config *dbConfig) string {
|
|
|
|
return config.Name
|
|
|
|
}
|
|
|
|
|
2020-05-30 13:11:21 +03:00
|
|
|
func IsDev(env string) bool {
|
|
|
|
return env == "dev" || env == "development"
|
2020-02-20 17:39:56 +03:00
|
|
|
}
|
2020-05-24 18:32:26 +03:00
|
|
|
|
2020-05-24 22:42:15 +03:00
|
|
|
func readVersion() string {
|
|
|
|
file, err := os.Open("version.txt")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
bytes, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(bytes)
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:37:38 +03:00
|
|
|
func readLanguageColors() map[string]string {
|
2020-05-24 18:32:26 +03:00
|
|
|
// Read language colors
|
|
|
|
// Source: https://raw.githubusercontent.com/ozh/github-colors/master/colors.json
|
|
|
|
var colors = make(map[string]string)
|
|
|
|
var rawColors map[string]struct {
|
|
|
|
Color string `json:"color"`
|
|
|
|
Url string `json:"url"`
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := ioutil.ReadFile("data/colors.json")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &rawColors); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range rawColors {
|
|
|
|
colors[strings.ToLower(k)] = v.Color
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:37:38 +03:00
|
|
|
return colors
|
|
|
|
}
|
|
|
|
|
|
|
|
func mustReadConfigLocation() string {
|
|
|
|
if _, err := os.Stat(*cFlag); err != nil {
|
|
|
|
log.Fatalf("failed to find config file at '%s'\n", *cFlag)
|
|
|
|
}
|
|
|
|
|
|
|
|
return *cFlag
|
|
|
|
}
|
|
|
|
|
|
|
|
func Set(config *Config) {
|
|
|
|
cfg = config
|
|
|
|
}
|
|
|
|
|
|
|
|
func Get() *Config {
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
func Load() *Config {
|
|
|
|
config := &Config{}
|
|
|
|
|
2020-11-08 12:12:49 +03:00
|
|
|
flag.Parse()
|
|
|
|
|
2020-10-04 12:14:44 +03:00
|
|
|
maybeMigrateLegacyConfig()
|
|
|
|
|
2020-10-04 11:37:38 +03:00
|
|
|
if err := configor.New(&configor.Config{}).Load(config, mustReadConfigLocation()); err != nil {
|
|
|
|
log.Fatalf("failed to read config: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
config.Version = readVersion()
|
|
|
|
config.App.LanguageColors = readLanguageColors()
|
|
|
|
config.Security.SecureCookie = securecookie.New(
|
2020-05-24 18:32:26 +03:00
|
|
|
securecookie.GenerateRandomKey(64),
|
|
|
|
securecookie.GenerateRandomKey(32),
|
|
|
|
)
|
|
|
|
|
2020-10-04 11:37:38 +03:00
|
|
|
if strings.HasSuffix(config.Server.BasePath, "/") {
|
|
|
|
config.Server.BasePath = config.Server.BasePath[:len(config.Server.BasePath)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range config.App.CustomLanguages {
|
|
|
|
if v == "" {
|
|
|
|
config.App.CustomLanguages[k] = "unknown"
|
|
|
|
}
|
|
|
|
}
|
2020-09-29 19:55:07 +03:00
|
|
|
|
2020-10-04 11:37:38 +03:00
|
|
|
Set(config)
|
2020-09-29 19:55:07 +03:00
|
|
|
return Get()
|
2020-05-24 18:32:26 +03:00
|
|
|
}
|