refactor: migrate to new config (resolve #54)

This commit is contained in:
Ferdinand Mütsch 2020-10-04 10:37:38 +02:00
parent 2ecbb3ea02
commit 660fefcca9
16 changed files with 154 additions and 156 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ wakapi
.idea
build
*.exe
*.db
*.db
config.yml

25
config.default.yml Normal file
View File

@ -0,0 +1,25 @@
env: development
server:
listen: 127.0.0.1
port: 3000
base_path: /
app:
cleanup: false # only edit, if you know what you're doing
custom_languages:
vue: Vue
jsx: JSX
db:
host: # leave blank when using sqlite3
port: # leave blank when using sqlite3
user: # leave blank when using sqlite3
password: # leave blank when using sqlite3
name: wakapi_db.db # database name for mysql / postgres or file path for sqlite (e.g. /tmp/wakapi.db)
dialect: sqlite3 # mysql, postgres, sqlite3
max_conn: 2
security:
password_salt: # CHANGE !
insecure_cookies: false

View File

@ -2,43 +2,48 @@ package config
import (
"encoding/json"
"flag"
"github.com/gorilla/securecookie"
"github.com/jinzhu/configor"
"github.com/jinzhu/gorm"
"github.com/joho/godotenv"
"github.com/muety/wakapi/models"
migrate "github.com/rubenv/sql-migrate"
"gopkg.in/ini.v1"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
)
var cfg *Config
type Config struct {
Env string
Version string
Port int
Addr string
BasePath string
DbHost string
DbPort uint
DbUser string
DbPassword string
DbName string
DbDialect string
DbMaxConn uint
CleanUp bool
// this is actually a pepper (https://en.wikipedia.org/wiki/Pepper_(cryptography))
PasswordSalt string
SecureCookieHashKey string
SecureCookieBlockKey string
InsecureCookies bool
CustomLanguages map[string]string
LanguageColors map[string]string
SecureCookie *securecookie.SecureCookie
Env string `default:"dev" env:"ENVIRONMENT"`
Version string
App struct {
CleanUp bool `default:"false" env:"WAKAPI_CLEANUP"`
CustomLanguages map[string]string `yaml:"custom_languages"`
LanguageColors map[string]string
}
Security 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
}
Db 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_PORT"`
Dialect string `default:"sqlite3" env:"WAKAPI_DB_TYPE"`
MaxConn uint `yaml:"max_conn" default:"2" env:"WAKAPI_DB_MAX_CONNECTIONS"`
}
Server struct {
Port int `default:"3000" env:"WAKAPI_PORT"`
Addr string `default:"127.0.0.1" env:"WAKAPI_LISTEN_IPV4"`
BasePath string `yaml:"base_path" default:"/" env:"WAKAPI_BASE_PATH"`
}
}
func (c *Config) IsDev() bool {
@ -120,66 +125,7 @@ func readVersion() string {
return string(bytes)
}
func Set(config *Config) {
cfg = config
}
func Get() *Config {
return cfg
}
func Load() *Config {
if err := godotenv.Load(); err != nil {
log.Fatal(err)
}
version := readVersion()
env := LookupFatal("ENV")
dbType := LookupFatal("WAKAPI_DB_TYPE")
dbUser := LookupFatal("WAKAPI_DB_USER")
dbPassword := LookupFatal("WAKAPI_DB_PASSWORD")
dbHost := LookupFatal("WAKAPI_DB_HOST")
dbName := LookupFatal("WAKAPI_DB_NAME")
dbPortStr := LookupFatal("WAKAPI_DB_PORT")
passwordSalt := LookupFatal("WAKAPI_PASSWORD_SALT")
dbPort, err := strconv.Atoi(dbPortStr)
cfg, err := ini.Load("config.ini")
if err != nil {
log.Fatalf("Fail to read file: %v", err)
}
if dbType == "" {
dbType = "mysql"
}
dbMaxConn := cfg.Section("database").Key("max_connections").MustUint(1)
addr := cfg.Section("server").Key("listen").MustString("127.0.0.1")
insecureCookies := IsDev(env) || cfg.Section("server").Key("insecure_cookies").MustBool(false)
port, err := strconv.Atoi(os.Getenv("PORT"))
if err != nil {
port = cfg.Section("server").Key("port").MustInt()
}
basePathEnv, basePathEnvExists := os.LookupEnv("WAKAPI_BASE_PATH")
basePath := cfg.Section("server").Key("base_path").MustString("/")
if basePathEnvExists {
basePath = basePathEnv
}
if strings.HasSuffix(basePath, "/") {
basePath = basePath[:len(basePath)-1]
}
cleanUp := cfg.Section("app").Key("cleanup").MustBool(false)
// Read custom languages
customLangs := make(map[string]string)
languageKeys := cfg.Section("languages").Keys()
for _, k := range languageKeys {
customLangs[k.Name()] = k.MustString("unknown")
}
func readLanguageColors() map[string]string {
// Read language colors
// Source: https://raw.githubusercontent.com/ozh/github-colors/master/colors.json
var colors = make(map[string]string)
@ -201,32 +147,54 @@ func Load() *Config {
colors[strings.ToLower(k)] = v.Color
}
return colors
}
func mustReadConfigLocation() string {
var cFlag = flag.String("c", "config.yml", "config file location")
flag.Parse()
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{}
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()
// TODO: Read keys from env, so that users are not logged out every time the server is restarted
secureCookie := securecookie.New(
config.Security.SecureCookie = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32),
)
Set(&Config{
Env: env,
Version: version,
Port: port,
Addr: addr,
BasePath: basePath,
DbHost: dbHost,
DbPort: uint(dbPort),
DbUser: dbUser,
DbPassword: dbPassword,
DbName: dbName,
DbDialect: dbType,
DbMaxConn: dbMaxConn,
CleanUp: cleanUp,
InsecureCookies: insecureCookies,
SecureCookie: secureCookie,
PasswordSalt: passwordSalt,
CustomLanguages: customLangs,
LanguageColors: colors,
})
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"
}
}
Set(config)
return Get()
}

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/gorilla/schema v1.1.0
github.com/gorilla/securecookie v1.1.1
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4
github.com/jinzhu/configor v1.2.0
github.com/jinzhu/gorm v1.9.11
github.com/joho/godotenv v1.3.0
github.com/kr/pretty v0.2.0 // indirect

