diff --git a/config/config.go b/config/config.go index d0d1e82..0ceb11c 100644 --- a/config/config.go +++ b/config/config.go @@ -3,11 +3,15 @@ package config import ( "encoding/json" "flag" + "fmt" "github.com/gorilla/securecookie" "github.com/jinzhu/configor" - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" migrate "github.com/rubenv/sql-migrate" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/gorm" "io/ioutil" "log" "os" @@ -75,31 +79,16 @@ func (c *Config) IsDev() bool { func (c *Config) GetMigrationFunc(dbDialect string) models.MigrationFunc { switch dbDialect { - case "sqlite3": - return func(db *gorm.DB) error { - migrations := &migrate.FileMigrationSource{ - Dir: "migrations/sqlite3", - } - - migrate.SetIgnoreUnknown(true) - n, err := migrate.Exec(db.DB(), "sqlite3", migrations, migrate.Up) - if err != nil { - return err - } - - log.Printf("applied %d migrations\n", n) - return nil - } default: return func(db *gorm.DB) error { db.AutoMigrate(&models.Alias{}) db.AutoMigrate(&models.Summary{}) db.AutoMigrate(&models.SummaryItem{}) db.AutoMigrate(&models.User{}) - db.AutoMigrate(&models.Heartbeat{}).AddForeignKey("user_id", "users(id)", "RESTRICT", "RESTRICT") - db.AutoMigrate(&models.SummaryItem{}).AddForeignKey("summary_id", "summaries(id)", "CASCADE", "CASCADE") + db.AutoMigrate(&models.Heartbeat{}) + db.AutoMigrate(&models.SummaryItem{}) db.AutoMigrate(&models.KeyStringValue{}) - db.AutoMigrate(&models.CustomRule{}) + db.AutoMigrate(&models.LanguageMapping{}) return nil } } @@ -112,7 +101,8 @@ func (c *Config) GetFixturesFunc(dbDialect string) models.MigrationFunc { } migrate.SetIgnoreUnknown(true) - n, err := migrate.Exec(db.DB(), dbDialect, migrations, migrate.Up) + sqlDb, _ := db.DB() + n, err := migrate.Exec(sqlDb, dbDialect, migrations, migrate.Up) if err != nil { return err } @@ -122,6 +112,50 @@ func (c *Config) GetFixturesFunc(dbDialect string) models.MigrationFunc { } } +func (c *dbConfig) GetDialector() gorm.Dialector { + switch c.Dialect { + case "mysql": + return mysql.New(mysql.Config{ + DriverName: c.Dialect, + DSN: mysqlConnectionString(c), + }) + case "postgres": + return postgres.New(postgres.Config{ + DriverName: c.Dialect, + DSN: mysqlConnectionString(c), + }) + case "sqlite3": + 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 +} + func IsDev(env string) bool { return env == "dev" || env == "development" } diff --git a/go.mod b/go.mod index 3f824a8..8897ffc 100644 --- a/go.mod +++ b/go.mod @@ -9,14 +9,19 @@ require ( 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 + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc github.com/satori/go.uuid v1.2.0 - github.com/t-tiger/gorm-bulk-insert v1.3.0 - golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/ini.v1 v1.50.0 - gopkg.in/yaml.v2 v2.2.5 + gopkg.in/yaml.v2 v2.2.8 + gorm.io/driver/mysql v1.0.3 + gorm.io/driver/postgres v1.0.5 + gorm.io/driver/sqlite v1.1.3 + gorm.io/gorm v1.20.5 ) diff --git a/go.sum b/go.sum index 4c60bb1..6e0c243 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ 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= @@ -32,21 +31,24 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0 h1:epsH3lb7KVbXHYk7LYGN5EiE0MxcevHU85CKITJ0wUY= github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -57,8 +59,6 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= @@ -72,8 +72,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= @@ -85,6 +86,8 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -95,7 +98,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -107,11 +109,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -153,16 +152,66 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U= +github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.5 h1:NUbEWPmCQZbMmYlTjVoNPhc0CfnYyz2bfUAh6A5ZVJM= +github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.5.0 h1:jzBqRk2HFG2CV4AIwgCI2PwTgm6UUoCAK2ofHHRirtc= +github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.9.0 h1:6STjDqppM2ROy5p1wNDcsC7zJTjSHeuCsguZmXyzx7c= +github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 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= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -170,7 +219,6 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -181,29 +229,38 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -226,6 +283,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -255,6 +314,7 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -283,6 +343,9 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc h1:+2DdDcxVYlarHjYcZTt8dZ4Ec8cXZirzL5ko0mkKPjU= github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -292,8 +355,12 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -315,20 +382,19 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/t-tiger/gorm-bulk-insert v0.0.0-20191014134946-beb77b81825f h1:Op5lFYUNE7tPxu6gJfwkgY8HMIWpLqiLApBJfGs71U8= -github.com/t-tiger/gorm-bulk-insert v0.0.0-20191014134946-beb77b81825f/go.mod h1:SK1RZT4TR1aMUNGtbk6YxTPgx2D/gfbxB571QGnAV+c= -github.com/t-tiger/gorm-bulk-insert v1.3.0 h1:9k7BaVEhw/3fsvh6GTOBwJ2RXk3asc5xs5m6hwozq20= -github.com/t-tiger/gorm-bulk-insert v1.3.0/go.mod h1:ruDlk8xDl+8sX4bA7PQuYly9YEb3pbp1eP2LCyeRrFY= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -337,10 +403,14 @@ go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -348,12 +418,16 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -397,19 +471,24 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -419,24 +498,27 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -451,14 +533,16 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.50.0 h1:c/4YI/GUgB7d2yOkxdsQyYDhW67nWrTl6Zyd9vagYmg= gopkg.in/ini.v1 v1.50.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -467,11 +551,21 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg= +gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= +gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw= +gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA= +gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc= +gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= +gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.5 h1:g3tpSF9kggASzReK+Z3dYei1IJODLqNUbOjSuCczY8g= +gorm.io/gorm v1.20.5/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/main.go b/main.go index 4424357..99c15ca 100644 --- a/main.go +++ b/main.go @@ -11,16 +11,15 @@ import ( "time" "github.com/gorilla/mux" - "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/mysql" - _ "github.com/jinzhu/gorm/dialects/postgres" - _ "github.com/jinzhu/gorm/dialects/sqlite" "github.com/muety/wakapi/middlewares" "github.com/muety/wakapi/routes" shieldsV1Routes "github.com/muety/wakapi/routes/compat/shields/v1" wtV1Routes "github.com/muety/wakapi/routes/compat/wakatime/v1" "github.com/muety/wakapi/services" - "github.com/muety/wakapi/utils" + _ "gorm.io/driver/mysql" + _ "gorm.io/driver/postgres" + _ "gorm.io/driver/sqlite" + "gorm.io/gorm" ) var ( @@ -29,22 +28,22 @@ var ( ) var ( - aliasRepository *repositories.AliasRepository - heartbeatRepository *repositories.HeartbeatRepository - userRepository *repositories.UserRepository - customRuleRepository *repositories.CustomRuleRepository - summaryRepository *repositories.SummaryRepository - keyValueRepository *repositories.KeyValueRepository + aliasRepository *repositories.AliasRepository + heartbeatRepository *repositories.HeartbeatRepository + userRepository *repositories.UserRepository + languageMappingRepository *repositories.LanguageMappingRepository + summaryRepository *repositories.SummaryRepository + keyValueRepository *repositories.KeyValueRepository ) var ( - aliasService *services.AliasService - heartbeatService *services.HeartbeatService - userService *services.UserService - customRuleService *services.CustomRuleService - summaryService *services.SummaryService - aggregationService *services.AggregationService - keyValueService *services.KeyValueService + aliasService *services.AliasService + heartbeatService *services.HeartbeatService + userService *services.UserService + languageMappingService *services.LanguageMappingService + summaryService *services.SummaryService + aggregationService *services.AggregationService + keyValueService *services.KeyValueService ) // TODO: Refactor entire project to be structured after business domains @@ -64,37 +63,39 @@ func main() { // Connect to database var err error - db, err = gorm.Open(config.Db.Dialect, utils.MakeConnectionString(config)) + db, err = gorm.Open(config.Db.GetDialector(), &gorm.Config{}) if config.Db.Dialect == "sqlite3" { - db.DB().Exec("PRAGMA foreign_keys = ON;") + db.Raw("PRAGMA foreign_keys = ON;") } - db.LogMode(config.IsDev()) - db.DB().SetMaxIdleConns(int(config.Db.MaxConn)) - db.DB().SetMaxOpenConns(int(config.Db.MaxConn)) + + sqlDb, _ := db.DB() + sqlDb.SetMaxIdleConns(int(config.Db.MaxConn)) + sqlDb.SetMaxOpenConns(int(config.Db.MaxConn)) if err != nil { log.Println(err) log.Fatal("could not connect to database") } - defer db.Close() + defer sqlDb.Close() // Migrate database schema + common.RunCustomPreMigrations(db, config) runDatabaseMigrations() - runCustomMigrations() + common.RunCustomPostMigrations(db, config) // Repositories aliasRepository = repositories.NewAliasRepository(db) heartbeatRepository = repositories.NewHeartbeatRepository(db) userRepository = repositories.NewUserRepository(db) - customRuleRepository = repositories.NewCustomRuleRepository(db) + languageMappingRepository = repositories.NewLanguageMappingRepository(db) summaryRepository = repositories.NewSummaryRepository(db) keyValueRepository = repositories.NewKeyValueRepository(db) // Services aliasService = services.NewAliasService(aliasRepository) - heartbeatService = services.NewHeartbeatService(heartbeatRepository) userService = services.NewUserService(userRepository) - customRuleService = services.NewCustomRuleService(customRuleRepository) - summaryService = services.NewSummaryService(summaryRepository, heartbeatService, aliasService, customRuleService) + languageMappingService = services.NewLanguageMappingService(languageMappingRepository) + heartbeatService = services.NewHeartbeatService(heartbeatRepository, languageMappingService) + summaryService = services.NewSummaryService(summaryRepository, heartbeatService, aliasService) aggregationService = services.NewAggregationService(userService, summaryService, heartbeatService) keyValueService = services.NewKeyValueService(keyValueRepository) @@ -110,8 +111,8 @@ func main() { // Handlers summaryHandler := routes.NewSummaryHandler(summaryService) healthHandler := routes.NewHealthHandler(db) - heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService, customRuleService) - settingsHandler := routes.NewSettingsHandler(userService, customRuleService) + heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService, languageMappingService) + settingsHandler := routes.NewSettingsHandler(userService, languageMappingService) publicHandler := routes.NewIndexHandler(userService, keyValueService) wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(summaryService) wakatimeV1SummariesHandler := wtV1Routes.NewSummariesHandler(summaryService) @@ -156,8 +157,8 @@ func main() { // Settings Routes settingsRouter.Methods(http.MethodGet).HandlerFunc(settingsHandler.GetIndex) settingsRouter.Path("/credentials").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCredentials) - settingsRouter.Path("/customrules").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCreateCustomRule) - settingsRouter.Path("/customrules/delete").Methods(http.MethodPost).HandlerFunc(settingsHandler.DeleteCustomRule) + settingsRouter.Path("/language_mappings").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostCreateLanguageMapping) + settingsRouter.Path("/language_mappings/delete").Methods(http.MethodPost).HandlerFunc(settingsHandler.DeleteLanguageMapping) settingsRouter.Path("/reset").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostResetApiKey) settingsRouter.Path("/badges").Methods(http.MethodPost).HandlerFunc(settingsHandler.PostToggleBadges) @@ -194,11 +195,6 @@ func runDatabaseMigrations() { } } -func runCustomMigrations() { - common.ApplyFixtures(db) - common.MigrateLanguages(db) -} - func promptAbort(message string, timeoutSec int) { log.Printf("[WARNING] %s.\nTo abort server startup, press Ctrl+C.\n", message) for i := timeoutSec; i > 0; i-- { diff --git a/migrations/common/common.go b/migrations/common/common.go new file mode 100644 index 0000000..3cc0c99 --- /dev/null +++ b/migrations/common/common.go @@ -0,0 +1,11 @@ +package common + +import ( + "github.com/muety/wakapi/config" + "gorm.io/gorm" +) + +type migrationFunc struct { + f func(db *gorm.DB, cfg *config.Config) error + name string +} diff --git a/migrations/common/custom_post.go b/migrations/common/custom_post.go new file mode 100644 index 0000000..531720d --- /dev/null +++ b/migrations/common/custom_post.go @@ -0,0 +1,30 @@ +package common + +import ( + "github.com/muety/wakapi/config" + "gorm.io/gorm" + "log" +) + +var customPostMigrations []migrationFunc + +func init() { + customPostMigrations = []migrationFunc{ + { + f: func(db *gorm.DB, cfg *config.Config) error { + return cfg.GetFixturesFunc(cfg.Db.Dialect)(db) + }, + name: "apply fixtures", + }, + // TODO: add function to modify aggregated summaries according to configured custom language mappings + } +} + +func RunCustomPostMigrations(db *gorm.DB, cfg *config.Config) { + for _, m := range customPostMigrations { + log.Printf("running migration '%s'\n", m.name) + if err := m.f(db, cfg); err != nil { + log.Fatalf("migration '%s' failed – %v\n", m.name, err) + } + } +} diff --git a/migrations/common/custom_pre.go b/migrations/common/custom_pre.go new file mode 100644 index 0000000..4b41586 --- /dev/null +++ b/migrations/common/custom_pre.go @@ -0,0 +1,33 @@ +package common + +import ( + "github.com/muety/wakapi/config" + "github.com/muety/wakapi/models" + "gorm.io/gorm" + "log" +) + +var customPreMigrations []migrationFunc + +func init() { + customPreMigrations = []migrationFunc{ + { + f: func(db *gorm.DB, cfg *config.Config) error { + if db.Migrator().HasTable("custom_rules") { + return db.Migrator().RenameTable("custom_rules", &models.LanguageMapping{}) + } + return nil + }, + name: "rename language mappings table", + }, + } +} + +func RunCustomPreMigrations(db *gorm.DB, cfg *config.Config) { + for _, m := range customPreMigrations { + log.Printf("running migration '%s'\n", m.name) + if err := m.f(db, cfg); err != nil { + log.Fatalf("migration '%s' failed – %v\n", m.name, err) + } + } +} diff --git a/migrations/common/fixtures.go b/migrations/common/fixtures.go deleted file mode 100644 index 7db2484..0000000 --- a/migrations/common/fixtures.go +++ /dev/null @@ -1,15 +0,0 @@ -package common - -import ( - "github.com/jinzhu/gorm" - "github.com/muety/wakapi/config" - "log" -) - -func ApplyFixtures(db *gorm.DB) { - cfg := config.Get() - - if err := cfg.GetFixturesFunc(cfg.Db.Dialect)(db); err != nil { - log.Fatal(err) - } -} diff --git a/migrations/common/languages.go b/migrations/common/languages.go index ed63a7c..3556a35 100644 --- a/migrations/common/languages.go +++ b/migrations/common/languages.go @@ -1,9 +1,9 @@ package common import ( - "github.com/jinzhu/gorm" "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" + "gorm.io/gorm" "log" ) diff --git a/migrations/sqlite3/1_initial.sql b/migrations/sqlite3/1_initial.sql deleted file mode 100644 index 2957de4..0000000 --- a/migrations/sqlite3/1_initial.sql +++ /dev/null @@ -1,87 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied -create table aliases -( - id integer primary key autoincrement, - type integer not null, - user_id varchar(255) not null, - key varchar(255) not null, - value varchar(255) not null -); - -create index idx_alias_type_key - on aliases (type, key); - -create index idx_alias_user - on aliases (user_id); - -create table summaries -( - id integer primary key autoincrement, - user_id varchar(255) not null, - from_time timestamp default CURRENT_TIMESTAMP not null, - to_time timestamp default CURRENT_TIMESTAMP not null -); - -create index idx_time_summary_user - on summaries (user_id, from_time, to_time); - -create table summary_items -( - id integer primary key autoincrement, - summary_id integer REFERENCES summaries (id) ON DELETE CASCADE ON UPDATE CASCADE, - type integer, - key varchar(255), - total bigint -); - -create table users -( - id varchar(255) primary key, - api_key varchar(255) unique, - password varchar(255) -); - -create table heartbeats -( - id integer primary key autoincrement, - user_id varchar(255) not null REFERENCES users (id) ON DELETE RESTRICT ON UPDATE RESTRICT, - entity varchar(255) not null, - type varchar(255), - category varchar(255), - project varchar(255), - branch varchar(255), - language varchar(255), - is_write bool, - editor varchar(255), - operating_system varchar(255), - time timestamp default CURRENT_TIMESTAMP -); - -create index idx_entity - on heartbeats (entity); - -create index idx_language - on heartbeats (language); - -create index idx_time - on heartbeats (time); - -create index idx_time_user - on heartbeats (user_id, time); - - - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back -DROP INDEX idx_alias_user; -DROP INDEX idx_alias_type_key; -DROP TABLE aliases; -DROP INDEX idx_time_summary_user; -DROP TABLE summaries; -DROP TABLE summary_items; -DROP TABLE heartbeats; -DROP INDEX idx_entity; -DROP INDEX idx_language; -DROP INDEX idx_time; -DROP INDEX idx_time_user; \ No newline at end of file diff --git a/migrations/sqlite3/2_key_value_table.sql b/migrations/sqlite3/2_key_value_table.sql deleted file mode 100644 index 1535732..0000000 --- a/migrations/sqlite3/2_key_value_table.sql +++ /dev/null @@ -1,11 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied -create table key_string_values -( - key varchar(255) primary key, - value text -); - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back -drop table key_string_value; \ No newline at end of file diff --git a/migrations/sqlite3/3_user_creation_date.sql b/migrations/sqlite3/3_user_creation_date.sql deleted file mode 100644 index 8450d8e..0000000 --- a/migrations/sqlite3/3_user_creation_date.sql +++ /dev/null @@ -1,20 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied - --- SQLite does not allow altering a table to add a new column with default of CURRENT_TIMESTAMP --- See https://www.sqlite.org/lang_altertable.html - -alter table users - add `created_at` timestamp default '2020-01-01T00:00:00.000' not null; - -alter table users - add `last_logged_in_at` timestamp default '2020-01-01T00:00:00.000' not null; - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back - -alter table users - drop column `created_at`; - -alter table users - drop column `last_logged_in_at`; \ No newline at end of file diff --git a/migrations/sqlite3/4_machine_column.sql b/migrations/sqlite3/4_machine_column.sql deleted file mode 100644 index 4d090e1..0000000 --- a/migrations/sqlite3/4_machine_column.sql +++ /dev/null @@ -1,11 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied - -alter table heartbeats - add column `machine` varchar(255); - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back - -alter table heartbeats - drop column `machine`; \ No newline at end of file diff --git a/migrations/sqlite3/5_badges_column.sql b/migrations/sqlite3/5_badges_column.sql deleted file mode 100644 index 5fa2f32..0000000 --- a/migrations/sqlite3/5_badges_column.sql +++ /dev/null @@ -1,11 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied - -alter table users - add column `badges_enabled` tinyint(1) default 0 not null; - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back - -alter table users - drop column `badges_enabled`; \ No newline at end of file diff --git a/migrations/sqlite3/6_customrule_table.sql b/migrations/sqlite3/6_customrule_table.sql deleted file mode 100644 index 6977e6a..0000000 --- a/migrations/sqlite3/6_customrule_table.sql +++ /dev/null @@ -1,15 +0,0 @@ --- +migrate Up --- SQL in section 'Up' is executed when this migration is applied - -create table custom_rules -( - id integer primary key autoincrement, - user_id varchar(255) not null REFERENCES users (id) ON DELETE RESTRICT ON UPDATE RESTRICT, - extension varchar(255), - language varchar(255) -); - --- +migrate Down --- SQL section 'Down' is executed when this migration is rolled back - -DROP TABLE custom_rules; diff --git a/models/heartbeat.go b/models/heartbeat.go index 1da10e6..f9e434b 100644 --- a/models/heartbeat.go +++ b/models/heartbeat.go @@ -1,7 +1,6 @@ package models import ( - "fmt" "regexp" "time" ) @@ -20,7 +19,7 @@ type Heartbeat struct { Editor string `json:"editor"` OperatingSystem string `json:"operating_system"` Machine string `json:"machine"` - Time CustomTime `json:"time" gorm:"type:timestamp(3); default:CURRENT_TIMESTAMP(3); index:idx_time,idx_time_user"` + Time CustomTime `json:"time" gorm:"type:timestamp; default:CURRENT_TIMESTAMP; index:idx_time,idx_time_user"` languageRegex *regexp.Regexp } @@ -28,30 +27,17 @@ func (h *Heartbeat) Valid() bool { return h.User != nil && h.UserID != "" && h.Time != CustomTime(time.Time{}) } -func (h *Heartbeat) AugmentWithConfigRules(customLangs map[string]string) { - if h.Language == "" { - if h.languageRegex == nil { - h.languageRegex = regexp.MustCompile(`^.+\.(.+)$`) - } - groups := h.languageRegex.FindAllStringSubmatch(h.Entity, -1) - if len(groups) == 0 || len(groups[0]) != 2 { - return - } - ending := groups[0][1] - if _, ok := customLangs[ending]; !ok { - return - } - h.Language, _ = customLangs[ending] +func (h *Heartbeat) Augment(languageMappings map[string]string) { + if h.languageRegex == nil { + h.languageRegex = regexp.MustCompile(`^.+\.(.+)$`) } -} - -func (h *Heartbeat) AugmentWithUserRules(customRules []*CustomRule) { - for _, lang := range customRules { - reg := fmt.Sprintf(".*%s$", lang.Extension) - match, err := regexp.MatchString(reg, h.Entity) - if match && err == nil { - h.Language = lang.Language - return - } + groups := h.languageRegex.FindAllStringSubmatch(h.Entity, -1) + if len(groups) == 0 || len(groups[0]) != 2 { + return } + ending := groups[0][1] + if _, ok := languageMappings[ending]; !ok { + return + } + h.Language, _ = languageMappings[ending] } diff --git a/models/customrule.go b/models/language_mapping.go similarity index 55% rename from models/customrule.go rename to models/language_mapping.go index d4f07f7..13b05b8 100644 --- a/models/customrule.go +++ b/models/language_mapping.go @@ -1,10 +1,10 @@ package models -type CustomRule struct { +type LanguageMapping struct { ID uint `json:"id" gorm:"primary_key"` User *User `json:"-" gorm:"not null"` - UserID string `json:"-" gorm:"not null; index:idx_customrule_user"` - Extension string `json:"extension"` + UserID string `json:"-" gorm:"not null; index:idx_language_mapping_user; uniqueIndex:idx_language_mapping_composite"` + Extension string `json:"extension" gorm:"uniqueIndex:idx_language_mapping_composite"` Language string `json:"language"` } diff --git a/models/shared.go b/models/shared.go index a5f7b4c..dc35cb5 100644 --- a/models/shared.go +++ b/models/shared.go @@ -4,7 +4,7 @@ import ( "database/sql/driver" "errors" "fmt" - "github.com/jinzhu/gorm" + "gorm.io/gorm" "math" "strconv" "strings" diff --git a/models/summary.go b/models/summary.go index b0f97c2..4af3a60 100644 --- a/models/summary.go +++ b/models/summary.go @@ -36,8 +36,8 @@ const UnknownSummaryKey = "unknown" type Summary struct { ID uint `json:"-" gorm:"primary_key"` UserID string `json:"user_id" gorm:"not null; index:idx_time_summary_user"` - FromTime CustomTime `json:"from" gorm:"not null; type:timestamp(3); default:CURRENT_TIMESTAMP(3); index:idx_time_summary_user"` - ToTime CustomTime `json:"to" gorm:"not null; type:timestamp(3); default:CURRENT_TIMESTAMP(3); index:idx_time_summary_user"` + FromTime CustomTime `json:"from" gorm:"not null; type:timestamp; default:CURRENT_TIMESTAMP; index:idx_time_summary_user"` + ToTime CustomTime `json:"to" gorm:"not null; type:timestamp; default:CURRENT_TIMESTAMP; index:idx_time_summary_user"` Projects []*SummaryItem `json:"projects"` Languages []*SummaryItem `json:"languages"` Editors []*SummaryItem `json:"editors"` diff --git a/repositories/alias.go b/repositories/alias.go index a1783b8..c905828 100644 --- a/repositories/alias.go +++ b/repositories/alias.go @@ -1,8 +1,8 @@ package repositories import ( - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" + "gorm.io/gorm" ) type AliasRepository struct { diff --git a/repositories/custom_rule.go b/repositories/custom_rule.go deleted file mode 100644 index acd1d80..0000000 --- a/repositories/custom_rule.go +++ /dev/null @@ -1,46 +0,0 @@ -package repositories - -import ( - "github.com/jinzhu/gorm" - "github.com/muety/wakapi/models" -) - -type CustomRuleRepository struct { - db *gorm.DB -} - -func NewCustomRuleRepository(db *gorm.DB) *CustomRuleRepository { - return &CustomRuleRepository{db: db} -} - -func (r *CustomRuleRepository) GetById(id uint) (*models.CustomRule, error) { - rule := &models.CustomRule{} - if err := r.db.Where(&models.CustomRule{ID: id}).First(rule).Error; err != nil { - return rule, err - } - return rule, nil -} - -func (r *CustomRuleRepository) GetByUser(userId string) ([]*models.CustomRule, error) { - var rules []*models.CustomRule - if err := r.db. - Where(&models.CustomRule{UserID: userId}). - Find(&rules).Error; err != nil { - return rules, err - } - return rules, nil -} - -func (r *CustomRuleRepository) Insert(rule *models.CustomRule) (*models.CustomRule, error) { - result := r.db.Create(rule) - if err := result.Error; err != nil { - return nil, err - } - return rule, nil -} - -func (r *CustomRuleRepository) Delete(id uint) error { - return r.db. - Where("id = ?", id). - Delete(models.CustomRule{}).Error -} diff --git a/repositories/heartbeart.go b/repositories/heartbeart.go index 1eb5ccd..3fcd320 100644 --- a/repositories/heartbeart.go +++ b/repositories/heartbeart.go @@ -1,9 +1,8 @@ package repositories import ( - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" - gormbulk "github.com/t-tiger/gorm-bulk-insert" + "gorm.io/gorm" "time" ) @@ -16,12 +15,7 @@ func NewHeartbeatRepository(db *gorm.DB) *HeartbeatRepository { } func (r *HeartbeatRepository) InsertBatch(heartbeats []*models.Heartbeat) error { - var batch []interface{} - for _, h := range heartbeats { - batch = append(batch, *h) - } - - if err := gormbulk.BulkInsert(r.db, batch, 3000); err != nil { + if err := r.db.Create(&heartbeats).Error; err != nil { return err } return nil diff --git a/repositories/key_value.go b/repositories/key_value.go index 0973baa..9606572 100644 --- a/repositories/key_value.go +++ b/repositories/key_value.go @@ -2,8 +2,8 @@ package repositories import ( "errors" - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" + "gorm.io/gorm" ) type KeyValueRepository struct { diff --git a/repositories/language_mapping.go b/repositories/language_mapping.go new file mode 100644 index 0000000..27270fe --- /dev/null +++ b/repositories/language_mapping.go @@ -0,0 +1,48 @@ +package repositories + +import ( + "github.com/muety/wakapi/config" + "github.com/muety/wakapi/models" + "gorm.io/gorm" +) + +type LanguageMappingRepository struct { + config *config.Config + db *gorm.DB +} + +func NewLanguageMappingRepository(db *gorm.DB) *LanguageMappingRepository { + return &LanguageMappingRepository{config: config.Get(), db: db} +} + +func (r *LanguageMappingRepository) GetById(id uint) (*models.LanguageMapping, error) { + mapping := &models.LanguageMapping{} + if err := r.db.Where(&models.LanguageMapping{ID: id}).First(mapping).Error; err != nil { + return mapping, err + } + return mapping, nil +} + +func (r *LanguageMappingRepository) GetByUser(userId string) ([]*models.LanguageMapping, error) { + var mappings []*models.LanguageMapping + if err := r.db. + Where(&models.LanguageMapping{UserID: userId}). + Find(&mappings).Error; err != nil { + return mappings, err + } + return mappings, nil +} + +func (r *LanguageMappingRepository) Insert(mapping *models.LanguageMapping) (*models.LanguageMapping, error) { + result := r.db.Create(mapping) + if err := result.Error; err != nil { + return nil, err + } + return mapping, nil +} + +func (r *LanguageMappingRepository) Delete(id uint) error { + return r.db. + Where("id = ?", id). + Delete(models.LanguageMapping{}).Error +} diff --git a/repositories/summary.go b/repositories/summary.go index 1221216..39aaa9e 100644 --- a/repositories/summary.go +++ b/repositories/summary.go @@ -1,8 +1,8 @@ package repositories import ( - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" + "gorm.io/gorm" "time" ) diff --git a/repositories/user.go b/repositories/user.go index 4cc79b7..71e3423 100644 --- a/repositories/user.go +++ b/repositories/user.go @@ -2,8 +2,8 @@ package repositories import ( "errors" - "github.com/jinzhu/gorm" "github.com/muety/wakapi/models" + "gorm.io/gorm" ) type UserRepository struct { diff --git a/routes/health.go b/routes/health.go index 5f26c60..42f17c4 100644 --- a/routes/health.go +++ b/routes/health.go @@ -2,7 +2,7 @@ package routes import ( "fmt" - "github.com/jinzhu/gorm" + "gorm.io/gorm" "net/http" ) @@ -16,8 +16,10 @@ func NewHealthHandler(db *gorm.DB) *HealthHandler { func (h *HealthHandler) ApiGet(w http.ResponseWriter, r *http.Request) { var dbStatus int - if err := h.db.DB().Ping(); err == nil { - dbStatus = 1 + if sqlDb, err := h.db.DB(); err == nil { + if err := sqlDb.Ping(); err == nil { + dbStatus = 1 + } } w.Header().Set("Content-Type", "text/plain") diff --git a/routes/heartbeat.go b/routes/heartbeat.go index 0eb71a0..4a8ec79 100644 --- a/routes/heartbeat.go +++ b/routes/heartbeat.go @@ -2,7 +2,7 @@ package routes import ( "encoding/json" - config2 "github.com/muety/wakapi/config" + conf "github.com/muety/wakapi/config" "net/http" "os" @@ -13,16 +13,16 @@ import ( ) type HeartbeatHandler struct { - config *config2.Config - heartbeatSrvc *services.HeartbeatService - customRuleSrvc *services.CustomRuleService + config *conf.Config + heartbeatSrvc *services.HeartbeatService + languageMappingSrvc *services.LanguageMappingService } -func NewHeartbeatHandler(heartbeatService *services.HeartbeatService, customRuleService *services.CustomRuleService) *HeartbeatHandler { +func NewHeartbeatHandler(heartbeatService *services.HeartbeatService, languageMappingService *services.LanguageMappingService) *HeartbeatHandler { return &HeartbeatHandler{ - config: config2.Get(), - heartbeatSrvc: heartbeatService, - customRuleSrvc: customRuleService, + config: conf.Get(), + heartbeatSrvc: heartbeatService, + languageMappingSrvc: languageMappingService, } } @@ -43,12 +43,12 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) { return } - rules, err := h.customRuleSrvc.GetCustomRuleForUser(user.ID) + /*languageMappings, err := h.languageMappingSrvc.ResolveByUser(user.ID) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return - } + }*/ for _, hb := range heartbeats { hb.OperatingSystem = opSys @@ -56,8 +56,6 @@ func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) { hb.Machine = machineName hb.User = user hb.UserID = user.ID - hb.AugmentWithConfigRules(h.config.App.CustomLanguages) - hb.AugmentWithUserRules(rules) if !hb.Valid() { w.WriteHeader(http.StatusBadRequest) diff --git a/routes/settings.go b/routes/settings.go index 87126ab..c9e9a33 100644 --- a/routes/settings.go +++ b/routes/settings.go @@ -13,18 +13,18 @@ import ( ) type SettingsHandler struct { - config *conf.Config - userSrvc *services.UserService - customRuleSrvc *services.CustomRuleService + config *conf.Config + userSrvc *services.UserService + languageMappingSrvc *services.LanguageMappingService } var credentialsDecoder = schema.NewDecoder() -func NewSettingsHandler(userService *services.UserService, customRuleService *services.CustomRuleService) *SettingsHandler { +func NewSettingsHandler(userService *services.UserService, languageMappingService *services.LanguageMappingService) *SettingsHandler { return &SettingsHandler{ - config: conf.Get(), - customRuleSrvc: customRuleService, - userSrvc: userService, + config: conf.Get(), + languageMappingSrvc: languageMappingService, + userSrvc: userService, } } @@ -34,12 +34,12 @@ func (h *SettingsHandler) GetIndex(w http.ResponseWriter, r *http.Request) { } user := r.Context().Value(models.UserKey).(*models.User) - rules, _ := h.customRuleSrvc.GetCustomRuleForUser(user.ID) + mappings, _ := h.languageMappingSrvc.GetByUser(user.ID) data := map[string]interface{}{ - "User": user, - "Rules": rules, - "Success": r.FormValue("success"), - "Error": r.FormValue("error"), + "User": user, + "LanguageMappings": mappings, + "Success": r.FormValue("success"), + "Error": r.FormValue("error"), } templates[conf.SettingsTemplate].Execute(w, data) @@ -54,34 +54,34 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request var credentials models.CredentialsReset if err := r.ParseForm(); err != nil { - respondAlert(w, "missing parameters", "", conf.SettingsTemplate, http.StatusBadRequest) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("missing parameters")), http.StatusFound) return } if err := credentialsDecoder.Decode(&credentials, r.PostForm); err != nil { - respondAlert(w, "missing parameters", "", conf.SettingsTemplate, http.StatusBadRequest) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("missing parameters")), http.StatusFound) return } if !utils.CompareBcrypt(user.Password, credentials.PasswordOld, h.config.Security.PasswordSalt) { - respondAlert(w, "invalid credentials", "", conf.SettingsTemplate, http.StatusUnauthorized) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("invalid credentials")), http.StatusFound) return } if !credentials.IsValid() { - respondAlert(w, "invalid parameters", "", conf.SettingsTemplate, http.StatusBadRequest) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("invalid parameters")), http.StatusFound) return } user.Password = credentials.PasswordNew if hash, err := utils.HashBcrypt(user.Password, h.config.Security.PasswordSalt); err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("internal server error")), http.StatusFound) return } else { user.Password = hash } if _, err := h.userSrvc.Update(user); err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("internal server error")), http.StatusFound) return } @@ -91,7 +91,7 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request } encoded, err := h.config.Security.SecureCookie.Encode(models.AuthCookieKey, login) if err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("internal server error")), http.StatusFound) return } @@ -104,39 +104,36 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request } http.SetCookie(w, cookie) - msg := url.QueryEscape("password was updated successfully") - http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) + http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, url.QueryEscape("password was updated successfully")), http.StatusFound) } -func (h *SettingsHandler) DeleteCustomRule(w http.ResponseWriter, r *http.Request) { +func (h *SettingsHandler) DeleteLanguageMapping(w http.ResponseWriter, r *http.Request) { if h.config.IsDev() { loadTemplates() } user := r.Context().Value(models.UserKey).(*models.User) - ruleId, err := strconv.Atoi(r.PostFormValue("ruleid")) + id, err := strconv.Atoi(r.PostFormValue("mapping_id")) if err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("could not delete mapping")), http.StatusFound) return } - rule := &models.CustomRule{ - ID: uint(ruleId), + mapping := &models.LanguageMapping{ + ID: uint(id), UserID: user.ID, } - err = h.customRuleSrvc.Delete(rule) + err = h.languageMappingSrvc.Delete(mapping) if err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("could not delete mapping")), http.StatusFound) return } - msg := url.QueryEscape("Custom rule deleted successfully.") - - http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) + http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, url.QueryEscape("mapping deleted successfully")), http.StatusFound) } -func (h *SettingsHandler) PostCreateCustomRule(w http.ResponseWriter, r *http.Request) { +func (h *SettingsHandler) PostCreateLanguageMapping(w http.ResponseWriter, r *http.Request) { if h.config.IsDev() { loadTemplates() } @@ -148,20 +145,18 @@ func (h *SettingsHandler) PostCreateCustomRule(w http.ResponseWriter, r *http.Re extension = extension[1:] } - rule := &models.CustomRule{ + mapping := &models.LanguageMapping{ UserID: user.ID, Extension: extension, Language: language, } - if _, err := h.customRuleSrvc.Create(rule); err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + if _, err := h.languageMappingSrvc.Create(mapping); err != nil { + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("mapping already exists")), http.StatusFound) return } - msg := url.QueryEscape("Custom rule saved successfully.") - - http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound) + http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, url.QueryEscape("mapping added successfully")), http.StatusFound) } func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request) { @@ -171,7 +166,7 @@ func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request user := r.Context().Value(models.UserKey).(*models.User) if _, err := h.userSrvc.ResetApiKey(user); err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("internal server error")), http.StatusFound) return } @@ -187,7 +182,7 @@ func (h *SettingsHandler) PostToggleBadges(w http.ResponseWriter, r *http.Reques user := r.Context().Value(models.UserKey).(*models.User) if _, err := h.userSrvc.ToggleBadges(user); err != nil { - respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError) + http.Redirect(w, r, fmt.Sprintf("%s/settings?error=%s", h.config.Server.BasePath, url.QueryEscape("internal server error")), http.StatusFound) return } diff --git a/services/custom_rule.go b/services/custom_rule.go deleted file mode 100644 index a17d587..0000000 --- a/services/custom_rule.go +++ /dev/null @@ -1,56 +0,0 @@ -package services - -import ( - "github.com/muety/wakapi/config" - "github.com/muety/wakapi/models" - "github.com/muety/wakapi/repositories" - "github.com/patrickmn/go-cache" - "time" -) - -type CustomRuleService struct { - config *config.Config - repository *repositories.CustomRuleRepository - cache *cache.Cache -} - -func NewCustomRuleService(customRuleRepo *repositories.CustomRuleRepository) *CustomRuleService { - return &CustomRuleService{ - config: config.Get(), - repository: customRuleRepo, - cache: cache.New(1*time.Hour, 2*time.Hour), - } -} - -func (srv *CustomRuleService) GetCustomRuleById(id uint) (*models.CustomRule, error) { - return srv.repository.GetById(id) -} - -func (srv *CustomRuleService) GetCustomRuleForUser(userId string) ([]*models.CustomRule, error) { - if rules, found := srv.cache.Get(userId); found { - return rules.([]*models.CustomRule), nil - } - - rules, err := srv.repository.GetByUser(userId) - if err != nil { - return nil, err - } - srv.cache.Set(userId, rules, cache.DefaultExpiration) - return rules, nil -} - -func (srv *CustomRuleService) Create(rule *models.CustomRule) (*models.CustomRule, error) { - result, err := srv.repository.Insert(rule) - if err != nil { - return nil, err - } - - srv.cache.Delete(result.UserID) - return result, nil -} - -func (srv *CustomRuleService) Delete(rule *models.CustomRule) error { - err := srv.repository.Delete(rule.ID) - srv.cache.Delete(rule.UserID) - return err -} diff --git a/services/heartbeat.go b/services/heartbeat.go index 5aa06ac..48a57dd 100644 --- a/services/heartbeat.go +++ b/services/heartbeat.go @@ -16,14 +16,16 @@ const ( ) type HeartbeatService struct { - config *config.Config - repository *repositories.HeartbeatRepository + config *config.Config + repository *repositories.HeartbeatRepository + languageMappingSrvc *LanguageMappingService } -func NewHeartbeatService(heartbeatRepo *repositories.HeartbeatRepository) *HeartbeatService { +func NewHeartbeatService(heartbeatRepo *repositories.HeartbeatRepository, languageMappingService *LanguageMappingService) *HeartbeatService { return &HeartbeatService{ - config: config.Get(), - repository: heartbeatRepo, + config: config.Get(), + repository: heartbeatRepo, + languageMappingSrvc: languageMappingService, } } @@ -32,7 +34,11 @@ func (srv *HeartbeatService) InsertBatch(heartbeats []*models.Heartbeat) error { } func (srv *HeartbeatService) GetAllWithin(from, to time.Time, user *models.User) ([]*models.Heartbeat, error) { - return srv.repository.GetAllWithin(from, to, user) + heartbeats, err := srv.repository.GetAllWithin(from, to, user) + if err != nil { + return nil, err + } + return srv.augmented(heartbeats, user.ID) } func (srv *HeartbeatService) GetFirstUserHeartbeats(userIds []string) ([]*models.Heartbeat, error) { @@ -59,3 +65,16 @@ func (srv *HeartbeatService) ScheduleCleanUp() { gocron.Every(1).Day().At("02:30").Do(srv.CleanUp) <-gocron.Start() } + +func (srv *HeartbeatService) augmented(heartbeats []*models.Heartbeat, userId string) ([]*models.Heartbeat, error) { + languageMapping, err := srv.languageMappingSrvc.ResolveByUser(userId) + if err != nil { + return nil, err + } + + for i := range heartbeats { + heartbeats[i].Augment(languageMapping) + } + + return heartbeats, nil +} diff --git a/services/language_mapping.go b/services/language_mapping.go new file mode 100644 index 0000000..40fc732 --- /dev/null +++ b/services/language_mapping.go @@ -0,0 +1,73 @@ +package services + +import ( + "github.com/muety/wakapi/config" + "github.com/muety/wakapi/models" + "github.com/muety/wakapi/repositories" + "github.com/patrickmn/go-cache" + "time" +) + +type LanguageMappingService struct { + config *config.Config + repository *repositories.LanguageMappingRepository + cache *cache.Cache +} + +func NewLanguageMappingService(languageMappingsRepo *repositories.LanguageMappingRepository) *LanguageMappingService { + return &LanguageMappingService{ + config: config.Get(), + repository: languageMappingsRepo, + cache: cache.New(1*time.Hour, 2*time.Hour), + } +} + +func (srv *LanguageMappingService) GetById(id uint) (*models.LanguageMapping, error) { + return srv.repository.GetById(id) +} + +func (srv *LanguageMappingService) GetByUser(userId string) ([]*models.LanguageMapping, error) { + if mappings, found := srv.cache.Get(userId); found { + return mappings.([]*models.LanguageMapping), nil + } + + mappings, err := srv.repository.GetByUser(userId) + if err != nil { + return nil, err + } + srv.cache.Set(userId, mappings, cache.DefaultExpiration) + return mappings, nil +} + +func (srv *LanguageMappingService) ResolveByUser(userId string) (map[string]string, error) { + mappings := srv.getServerMappings() + userMappings, err := srv.GetByUser(userId) + if err != nil { + return nil, err + } + + for _, m := range userMappings { + mappings[m.Extension] = m.Language + } + return mappings, nil +} + +func (srv *LanguageMappingService) Create(mapping *models.LanguageMapping) (*models.LanguageMapping, error) { + result, err := srv.repository.Insert(mapping) + if err != nil { + return nil, err + } + + srv.cache.Delete(result.UserID) + return result, nil +} + +func (srv *LanguageMappingService) Delete(mapping *models.LanguageMapping) error { + err := srv.repository.Delete(mapping.ID) + srv.cache.Delete(mapping.UserID) + return err +} + +func (srv LanguageMappingService) getServerMappings() map[string]string { + return srv.config.App.CustomLanguages +} diff --git a/services/summary.go b/services/summary.go index bcd30d5..d6248c4 100644 --- a/services/summary.go +++ b/services/summary.go @@ -17,22 +17,20 @@ import ( const HeartbeatDiffThreshold = 2 * time.Minute type SummaryService struct { - config *config.Config - cache *cache.Cache - repository *repositories.SummaryRepository - heartbeatService *HeartbeatService - aliasService *AliasService - customRuleService *CustomRuleService + config *config.Config + cache *cache.Cache + repository *repositories.SummaryRepository + heartbeatService *HeartbeatService + aliasService *AliasService } -func NewSummaryService(summaryRepo *repositories.SummaryRepository, heartbeatService *HeartbeatService, aliasService *AliasService, customRuleService *CustomRuleService) *SummaryService { +func NewSummaryService(summaryRepo *repositories.SummaryRepository, heartbeatService *HeartbeatService, aliasService *AliasService) *SummaryService { return &SummaryService{ - config: config.Get(), - cache: cache.New(24*time.Hour, 24*time.Hour), - repository: summaryRepo, - heartbeatService: heartbeatService, - aliasService: aliasService, - customRuleService: customRuleService, + config: config.Get(), + cache: cache.New(24*time.Hour, 24*time.Hour), + repository: summaryRepo, + heartbeatService: heartbeatService, + aliasService: aliasService, } } diff --git a/utils/common.go b/utils/common.go index ee297d1..f46d92d 100644 --- a/utils/common.go +++ b/utils/common.go @@ -2,8 +2,6 @@ package utils import ( "errors" - "fmt" - "github.com/muety/wakapi/config" "regexp" "time" ) @@ -28,41 +26,3 @@ func ParseUserAgent(ua string) (string, string, error) { } return groups[0][1], groups[0][2], nil } - -func MakeConnectionString(config *config.Config) string { - switch config.Db.Dialect { - case "mysql": - return mysqlConnectionString(config) - case "postgres": - return postgresConnectionString(config) - case "sqlite3": - return sqliteConnectionString(config) - } - return "" -} - -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.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.Db.Host, - config.Db.Port, - config.Db.User, - config.Db.Name, - config.Db.Password, - ) -} - -func sqliteConnectionString(config *config.Config) string { - return config.Db.Name -} diff --git a/version.txt b/version.txt index 01b7568..850e742 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.13.3 +1.14.0 diff --git a/views/settings.tpl.html b/views/settings.tpl.html index be9b1ba..2b8ed68 100644 --- a/views/settings.tpl.html +++ b/views/settings.tpl.html @@ -68,23 +68,23 @@
- Custom Rules + Language Mappings
- Custom rules modify future coding activity, they’re useful for correcting languages displayed wrong on your dashboard. + You can specify custom mapping from file extensions to programming languages (e.g. a .jsx file could be mapped to React.
- {{ if .Rules }} - {{ range $i, $rule := .Rules }} + {{ if .LanguageMappings }} + {{ range $i, $mapping := .LanguageMappings }}
- {{ $rule.Extension }} + {{ $mapping.Extension }} - {{ $rule.Language }} + {{ $mapping.Language }} -
- + + @@ -97,12 +97,12 @@
{{end}} - +
+ name="extension" placeholder=".py" minlength="1" required>
@@ -112,7 +112,7 @@