3
go.sum
View File

@ -1,6 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@ -154,6 +155,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4 h1:UbQcOUL8J8EpnhYmLa2v6y5PSOPEdRRSVQxh7imPjHg=
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4/go.mod h1:1nXLkt6gXojCECs34KL3+LlZ3gTpZlkPUA8ejW3WeP0=
github.com/jinzhu/configor v1.2.0 h1:u78Jsrxw2+3sGbGMgpY64ObKU4xWCNmNRJIjGVqxYQA=
github.com/jinzhu/configor v1.2.0/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=

20
main.go
View File

@ -47,19 +47,19 @@ func main() {
}
// Show data loss warning
if config.CleanUp {
if config.App.CleanUp {
promptAbort("`CLEANUP` is set to `true`, which may cause data loss. Are you sure to continue?", 5)
}
// Connect to database
var err error
db, err = gorm.Open(config.DbDialect, utils.MakeConnectionString(config))
if config.DbDialect == "sqlite3" {
db, err = gorm.Open(config.Db.Dialect, utils.MakeConnectionString(config))
if config.Db.Dialect == "sqlite3" {
db.DB().Exec("PRAGMA foreign_keys = ON;")
}
db.LogMode(config.IsDev())
db.DB().SetMaxIdleConns(int(config.DbMaxConn))
db.DB().SetMaxOpenConns(int(config.DbMaxConn))
db.DB().SetMaxIdleConns(int(config.Db.MaxConn))
db.DB().SetMaxOpenConns(int(config.Db.MaxConn))
if err != nil {
log.Println(err)
log.Fatal("could not connect to database")
@ -85,7 +85,7 @@ func main() {
// Aggregate heartbeats to summaries and persist them
go aggregationService.Schedule()
if config.CleanUp {
if config.App.CleanUp {
go heartbeatService.ScheduleCleanUp()
}
@ -159,7 +159,7 @@ func main() {
router.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./static")))
// Listen HTTP
portString := config.Addr + ":" + strconv.Itoa(config.Port)
portString := config.Server.Addr + ":" + strconv.Itoa(config.Server.Port)
s := &http.Server{
Handler: router,
Addr: portString,
@ -171,19 +171,19 @@ func main() {
}
func runDatabaseMigrations() {
if err := config.GetMigrationFunc(config.DbDialect)(db); err != nil {
if err := config.GetMigrationFunc(config.Db.Dialect)(db); err != nil {
log.Fatal(err)
}
}
func applyFixtures() {
if err := config.GetFixturesFunc(config.DbDialect)(db); err != nil {
if err := config.GetFixturesFunc(config.Db.Dialect)(db); err != nil {
log.Fatal(err)
}
}
func migrateLanguages() {
for k, v := range config.CustomLanguages {
for k, v := range config.App.CustomLanguages {
result := db.Model(models.Heartbeat{}).
Where("language = ?", "").
Where("entity LIKE ?", "%."+k).

View File

@ -58,8 +58,8 @@ func (m *AuthenticateMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Reques
if strings.HasPrefix(r.URL.Path, "/api") {
w.WriteHeader(http.StatusUnauthorized)
} else {
utils.ClearCookie(w, models.AuthCookieKey, !m.config.InsecureCookies)
http.Redirect(w, r, fmt.Sprintf("%s/?error=unauthorized", m.config.BasePath), http.StatusFound)
utils.ClearCookie(w, models.AuthCookieKey, !m.config.Security.InsecureCookies)
http.Redirect(w, r, fmt.Sprintf("%s/?error=unauthorized", m.config.Server.BasePath), http.StatusFound)
}
return
}
@ -107,7 +107,7 @@ func (m *AuthenticateMiddleware) tryGetUserByCookie(r *http.Request) (*models.Us
return nil, err
}
if !CheckAndMigratePassword(user, login, m.config.PasswordSalt, m.userSrvc) {
if !CheckAndMigratePassword(user, login, m.config.Security.PasswordSalt, m.userSrvc) {
return nil, errors.New("invalid password")
}

View File

@ -47,7 +47,7 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) {
hb.Machine = machineName
hb.User = user
hb.UserID = user.ID
hb.Augment(h.config.CustomLanguages)
hb.Augment(h.config.App.CustomLanguages)
if !hb.Valid() {
w.WriteHeader(http.StatusBadRequest)

View File

@ -36,7 +36,7 @@ func (h *IndexHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
}
if cookie, err := r.Cookie(models.AuthCookieKey); err == nil && cookie.Value != "" {
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.Server.BasePath), http.StatusFound)
return
}
@ -68,7 +68,7 @@ func (h *IndexHandler) PostLogin(w http.ResponseWriter, r *http.Request) {
}
if cookie, err := r.Cookie(models.AuthCookieKey); err == nil && cookie.Value != "" {
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.Server.BasePath), http.StatusFound)
return
}
@ -89,12 +89,12 @@ func (h *IndexHandler) PostLogin(w http.ResponseWriter, r *http.Request) {
}
// TODO: depending on middleware package here is a hack
if !middlewares.CheckAndMigratePassword(user, &login, h.config.PasswordSalt, h.userSrvc) {
if !middlewares.CheckAndMigratePassword(user, &login, h.config.Security.PasswordSalt, h.userSrvc) {
respondAlert(w, "invalid credentials", "", "", http.StatusUnauthorized)
return
}
encoded, err := h.config.SecureCookie.Encode(models.AuthCookieKey, login)
encoded, err := h.config.Security.SecureCookie.Encode(models.AuthCookieKey, login)
if err != nil {
respondAlert(w, "internal server error", "", "", http.StatusInternalServerError)
return
@ -107,11 +107,11 @@ func (h *IndexHandler) PostLogin(w http.ResponseWriter, r *http.Request) {
Name: models.AuthCookieKey,
Value: encoded,
Path: "/",
Secure: !h.config.InsecureCookies,
Secure: !h.config.Security.InsecureCookies,
HttpOnly: true,
}
http.SetCookie(w, cookie)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.Server.BasePath), http.StatusFound)
}
func (h *IndexHandler) PostLogout(w http.ResponseWriter, r *http.Request) {
@ -119,8 +119,8 @@ func (h *IndexHandler) PostLogout(w http.ResponseWriter, r *http.Request) {
loadTemplates()
}
utils.ClearCookie(w, models.AuthCookieKey, !h.config.InsecureCookies)
http.Redirect(w, r, fmt.Sprintf("%s/", h.config.BasePath), http.StatusFound)
utils.ClearCookie(w, models.AuthCookieKey, !h.config.Security.InsecureCookies)
http.Redirect(w, r, fmt.Sprintf("%s/", h.config.Server.BasePath), http.StatusFound)
}
func (h *IndexHandler) GetSignup(w http.ResponseWriter, r *http.Request) {
@ -129,7 +129,7 @@ func (h *IndexHandler) GetSignup(w http.ResponseWriter, r *http.Request) {
}
if cookie, err := r.Cookie(models.AuthCookieKey); err == nil && cookie.Value != "" {
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.Server.BasePath), http.StatusFound)
return
}
@ -146,7 +146,7 @@ func (h *IndexHandler) PostSignup(w http.ResponseWriter, r *http.Request) {
}
if cookie, err := r.Cookie(models.AuthCookieKey); err == nil && cookie.Value != "" {
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/summary", h.config.Server.BasePath), http.StatusFound)
return
}
@ -176,5 +176,5 @@ func (h *IndexHandler) PostSignup(w http.ResponseWriter, r *http.Request) {
}
msg := url.QueryEscape("account created successfully")
http.Redirect(w, r, fmt.Sprintf("%s/?success=%s", h.config.BasePath, msg), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/?success=%s", h.config.Server.BasePath, msg), http.StatusFound)
}

View File

@ -25,7 +25,7 @@ func loadTemplates() {
"title": strings.Title,
"capitalize": utils.Capitalize,
"getBasePath": func() string {
return config.Get().BasePath
return config.Get().Server.BasePath
},
"getVersion": func() string {
return config.Get().Version

View File

@ -59,7 +59,7 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
return
}
if !utils.CheckPasswordBcrypt(user, credentials.PasswordOld, h.config.PasswordSalt) {
if !utils.CheckPasswordBcrypt(user, credentials.PasswordOld, h.config.Security.PasswordSalt) {
respondAlert(w, "invalid credentials", "", "settings.tpl.html", http.StatusUnauthorized)
return
}
@ -70,7 +70,7 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
}
user.Password = credentials.PasswordNew
if err := utils.HashPassword(user, h.config.PasswordSalt); err != nil {
if err := utils.HashPassword(user, h.config.Security.PasswordSalt); err != nil {
respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError)
return
}
@ -84,7 +84,7 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
Username: user.ID,
Password: user.Password,
}
encoded, err := h.config.SecureCookie.Encode(models.AuthCookieKey, login)
encoded, err := h.config.Security.SecureCookie.Encode(models.AuthCookieKey, login)
if err != nil {
respondAlert(w, "internal server error", "", "settings.tpl.html", http.StatusInternalServerError)
return
@ -94,13 +94,13 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
Name: models.AuthCookieKey,
Value: encoded,
Path: "/",
Secure: !h.config.InsecureCookies,
Secure: !h.config.Security.InsecureCookies,
HttpOnly: true,
}
http.SetCookie(w, cookie)
msg := url.QueryEscape("password was updated successfully")
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.BasePath, msg), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound)
}
func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request) {
@ -115,7 +115,7 @@ func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request
}
msg := url.QueryEscape(fmt.Sprintf("your new api key is: %s", user.ApiKey))
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.BasePath, msg), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound)
}
func (h *SettingsHandler) PostToggleBadges(w http.ResponseWriter, r *http.Request) {
@ -130,5 +130,5 @@ func (h *SettingsHandler) PostToggleBadges(w http.ResponseWriter, r *http.Reques
return
}
http.Redirect(w, r, fmt.Sprintf("%s/settings", h.config.BasePath), http.StatusFound)
http.Redirect(w, r, fmt.Sprintf("%s/settings", h.config.Server.BasePath), http.StatusFound)
}

View File

@ -56,7 +56,7 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
vm := models.SummaryViewModel{
Summary: summary,
LanguageColors: utils.FilterLanguageColors(h.config.LanguageColors, summary),
LanguageColors: utils.FilterLanguageColors(h.config.App.LanguageColors, summary),
ApiKey: user.ApiKey,
}

View File

@ -51,7 +51,7 @@ func (srv *AggregationService) Schedule() {
go srv.summaryWorker(jobs, summaries)
}
for i := 0; i < int(srv.Config.DbMaxConn); i++ {
for i := 0; i < int(srv.Config.Db.MaxConn); i++ {
go srv.persistWorker(summaries)
}

View File

@ -54,7 +54,7 @@ func (srv *UserService) CreateOrGet(signup *models.Signup) (*models.User, bool,
Password: signup.Password,
}
if err := utils.HashPassword(u, srv.Config.PasswordSalt); err != nil {
if err := utils.HashPassword(u, srv.Config.Security.PasswordSalt); err != nil {
return nil, false, err
}
@ -103,7 +103,7 @@ func (srv *UserService) ToggleBadges(user *models.User) (*models.User, error) {
func (srv *UserService) MigrateMd5Password(user *models.User, login *models.Login) (*models.User, error) {
user.Password = login.Password
if err := utils.HashPassword(user, srv.Config.PasswordSalt); err != nil {
if err := utils.HashPassword(user, srv.Config.Security.PasswordSalt); err != nil {
return nil, err
}

View File

@ -52,7 +52,7 @@ func ExtractCookieAuth(r *http.Request, config *config.Config) (login *models.Lo
return nil, errors.New("missing authentication")
}
if err := config.SecureCookie.Decode(models.AuthCookieKey, cookie.Value, &login); err != nil {
if err := config.Security.SecureCookie.Decode(models.AuthCookieKey, cookie.Value, &login); err != nil {
return nil, errors.New("invalid parameters")
}

View File

@ -30,7 +30,7 @@ func ParseUserAgent(ua string) (string, string, error) {
}
func MakeConnectionString(config *config.Config) string {
switch config.DbDialect {
switch config.Db.Dialect {
case "mysql":
return mysqlConnectionString(config)
case "postgres":
@ -44,25 +44,25 @@ func MakeConnectionString(config *config.Config) string {
func mysqlConnectionString(config *config.Config) 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.DbUser,
config.DbPassword,
config.DbHost,
config.DbPort,
config.DbName,
config.Db.User,
config.Db.Password,
config.Db.Host,
config.Db.Port,
config.Db.Name,
"Local",
)
}
func postgresConnectionString(config *config.Config) string {
return fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=disable",
config.DbHost,
config.DbPort,
config.DbUser,
config.DbName,
config.DbPassword,
config.Db.Host,
config.Db.Port,
config.Db.User,
config.Db.Name,
config.Db.Password,
)
}
func sqliteConnectionString(config *config.Config) string {
return config.DbName
return config.Db.Name
}