Compare commits
8 Commits
590003aa36
...
sql
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a45705540c | ||
| 29ee3dc6fd | |||
|
|
306705cb28 | ||
|
|
c4ff0ab1b7 | ||
|
|
a26894dac8 | ||
|
|
4f07bc4c2a | ||
|
|
9ff11cc14c | ||
|
|
38b27b4d11 |
12
bindata.go
12
bindata.go
@@ -28,7 +28,7 @@ import (
|
|||||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@@ -36,7 +36,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
|
|||||||
clErr := gz.Close()
|
clErr := gz.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
if clErr != nil {
|
if clErr != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -142,7 +142,7 @@ func jsDragdropJs() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "js/dragdrop.js", size: 2066, mode: os.FileMode(420), modTime: time.Unix(1590859748, 0)}
|
info := bindataFileInfo{name: "js/dragdrop.js", size: 2066, mode: os.FileMode(420), modTime: time.Unix(1609421047, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ func templatesHtmlIndexHtmlTmpl() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/html/index.html.tmpl", size: 1786, mode: os.FileMode(420), modTime: time.Unix(1595849380, 0)}
|
info := bindataFileInfo{name: "templates/html/index.html.tmpl", size: 1786, mode: os.FileMode(420), modTime: time.Unix(1609421047, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ func templatesHtmlPastemetaHtmlTmpl() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/html/pasteMeta.html.tmpl", size: 1431, mode: os.FileMode(420), modTime: time.Unix(1603641311, 0)}
|
info := bindataFileInfo{name: "templates/html/pasteMeta.html.tmpl", size: 1431, mode: os.FileMode(420), modTime: time.Unix(1620245565, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@@ -302,7 +302,7 @@ func templatesTxtPastemetaTxtTmpl() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "templates/txt/pasteMeta.txt.tmpl", size: 652, mode: os.FileMode(420), modTime: time.Unix(1603638601, 0)}
|
info := bindataFileInfo{name: "templates/txt/pasteMeta.txt.tmpl", size: 652, mode: os.FileMode(420), modTime: time.Unix(1620245565, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.hashru.nl/dsprenkels/rushlink/internal/boltdb"
|
|
||||||
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
|
|
||||||
bolt "go.etcd.io/bbolt"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
boltDBPath = flag.String("boltdb", "", "location of the bolt database file")
|
|
||||||
fileStorePath = flag.String("file-store", "", "path to the directory where uploaded files will be stored")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
boltFileStore, err := boltdb.OpenFileStore(*fileStorePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
boltDB, err := boltdb.OpenDB(*boltDBPath, boltFileStore)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
defer boltDB.Close()
|
|
||||||
sqlDB, err := db.OpenDBFromEnvironment()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate database schema
|
|
||||||
m := db.Gormigrate(sqlDB)
|
|
||||||
if err := m.MigrateTo("202010251337"); err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate all files in filestorage
|
|
||||||
if err := sqlDB.Transaction(func(sqlTx *gorm.DB) error {
|
|
||||||
return boltDB.Bolt.View(func(boltTx *bolt.Tx) error {
|
|
||||||
// Migrate all the file uploads
|
|
||||||
allFUs, err := boltdb.AllFileUploads(boltTx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var fusDeleted uint
|
|
||||||
var sqlFUs []db.FileUpload
|
|
||||||
for _, fu := range allFUs {
|
|
||||||
isDeleted := fu.State == boltdb.FileUploadStateDeleted
|
|
||||||
sqlFU := db.FileUpload{
|
|
||||||
State: db.FileUploadState(int(fu.State)),
|
|
||||||
PubID: fu.ID,
|
|
||||||
FileName: fu.FileName,
|
|
||||||
ContentType: fu.ContentType,
|
|
||||||
Checksum: fu.Checksum,
|
|
||||||
DeletedAt: deletedAt(isDeleted),
|
|
||||||
}
|
|
||||||
sqlFUs = append(sqlFUs, sqlFU)
|
|
||||||
if isDeleted {
|
|
||||||
fusDeleted++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("migrating %v file uploads (of which %v deleted)", len(sqlFUs), fusDeleted)
|
|
||||||
sqlTx.Create(sqlFUs)
|
|
||||||
|
|
||||||
// Migrate all the pastes.
|
|
||||||
allPastes, err := boltdb.AllPastes(boltTx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pastesDeleted uint
|
|
||||||
var sqlPastes []db.Paste
|
|
||||||
for _, paste := range allPastes {
|
|
||||||
isDeleted := paste.State == boltdb.PasteStateDeleted
|
|
||||||
sqlPaste := db.Paste{
|
|
||||||
Type: db.PasteType(int(paste.Type)),
|
|
||||||
State: db.PasteState(int(paste.State)),
|
|
||||||
Content: paste.Content,
|
|
||||||
Key: paste.Key,
|
|
||||||
DeleteToken: paste.DeleteToken,
|
|
||||||
CreatedAt: paste.TimeCreated,
|
|
||||||
DeletedAt: deletedAt(isDeleted),
|
|
||||||
}
|
|
||||||
sqlPastes = append(sqlPastes, sqlPaste)
|
|
||||||
if isDeleted {
|
|
||||||
pastesDeleted++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("migrating %v pastes (of which %v deleted)", len(sqlPastes), pastesDeleted)
|
|
||||||
sqlTx.Create(sqlPastes)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}); err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("migration successful! :D")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deletedAt(isDeleted bool) gorm.DeletedAt {
|
|
||||||
if isDeleted {
|
|
||||||
return gorm.DeletedAt{Time: time.Now(), Valid: true}
|
|
||||||
}
|
|
||||||
return gorm.DeletedAt{}
|
|
||||||
}
|
|
||||||
5
go.mod
5
go.mod
@@ -3,19 +3,14 @@ module gitea.hashru.nl/dsprenkels/rushlink
|
|||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect
|
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.0
|
github.com/go-gormigrate/gormigrate/v2 v2.0.0
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-cmp v0.5.5 // indirect
|
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.10.0
|
github.com/prometheus/client_golang v1.10.0
|
||||||
github.com/prometheus/common v0.23.0 // indirect
|
github.com/prometheus/common v0.23.0 // indirect
|
||||||
go.etcd.io/bbolt v1.3.5
|
|
||||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 // indirect
|
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
|
||||||
google.golang.org/protobuf v1.26.0 // indirect
|
|
||||||
gorm.io/driver/postgres v1.1.0
|
gorm.io/driver/postgres v1.1.0
|
||||||
gorm.io/driver/sqlite v1.1.4
|
gorm.io/driver/sqlite v1.1.4
|
||||||
gorm.io/gorm v1.21.9
|
gorm.io/gorm v1.21.9
|
||||||
|
|||||||
86
go.sum
86
go.sum
@@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
@@ -35,6 +36,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
|||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
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/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/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/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/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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||||
@@ -46,7 +48,9 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
@@ -62,10 +66,6 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
|
|||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M=
|
|
||||||
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
|
|
||||||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
|
||||||
github.com/go-gormigrate/gormigrate v1.6.0 h1:JvKpFsbVPLfIPPkIrZq1zq19PLFT4DVSnL5BiyE1P0E=
|
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.0 h1:e2A3Uznk4viUC4UuemuVgsNnvYZyOA8B3awlYk3UioU=
|
github.com/go-gormigrate/gormigrate/v2 v2.0.0 h1:e2A3Uznk4viUC4UuemuVgsNnvYZyOA8B3awlYk3UioU=
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.0/go.mod h1:YuVJ+D/dNt4HWrThTBnjgZuRbt7AuwINeg4q52ZE3Jw=
|
github.com/go-gormigrate/gormigrate/v2 v2.0.0/go.mod h1:YuVJ+D/dNt4HWrThTBnjgZuRbt7AuwINeg4q52ZE3Jw=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
@@ -75,13 +75,16 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/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-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/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
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/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/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.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -89,16 +92,12 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
|
|||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
@@ -110,25 +109,19 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
|||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
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/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
@@ -170,12 +163,12 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
|||||||
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
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.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.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
github.com/jackc/pgconn v1.6.4 h1:S7T6cx5o2OqmxdHaXLH1ZeD1SbI8jBznyYE9Ec0RCQ8=
|
|
||||||
github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78=
|
github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78=
|
||||||
github.com/jackc/pgconn v1.8.1 h1:ySBX7Q87vOMqKU2bbmKbUvtYhauDFclYbNDYIE1/h6s=
|
github.com/jackc/pgconn v1.8.1 h1:ySBX7Q87vOMqKU2bbmKbUvtYhauDFclYbNDYIE1/h6s=
|
||||||
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
|
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
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/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/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 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
@@ -186,12 +179,9 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
|
|||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
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.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.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.2 h1:q1Hsy66zh4vuNsajBUF2PNqfAMMfxU5mk594lPE9vjY=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8=
|
github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.7 h1:6Pwi1b3QdY65cuv6SyVO0FgPd5J3Bl7wf/nQQjinHMA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.7/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-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 h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
@@ -201,7 +191,6 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU
|
|||||||
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
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.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.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||||
github.com/jackc/pgtype v1.4.2 h1:t+6LWm5eWPLX1H5Se702JSBcirq6uWa4jiG4wV1rAWY=
|
|
||||||
github.com/jackc/pgtype v1.4.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
github.com/jackc/pgtype v1.4.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||||
github.com/jackc/pgtype v1.7.0 h1:6f4kVsW01QftE38ufBYxKciO6gyioXSC0ABIRLcZrGs=
|
github.com/jackc/pgtype v1.7.0 h1:6f4kVsW01QftE38ufBYxKciO6gyioXSC0ABIRLcZrGs=
|
||||||
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
|
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
|
||||||
@@ -211,7 +200,6 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ
|
|||||||
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
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.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.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||||
github.com/jackc/pgx/v4 v4.8.1 h1:SUbCLP2pXvf/Sr/25KsuI4aTxiFYIvpfk4l6aTSdyCw=
|
|
||||||
github.com/jackc/pgx/v4 v4.8.1/go.mod h1:4HOLxrl8wToZJReD04/yB20GDwf4KBYETvlHciCnwW0=
|
github.com/jackc/pgx/v4 v4.8.1/go.mod h1:4HOLxrl8wToZJReD04/yB20GDwf4KBYETvlHciCnwW0=
|
||||||
github.com/jackc/pgx/v4 v4.11.0 h1:J86tSWd3Y7nKjwT/43xZBvpi04keQWx8gNC2YkdJhZI=
|
github.com/jackc/pgx/v4 v4.11.0 h1:J86tSWd3Y7nKjwT/43xZBvpi04keQWx8gNC2YkdJhZI=
|
||||||
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
|
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
|
||||||
@@ -222,18 +210,17 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
|||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
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/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
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/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
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=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
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=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
@@ -244,13 +231,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
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.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.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/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-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/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
@@ -268,12 +258,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||||
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||||
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
@@ -319,25 +305,22 @@ 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 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/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.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/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
|
|
||||||
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
|
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
|
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
|
||||||
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
|
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
@@ -346,9 +329,6 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
|
|||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||||
github.com/prometheus/common v0.23.0 h1:GXWvPYuTUenIa+BhOq/x+L/QZzCqASkVRny5KTlPDGM=
|
github.com/prometheus/common v0.23.0 h1:GXWvPYuTUenIa+BhOq/x+L/QZzCqASkVRny5KTlPDGM=
|
||||||
@@ -357,8 +337,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
|
|
||||||
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
@@ -374,6 +352,7 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
|
|||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
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/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-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/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/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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
@@ -395,6 +374,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
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/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/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
@@ -402,10 +382,6 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
|||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
|
||||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
|
||||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
|
||||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
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.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
@@ -432,12 +408,9 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@@ -496,14 +469,8 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
|
|
||||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
|
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -516,8 +483,6 @@ golang.org/x/text v0.3.0/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.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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|
||||||
golang.org/x/text v0.3.6/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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -541,8 +506,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
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.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.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
@@ -552,7 +515,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
|
|||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||||
@@ -562,24 +524,19 @@ google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
@@ -594,23 +551,20 @@ 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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gorm.io/driver/mysql v1.0.1 h1:omJoilUzyrAp0xNoio88lGJCroGdIOen9hq2A/+3ifw=
|
||||||
gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
|
gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
|
||||||
gorm.io/driver/postgres v1.0.0 h1:Yh4jyFQ0a7F+JPU0Gtiam/eKmpT/XFc1FKxotGqc6FM=
|
|
||||||
gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to=
|
gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to=
|
||||||
gorm.io/driver/postgres v1.1.0 h1:afBljg7PtJ5lA6YUWluV2+xovIPhS+YiInuL3kUjrbk=
|
gorm.io/driver/postgres v1.1.0 h1:afBljg7PtJ5lA6YUWluV2+xovIPhS+YiInuL3kUjrbk=
|
||||||
gorm.io/driver/postgres v1.1.0/go.mod h1:hXQIwafeRjJvUm+OMxcFWyswJ/vevcpPLlGocwAwuqw=
|
gorm.io/driver/postgres v1.1.0/go.mod h1:hXQIwafeRjJvUm+OMxcFWyswJ/vevcpPLlGocwAwuqw=
|
||||||
gorm.io/driver/sqlite v1.1.1/go.mod h1:hm2olEcl8Tmsc6eZyxYSeznnsDaMqamBvEXLNtBg4cI=
|
gorm.io/driver/sqlite v1.1.1/go.mod h1:hm2olEcl8Tmsc6eZyxYSeznnsDaMqamBvEXLNtBg4cI=
|
||||||
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/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
||||||
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
||||||
|
gorm.io/driver/sqlserver v1.0.2 h1:FzxAlw0/7hntMzSiNfotpYCo9Lz8dqWQGdmCGqIiFGo=
|
||||||
gorm.io/driver/sqlserver v1.0.2/go.mod h1:gb0Y9QePGgqjzrVyTQUZeh9zkd5v0iz71cM1B4ZycEY=
|
gorm.io/driver/sqlserver v1.0.2/go.mod h1:gb0Y9QePGgqjzrVyTQUZeh9zkd5v0iz71cM1B4ZycEY=
|
||||||
gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
gorm.io/gorm v1.20.1/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=
|
|
||||||
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
gorm.io/gorm v1.21.9 h1:INieZtn4P2Pw6xPJ8MzT0G4WUOsHq3RhfuDF1M6GW0E=
|
gorm.io/gorm v1.21.9 h1:INieZtn4P2Pw6xPJ8MzT0G4WUOsHq3RhfuDF1M6GW0E=
|
||||||
gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||||
|
|||||||
71
handlers.go
71
handlers.go
@@ -12,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
|
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@@ -94,7 +93,6 @@ func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request
|
|||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
key := vars["key"]
|
key := vars["key"]
|
||||||
var p *db.Paste
|
var p *db.Paste
|
||||||
var fu *db.FileUpload
|
|
||||||
var notFound bool
|
var notFound bool
|
||||||
err := rl.db.Transaction(func(tx *db.Database) error {
|
err := rl.db.Transaction(func(tx *db.Database) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -102,14 +100,6 @@ func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if p != nil && p.Type == db.PasteTypeFileUpload {
|
|
||||||
var id uuid.UUID
|
|
||||||
copy(id[:], p.Content)
|
|
||||||
fu, err = db.GetFileUpload(tx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if notFound {
|
if notFound {
|
||||||
@@ -123,12 +113,12 @@ func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request
|
|||||||
rl.renderError(w, r, status, err.Error())
|
rl.renderError(w, r, status, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rl.viewPasteHandlerInner(w, r, flags, p, fu)
|
rl.viewPasteHandlerInner(w, r, flags, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPaste, p *db.Paste, fu *db.FileUpload) {
|
func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPaste, p *db.Paste) {
|
||||||
if flags&viewShowMeta != 0 {
|
if flags&viewShowMeta != 0 {
|
||||||
rl.viewPasteHandlerInnerMeta(w, r, p, fu)
|
rl.viewPasteHandlerInnerMeta(w, r, p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,10 +126,7 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
|
|||||||
case db.PasteStatePresent:
|
case db.PasteStatePresent:
|
||||||
switch p.Type {
|
switch p.Type {
|
||||||
case db.PasteTypeFileUpload:
|
case db.PasteTypeFileUpload:
|
||||||
if fu == nil {
|
rl.viewFileUploadHandler(w, r, p.FileUpload)
|
||||||
panic(fmt.Sprintf("file for id %v does not exist in database\n", string(p.Content)))
|
|
||||||
}
|
|
||||||
rl.viewFileUploadHandler(w, r, fu)
|
|
||||||
return
|
return
|
||||||
case db.PasteTypeRedirect:
|
case db.PasteTypeRedirect:
|
||||||
if flags&viewNoRedirect != 0 {
|
if flags&viewNoRedirect != 0 {
|
||||||
@@ -191,7 +178,7 @@ func (rl *rushlink) viewFileUploadHandler(w http.ResponseWriter, r *http.Request
|
|||||||
http.ServeContent(w, r, fu.FileName, modtime, file)
|
http.ServeContent(w, r, fu.FileName, modtime, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *rushlink) viewPasteHandlerInnerMeta(w http.ResponseWriter, r *http.Request, p *db.Paste, fu *db.FileUpload) {
|
func (rl *rushlink) viewPasteHandlerInnerMeta(w http.ResponseWriter, r *http.Request, p *db.Paste) {
|
||||||
var cd canDelete
|
var cd canDelete
|
||||||
deleteToken := getDeleteTokenFromRequest(r)
|
deleteToken := getDeleteTokenFromRequest(r)
|
||||||
if deleteToken != "" {
|
if deleteToken != "" {
|
||||||
@@ -203,8 +190,8 @@ func (rl *rushlink) viewPasteHandlerInnerMeta(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileExt string
|
var fileExt string
|
||||||
if fu != nil {
|
if p.FileUpload != nil {
|
||||||
fileExt = fu.Ext()
|
fileExt = p.FileUpload.Ext()
|
||||||
}
|
}
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Paste": p,
|
"Paste": p,
|
||||||
@@ -290,7 +277,7 @@ func (rl *rushlink) newFileUploadPasteHandler(w http.ResponseWriter, r *http.Req
|
|||||||
panic(errors.Wrap(err, "saving fileUpload in db"))
|
panic(errors.Wrap(err, "saving fileUpload in db"))
|
||||||
}
|
}
|
||||||
|
|
||||||
paste, err = shortenFileUploadID(tx, fu.PubID)
|
paste, err = shortenFileUploadID(tx, fu)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -370,25 +357,39 @@ func (rl *rushlink) deletePasteHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
//
|
//
|
||||||
// Returns the new paste key if the fileUpload was successfully added to the
|
// Returns the new paste key if the fileUpload was successfully added to the
|
||||||
// database
|
// database
|
||||||
func shortenFileUploadID(tx *db.Database, id uuid.UUID) (*db.Paste, error) {
|
func shortenFileUploadID(tx *db.Database, fu *db.FileUpload) (*db.Paste, error) {
|
||||||
return shorten(tx, db.PasteTypeFileUpload, id[:])
|
// Generate the paste key
|
||||||
|
pasteKey, err := db.GeneratePasteKey(tx, highOnlineEntropy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "generating paste key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also generate a deleteToken
|
||||||
|
deleteToken, err := db.GenerateDeleteToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "generating delete token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new
|
||||||
|
p := db.Paste{
|
||||||
|
Type: db.PasteTypeFileUpload,
|
||||||
|
State: db.PasteStatePresent,
|
||||||
|
FileUpload: fu,
|
||||||
|
Key: pasteKey,
|
||||||
|
DeleteToken: deleteToken,
|
||||||
|
}
|
||||||
|
if err := p.Save(tx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new URL to the database
|
// Add a new URL to the database
|
||||||
//
|
//
|
||||||
// Returns the new paste key if the url was successfully shortened
|
// Returns the new paste key if the url was successfully shortened
|
||||||
func shortenURL(tx *db.Database, userURL *url.URL) (*db.Paste, error) {
|
func shortenURL(tx *db.Database, userURL *url.URL) (*db.Paste, error) {
|
||||||
return shorten(tx, db.PasteTypeRedirect, []byte(userURL.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a paste (of any kind) to the database with arbitrary content.
|
|
||||||
func shorten(tx *db.Database, ty db.PasteType, content []byte) (*db.Paste, error) {
|
|
||||||
// Generate the paste key
|
// Generate the paste key
|
||||||
var keyEntropy int
|
pasteKey, err := db.GeneratePasteKey(tx, 0)
|
||||||
if ty == db.PasteTypeFileUpload || ty == db.PasteTypePaste {
|
|
||||||
keyEntropy = highOnlineEntropy
|
|
||||||
}
|
|
||||||
pasteKey, err := db.GeneratePasteKey(tx, keyEntropy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "generating paste key")
|
return nil, errors.Wrap(err, "generating paste key")
|
||||||
}
|
}
|
||||||
@@ -401,9 +402,9 @@ func shorten(tx *db.Database, ty db.PasteType, content []byte) (*db.Paste, error
|
|||||||
|
|
||||||
// Store the new key
|
// Store the new key
|
||||||
p := db.Paste{
|
p := db.Paste{
|
||||||
Type: ty,
|
Type: db.PasteTypeRedirect,
|
||||||
State: db.PasteStatePresent,
|
State: db.PasteStatePresent,
|
||||||
Content: content,
|
URL: userURL.String(),
|
||||||
Key: pasteKey,
|
Key: pasteKey,
|
||||||
DeleteToken: deleteToken,
|
DeleteToken: deleteToken,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
package boltdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
bolt "go.etcd.io/bbolt"
|
|
||||||
|
|
||||||
gobmarsh "gitea.hashru.nl/dsprenkels/rushlink/pkg/gobmarsh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Database is the main rushlink database type.
|
|
||||||
//
|
|
||||||
// Open a database using DB.Open() and close it in the end using DB.Close().
|
|
||||||
// Only one instance of DB should exist in a program at any moment.
|
|
||||||
type Database struct {
|
|
||||||
Bolt *bolt.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentMigrateVersion holds the current "migrate version".
|
|
||||||
//
|
|
||||||
// If we alter the database format, we bump this number and write a new
|
|
||||||
// database migration in migrate().
|
|
||||||
const CurrentMigrateVersion = 3
|
|
||||||
|
|
||||||
// BucketConf holds the name for the "configuration" bucket.
|
|
||||||
//
|
|
||||||
// This bucket holds the database version, secret site-wide keys, etc.
|
|
||||||
const BucketConf = "conf"
|
|
||||||
|
|
||||||
// BucketPastes holds the name for the pastes bucket.
|
|
||||||
const BucketPastes = "pastes"
|
|
||||||
|
|
||||||
// BucketFileUpload holds the name for the file-upload bucket.
|
|
||||||
const BucketFileUpload = "fileUpload"
|
|
||||||
|
|
||||||
// KeyMigrateVersion stores the current migration version. If this value is less than
|
|
||||||
// CurrentMigrateVersion, the database has to be migrated.
|
|
||||||
const KeyMigrateVersion = "migrate_version"
|
|
||||||
|
|
||||||
// OpenDB opens a database file located at path.
|
|
||||||
func OpenDB(path string, fs *FileStore) (*Database, error) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, errors.New("database not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
db, err := bolt.Open(path, 0660, &bolt.Options{Timeout: 1 * time.Second})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to open database at '%v'", path)
|
|
||||||
}
|
|
||||||
if err := db.Update(func(tx *bolt.Tx) error { return migrate(tx, fs) }); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Database{db}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the bolt database
|
|
||||||
func (db *Database) Close() error {
|
|
||||||
if db == nil {
|
|
||||||
panic("no open database")
|
|
||||||
}
|
|
||||||
return db.Bolt.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize and migrate the database to the current version
|
|
||||||
func migrate(tx *bolt.Tx, fs *FileStore) error {
|
|
||||||
// Guidelines for error handling:
|
|
||||||
// - Errors based on malformed *structure* should be fatal!
|
|
||||||
// - Errors based on malformed *data* should print a warning
|
|
||||||
// (and if possible try to fix the error).
|
|
||||||
|
|
||||||
dbVersion, err := dbVersion(tx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate the database to version 1
|
|
||||||
if dbVersion < 1 {
|
|
||||||
log.Println("migrating database to version 1")
|
|
||||||
// Create conf bucket
|
|
||||||
_, err := tx.CreateBucket([]byte(BucketConf))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Create paste bucket
|
|
||||||
_, err = tx.CreateBucket([]byte(BucketPastes))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Update the version number
|
|
||||||
if err := setDBVersion(tx, 1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbVersion < 2 {
|
|
||||||
log.Println("migrating database to version 2")
|
|
||||||
// Create fileUpload bucket
|
|
||||||
_, err := tx.CreateBucket([]byte(BucketFileUpload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Update the version number
|
|
||||||
if err := setDBVersion(tx, 2); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbVersion < 3 {
|
|
||||||
log.Println("migrating database to version 3")
|
|
||||||
// In this version, we changed te way how Content-Types are being
|
|
||||||
// stored. Previously, we allowed clients to provide their own
|
|
||||||
// Content-Types for files, using the Content-Disposition header in
|
|
||||||
// multipart forms. The new way detects these types using
|
|
||||||
// http.DetectContentType.
|
|
||||||
//
|
|
||||||
// Scan through all the FileUploads and update their ContentTypes.
|
|
||||||
bucket := tx.Bucket([]byte(BucketFileUpload))
|
|
||||||
cursor := bucket.Cursor()
|
|
||||||
var id, storedBytes []byte
|
|
||||||
id, storedBytes = cursor.First()
|
|
||||||
for id != nil {
|
|
||||||
fu, err := decodeFileUpload(storedBytes)
|
|
||||||
if err != nil {
|
|
||||||
log.Print("error: ", errors.Wrapf(err, "corrupted FileUpload in database at '%v'", id))
|
|
||||||
id, storedBytes = cursor.Next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fu.State != FileUploadStatePresent {
|
|
||||||
id, storedBytes = cursor.Next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filePath := fu.Path(fs)
|
|
||||||
file, err := os.Open(fu.Path(fs))
|
|
||||||
if err != nil {
|
|
||||||
log.Print("error: ", errors.Wrapf(err, "could not open file at '%v'", filePath))
|
|
||||||
id, storedBytes = cursor.Next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
buf.Grow(512)
|
|
||||||
io.CopyN(&buf, file, 512)
|
|
||||||
contentType := http.DetectContentType(buf.Bytes())
|
|
||||||
if contentType != fu.ContentType {
|
|
||||||
fu.ContentType = contentType
|
|
||||||
fu.Save(tx)
|
|
||||||
cursor.Seek(id)
|
|
||||||
}
|
|
||||||
id, storedBytes = cursor.Next()
|
|
||||||
}
|
|
||||||
// Update the version number
|
|
||||||
if err := setDBVersion(tx, 3); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current migrate version from the database
|
|
||||||
func dbVersion(tx *bolt.Tx) (int, error) {
|
|
||||||
conf := tx.Bucket([]byte(BucketConf))
|
|
||||||
if conf == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
dbVersionBytes := conf.Get([]byte(KeyMigrateVersion))
|
|
||||||
if dbVersionBytes == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version was already stored
|
|
||||||
var dbVersion int
|
|
||||||
if err := gobmarsh.Unmarshal(dbVersionBytes, &dbVersion); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if dbVersion == 0 {
|
|
||||||
return 0, fmt.Errorf("database version is invalid (%v)", dbVersion)
|
|
||||||
}
|
|
||||||
if dbVersion > CurrentMigrateVersion {
|
|
||||||
return 0, fmt.Errorf("database version is too recent (%v > %v)", dbVersion, CurrentMigrateVersion)
|
|
||||||
}
|
|
||||||
return dbVersion, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the current migrate version in the database
|
|
||||||
func setDBVersion(tx *bolt.Tx, version int) error {
|
|
||||||
conf, err := tx.CreateBucketIfNotExists([]byte(BucketConf))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
versionBytes, err := gobmarsh.Marshal(version)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return conf.Put([]byte(KeyMigrateVersion), versionBytes)
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
package boltdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"hash/crc32"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
bolt "go.etcd.io/bbolt"
|
|
||||||
|
|
||||||
gobmarsh "gitea.hashru.nl/dsprenkels/rushlink/pkg/gobmarsh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Use the Castagnoli checksum because of the acceleration on Intel CPUs
|
|
||||||
var checksumTable = crc32.MakeTable(crc32.Castagnoli)
|
|
||||||
|
|
||||||
// FileStore holds the path to a file storage location.
|
|
||||||
type FileStore struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileUploadState determines the current state of a FileUpload object.
|
|
||||||
type FileUploadState int
|
|
||||||
|
|
||||||
// FileUpload models an uploaded file.
|
|
||||||
type FileUpload struct {
|
|
||||||
// State of the FileUpload (present/deleted/etc).
|
|
||||||
State FileUploadState
|
|
||||||
|
|
||||||
// ID identifies this FileUpload.
|
|
||||||
ID uuid.UUID
|
|
||||||
|
|
||||||
// FileName contains the original filename of this FileUpload.
|
|
||||||
FileName string
|
|
||||||
|
|
||||||
// Content type as determined by http.DetectContentType.
|
|
||||||
ContentType string
|
|
||||||
|
|
||||||
// Checksum holds a crc32c checksum of the file.
|
|
||||||
//
|
|
||||||
// This checksum is only meant to allow for the detection of random
|
|
||||||
// database corruption.
|
|
||||||
Checksum uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
dirMode os.FileMode = 0750
|
|
||||||
fileMode os.FileMode = 0640
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// FileUploadStateUndef is an undefined FileUpload.
|
|
||||||
FileUploadStateUndef FileUploadState = 0
|
|
||||||
// FileUploadStatePresent denotes the normal (existing) state.
|
|
||||||
FileUploadStatePresent FileUploadState = 1
|
|
||||||
// FileUploadStateDeleted denotes a deleted state.
|
|
||||||
FileUploadStateDeleted FileUploadState = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t FileUploadState) String() string {
|
|
||||||
switch t {
|
|
||||||
case FileUploadStateUndef:
|
|
||||||
return "unknown"
|
|
||||||
case FileUploadStatePresent:
|
|
||||||
return "present"
|
|
||||||
case FileUploadStateDeleted:
|
|
||||||
return "deleted"
|
|
||||||
default:
|
|
||||||
return "invalid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFileStore opens the file storage at path.
|
|
||||||
func OpenFileStore(path string) (*FileStore, error) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, errors.New("file-store not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create the file store directory if it does not yet exist
|
|
||||||
if err := os.MkdirAll(path, dirMode); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "creating file store directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FileStore{path[:]}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path returns the path of the FileStore root.
|
|
||||||
func (fs *FileStore) Path() string {
|
|
||||||
return fs.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// filePath resolves the path of a file in the FileStore given some id and filename.
|
|
||||||
func (fs *FileStore) filePath(id uuid.UUID, fileName string) string {
|
|
||||||
if fs.path == "" {
|
|
||||||
panic("fileStoreDir called while the file store path has not been set")
|
|
||||||
}
|
|
||||||
return path.Join(fs.path, hex.EncodeToString(id[:]), fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFileUpload creates a new FileUpload object.
|
|
||||||
//
|
|
||||||
// Internally, this function detects the type of the file stored in `r` using
|
|
||||||
// `http.DetectContentType`.
|
|
||||||
func NewFileUpload(fs *FileStore, r io.Reader, fileName string) (*FileUpload, error) {
|
|
||||||
// Generate a file ID
|
|
||||||
id, err := uuid.NewRandom()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "generating UUID")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a checksum for this file
|
|
||||||
hash := crc32.New(checksumTable)
|
|
||||||
tee := io.TeeReader(r, hash)
|
|
||||||
|
|
||||||
// Detect the file type
|
|
||||||
var tmpBuf bytes.Buffer
|
|
||||||
tmpBuf.Grow(512)
|
|
||||||
io.CopyN(&tmpBuf, tee, 512)
|
|
||||||
contentType := http.DetectContentType(tmpBuf.Bytes())
|
|
||||||
|
|
||||||
// Open the file on disk for writing
|
|
||||||
baseName := filepath.Base(fileName)
|
|
||||||
filePath := fs.filePath(id, baseName)
|
|
||||||
if err := os.Mkdir(path.Dir(filePath), dirMode); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "creating file dir")
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, fileMode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "opening file")
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// Write the file to disk
|
|
||||||
_, err = io.Copy(file, &tmpBuf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "writing to file")
|
|
||||||
}
|
|
||||||
_, err = io.Copy(file, tee)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "writing to file")
|
|
||||||
}
|
|
||||||
|
|
||||||
fu := &FileUpload{
|
|
||||||
State: FileUploadStatePresent,
|
|
||||||
ID: id,
|
|
||||||
FileName: baseName,
|
|
||||||
ContentType: contentType,
|
|
||||||
Checksum: hash.Sum32(),
|
|
||||||
}
|
|
||||||
return fu, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFileUpload tries to retrieve a FileUpload object from the bolt database.
|
|
||||||
func GetFileUpload(tx *bolt.Tx, id uuid.UUID) (*FileUpload, error) {
|
|
||||||
bucket := tx.Bucket([]byte(BucketFileUpload))
|
|
||||||
if bucket == nil {
|
|
||||||
return nil, errors.Errorf("bucket %v does not exist", BucketFileUpload)
|
|
||||||
}
|
|
||||||
storedBytes := bucket.Get(id[:])
|
|
||||||
if storedBytes == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return decodeFileUpload(storedBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllFileUploads tries to retrieve all FileUpload objects from the bolt database.
|
|
||||||
func AllFileUploads(tx *bolt.Tx) ([]FileUpload, error) {
|
|
||||||
bucket := tx.Bucket([]byte(BucketFileUpload))
|
|
||||||
if bucket == nil {
|
|
||||||
return nil, errors.Errorf("bucket %v does not exist", BucketFileUpload)
|
|
||||||
}
|
|
||||||
var fus []FileUpload
|
|
||||||
err := bucket.ForEach(func(_, storedBytes []byte) error {
|
|
||||||
fu, err := decodeFileUpload(storedBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fus = append(fus, *fu)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return fus, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeFileUpload(storedBytes []byte) (*FileUpload, error) {
|
|
||||||
fu := &FileUpload{}
|
|
||||||
err := gobmarsh.Unmarshal(storedBytes, fu)
|
|
||||||
return fu, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves a FileUpload in the database.
|
|
||||||
func (fu *FileUpload) Save(tx *bolt.Tx) error {
|
|
||||||
bucket := tx.Bucket([]byte(BucketFileUpload))
|
|
||||||
if bucket == nil {
|
|
||||||
return errors.Errorf("bucket %v does not exist", BucketFileUpload)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := gobmarsh.Marshal(fu)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "encoding for database failed")
|
|
||||||
}
|
|
||||||
if err := bucket.Put(fu.ID[:], buf); err != nil {
|
|
||||||
return errors.Wrap(err, "database transaction failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes a FileUpload from the database.
|
|
||||||
func (fu *FileUpload) Delete(tx *bolt.Tx, fs *FileStore) error {
|
|
||||||
// Remove the file in the backend
|
|
||||||
filePath := fu.Path(fs)
|
|
||||||
if err := os.Remove(filePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the file in the server
|
|
||||||
if err := (&FileUpload{
|
|
||||||
ID: fu.ID,
|
|
||||||
State: FileUploadStateDeleted,
|
|
||||||
}).Save(tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup the parent directory
|
|
||||||
wrap := "deletion succeeded, but removing the file directory has failed"
|
|
||||||
return errors.Wrap(os.Remove(path.Dir(filePath)), wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path returns the path to this FileUpload in the FileStore provided in fs.
|
|
||||||
func (fu *FileUpload) Path(fs *FileStore) string {
|
|
||||||
return fs.filePath(fu.ID, fu.FileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL returns the URL for the FileUpload.
|
|
||||||
func (fu *FileUpload) URL() *url.URL {
|
|
||||||
rawurl := "/uploads/" + hex.EncodeToString(fu.ID[:]) + "/" + fu.FileName
|
|
||||||
urlParse, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
|
||||||
panic("could not construct /uploads/ url")
|
|
||||||
}
|
|
||||||
return urlParse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ext returns the extension of the file attached to this FileUpload.
|
|
||||||
func (fu *FileUpload) Ext() string {
|
|
||||||
return filepath.Ext(fu.FileName)
|
|
||||||
}
|
|
||||||
@@ -1,345 +0,0 @@
|
|||||||
package boltdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
gobmarsh "gitea.hashru.nl/dsprenkels/rushlink/pkg/gobmarsh"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
bolt "go.etcd.io/bbolt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PasteType describes the type of Paste (i.e. file, redirect, [...]).
|
|
||||||
type PasteType int
|
|
||||||
|
|
||||||
// PasteState describes the state of a Paste (i.e. present, deleted, [...]).
|
|
||||||
type PasteState int
|
|
||||||
|
|
||||||
// Paste describes the main Paste model in the database.
|
|
||||||
type Paste struct {
|
|
||||||
Type PasteType
|
|
||||||
State PasteState
|
|
||||||
Content []byte
|
|
||||||
Key string
|
|
||||||
DeleteToken string
|
|
||||||
TimeCreated time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReservedPasteKeys keys are designated reserved, and will not be randomly chosen
|
|
||||||
var ReservedPasteKeys = []string{"xd42", "example"}
|
|
||||||
|
|
||||||
// Note: we use iota here. That means removals of PasteType* are not allowed,
|
|
||||||
// because this changes the value of the constant. Please add the comment
|
|
||||||
// "// deprecated" if you want to remove the constant. Additions are only
|
|
||||||
// allowed at the bottom of this block, for the same reason.
|
|
||||||
const (
|
|
||||||
PasteTypeUndef PasteType = iota
|
|
||||||
// PasteTypePaste is as of yet unused. It is still unclear if this type
|
|
||||||
// will ever get a proper meaning.
|
|
||||||
PasteTypePaste
|
|
||||||
PasteTypeRedirect
|
|
||||||
PasteTypeFileUpload
|
|
||||||
)
|
|
||||||
|
|
||||||
// Note: we use iota here. See the comment above PasteType*
|
|
||||||
const (
|
|
||||||
PasteStateUndef PasteState = iota
|
|
||||||
PasteStatePresent
|
|
||||||
PasteStateDeleted
|
|
||||||
)
|
|
||||||
|
|
||||||
// minKeyLen specifies the mimimum length of a paste key.
|
|
||||||
const minKeyLen = 4
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrKeyInvalidChar occurs when a key contains an invalid character.
|
|
||||||
ErrKeyInvalidChar = errors.New("invalid character in key")
|
|
||||||
// ErrKeyInvalidLength occurs when a key embeds a length that is incorrect.
|
|
||||||
ErrKeyInvalidLength = errors.New("key length encoding is incorrect")
|
|
||||||
// ErrPasteDoesNotExist occurs when a key does not exist in the database.
|
|
||||||
ErrPasteDoesNotExist = errors.New("url key not found in the database")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrHTTPStatusCode returns the HTTP status code that should correspond to
|
|
||||||
// the provided error.
|
|
||||||
// server error, or false if it is not.
|
|
||||||
func ErrHTTPStatusCode(err error) int {
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return 0
|
|
||||||
case ErrKeyInvalidChar, ErrKeyInvalidLength, ErrPasteDoesNotExist:
|
|
||||||
return http.StatusNotFound
|
|
||||||
}
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 encoding and decoding
|
|
||||||
var base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
|
||||||
var base64Encoder = base64.RawURLEncoding.WithPadding(base64.NoPadding)
|
|
||||||
|
|
||||||
func (t PasteType) String() string {
|
|
||||||
switch t {
|
|
||||||
case PasteTypeUndef:
|
|
||||||
return "unknown"
|
|
||||||
case PasteTypePaste:
|
|
||||||
return "paste"
|
|
||||||
case PasteTypeRedirect:
|
|
||||||
return "redirect"
|
|
||||||
case PasteTypeFileUpload:
|
|
||||||
return "file"
|
|
||||||
default:
|
|
||||||
return "invalid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t PasteState) String() string {
|
|
||||||
switch t {
|
|
||||||
case PasteStateUndef:
|
|
||||||
return "unknown"
|
|
||||||
case PasteStatePresent:
|
|
||||||
return "present"
|
|
||||||
case PasteStateDeleted:
|
|
||||||
return "deleted"
|
|
||||||
default:
|
|
||||||
return "invalid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPaste retrieves a paste from the database.
|
|
||||||
func GetPaste(tx *bolt.Tx, key string) (*Paste, error) {
|
|
||||||
if err := ValidatePasteKey(key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return GetPasteNoValidate(tx, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatePasteKey validates the format of the key that has
|
|
||||||
func ValidatePasteKey(key string) error {
|
|
||||||
internalLen := minKeyLen
|
|
||||||
countingOnes := true
|
|
||||||
for _, ch := range key {
|
|
||||||
limb := strings.IndexRune(base64Alphabet, ch)
|
|
||||||
if limb == -1 {
|
|
||||||
return ErrKeyInvalidChar
|
|
||||||
}
|
|
||||||
for i := 5; i >= 0 && countingOnes; i-- {
|
|
||||||
if (limb>>uint(i))&0x1 == 0 {
|
|
||||||
countingOnes = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
internalLen++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if internalLen != len(key) {
|
|
||||||
return ErrKeyInvalidLength
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPasteNoValidate retrieves a paste from the database without validating
|
|
||||||
// the key format first.
|
|
||||||
func GetPasteNoValidate(tx *bolt.Tx, key string) (*Paste, error) {
|
|
||||||
pastesBucket := tx.Bucket([]byte(BucketPastes))
|
|
||||||
if pastesBucket == nil {
|
|
||||||
return nil, errors.Errorf("bucket %v does not exist", BucketPastes)
|
|
||||||
}
|
|
||||||
storedBytes := pastesBucket.Get([]byte(key))
|
|
||||||
if storedBytes == nil {
|
|
||||||
return nil, ErrPasteDoesNotExist
|
|
||||||
}
|
|
||||||
return decodePaste(storedBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodePaste(storedBytes []byte) (*Paste, error) {
|
|
||||||
p := &Paste{}
|
|
||||||
err := gobmarsh.Unmarshal(storedBytes, p)
|
|
||||||
return p, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves this Paste to the database.
|
|
||||||
func (p *Paste) Save(tx *bolt.Tx) error {
|
|
||||||
bucket := tx.Bucket([]byte(BucketPastes))
|
|
||||||
if bucket == nil {
|
|
||||||
return errors.Errorf("bucket %v does not exist", BucketPastes)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := gobmarsh.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "encoding for database failed")
|
|
||||||
}
|
|
||||||
if err := bucket.Put([]byte(p.Key), buf); err != nil {
|
|
||||||
return errors.Wrap(err, "database transaction failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes this Paste from the database.
|
|
||||||
func (p *Paste) Delete(tx *bolt.Tx, fs *FileStore) error {
|
|
||||||
// Remove the (maybe) attached file
|
|
||||||
if p.Type == PasteTypeFileUpload {
|
|
||||||
fuID, err := uuid.FromBytes(p.Content)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to parse uuid")
|
|
||||||
}
|
|
||||||
fu, err := GetFileUpload(tx, fuID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to find file in database")
|
|
||||||
}
|
|
||||||
if err := fu.Delete(tx, fs); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to remove file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the old paste with a new empty paste
|
|
||||||
p.Type = PasteTypeUndef
|
|
||||||
p.State = PasteStateDeleted
|
|
||||||
p.Content = []byte{}
|
|
||||||
if err := p.Save(tx); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to delete paste in database")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedirectURL returns the URL from this paste.
|
|
||||||
//
|
|
||||||
// This function assumes that the paste is valid. If the paste struct is
|
|
||||||
// corrupted in some way, this function will panic.
|
|
||||||
func (p *Paste) RedirectURL() *url.URL {
|
|
||||||
if p.Type != PasteTypeRedirect {
|
|
||||||
panic("expected p.Type to be PasteTypeRedirect")
|
|
||||||
}
|
|
||||||
rawurl := string(p.Content)
|
|
||||||
urlParse, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrapf(err, "invalid URL ('%v') in database for key '%v'", rawurl, p.Key))
|
|
||||||
}
|
|
||||||
return urlParse
|
|
||||||
}
|
|
||||||
|
|
||||||
// GeneratePasteKey generates a new paste key. It will ensure that the newly
|
|
||||||
// generated paste key does not already exist in the database.
|
|
||||||
// The running time of this function is in O(log N), where N is the amount of
|
|
||||||
// keys stored in the url-shorten database.
|
|
||||||
// In tx, a Bolt transaction is given. Use minimumEntropy to set the mimimum
|
|
||||||
// guessing entropy of the generated key.
|
|
||||||
func GeneratePasteKey(tx *bolt.Tx, minimumEntropy int) (string, error) {
|
|
||||||
pastesBucket := tx.Bucket([]byte(BucketPastes))
|
|
||||||
if pastesBucket == nil {
|
|
||||||
return "", errors.Errorf("bucket %v does not exist", BucketPastes)
|
|
||||||
}
|
|
||||||
|
|
||||||
epoch := 0
|
|
||||||
var key string
|
|
||||||
for {
|
|
||||||
var err error
|
|
||||||
key, err = generatePasteKeyInner(epoch, minimumEntropy)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrap(err, "url-key generation failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
found := pastesBucket.Get([]byte(key))
|
|
||||||
if found == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
isReserved := false
|
|
||||||
for _, reservedKey := range ReservedPasteKeys {
|
|
||||||
if strings.HasPrefix(key, reservedKey) {
|
|
||||||
isReserved = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isReserved {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
epoch++
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatePasteKeyInner generates a new paste key, but leaves the
|
|
||||||
// uniqueness and is-reserved checks to the caller. That is, it only
|
|
||||||
// generates a random key in the correct (syntactical) format.
|
|
||||||
// Both epoch and entropy can be used to set the key length. Epoch is used
|
|
||||||
// to prevent collisions in retrying to generate new keys. Entropy (in bits)
|
|
||||||
// is used to ensure that a new key has at least some amount of guessing
|
|
||||||
// entropy.
|
|
||||||
func generatePasteKeyInner(epoch, entropy int) (string, error) {
|
|
||||||
entropyEpoch := entropy
|
|
||||||
entropyEpoch -= minKeyLen * 6 // First 4 characters provide 24 bits.
|
|
||||||
entropyEpoch++ // One bit less because of '0' bit.
|
|
||||||
entropyEpoch = (entropyEpoch-1)/5 + 1 // 5 bits for every added epoch.
|
|
||||||
if epoch < entropyEpoch {
|
|
||||||
epoch = entropyEpoch
|
|
||||||
}
|
|
||||||
urlKey := make([]byte, minKeyLen+epoch)
|
|
||||||
_, err := rand.Read(urlKey)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// Put all the values in the range 0..64 for easier base64-encoding
|
|
||||||
for i := 0; i < len(urlKey); i++ {
|
|
||||||
urlKey[i] &= 0x3F
|
|
||||||
}
|
|
||||||
// Implement truncate-resistance by forcing the prefix to
|
|
||||||
// 0b111110xxxxxxxxxx
|
|
||||||
// ^----- {epoch} ones followed by a single 0
|
|
||||||
//
|
|
||||||
// Example when epoch is 1: prefix is 0b10.
|
|
||||||
i := 0
|
|
||||||
for i < epoch {
|
|
||||||
// Set this bit to 1
|
|
||||||
limb := i / 6
|
|
||||||
bit := i % 6
|
|
||||||
urlKey[limb] |= 1 << uint(5-bit)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
// Finally set the next bit to 0
|
|
||||||
limb := i / 6
|
|
||||||
bit := i % 6
|
|
||||||
urlKey[limb] &= ^(1 << uint(5-bit))
|
|
||||||
|
|
||||||
// Convert this ID to a canonical base64 notation
|
|
||||||
for i := range urlKey {
|
|
||||||
urlKey[i] = base64Alphabet[urlKey[i]]
|
|
||||||
}
|
|
||||||
return string(urlKey), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateDeleteToken generates a new (random) delete token.
|
|
||||||
func GenerateDeleteToken() (string, error) {
|
|
||||||
var deleteToken [16]byte
|
|
||||||
_, err := rand.Read(deleteToken[:])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(deleteToken[:]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllPastes tries to retrieve all the Paste objects from the database.
|
|
||||||
func AllPastes(tx *bolt.Tx) ([]Paste, error) {
|
|
||||||
bucket := tx.Bucket([]byte(BucketPastes))
|
|
||||||
if bucket == nil {
|
|
||||||
return nil, errors.Errorf("bucket %v does not exist", BucketPastes)
|
|
||||||
}
|
|
||||||
var ps []Paste
|
|
||||||
err := bucket.ForEach(func(_, storedBytes []byte) error {
|
|
||||||
p, err := decodePaste(storedBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ps = append(ps, *p)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ps, nil
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package boltdb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestValidatePasteKey(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
key string
|
|
||||||
wantErr bool
|
|
||||||
errKind error
|
|
||||||
}{
|
|
||||||
{"xd42__", false, nil},
|
|
||||||
{"xd42_*", true, ErrKeyInvalidChar},
|
|
||||||
{"xd42_/", true, ErrKeyInvalidChar},
|
|
||||||
{"xd42_=", true, ErrKeyInvalidChar},
|
|
||||||
{"xd42_", true, ErrKeyInvalidLength},
|
|
||||||
{"xd42", true, ErrKeyInvalidLength},
|
|
||||||
{"xd4", true, ErrKeyInvalidLength},
|
|
||||||
{"xd", true, ErrKeyInvalidLength},
|
|
||||||
{"x", true, ErrKeyInvalidLength},
|
|
||||||
{"", true, ErrKeyInvalidLength},
|
|
||||||
|
|
||||||
{"KoJ5", false, nil},
|
|
||||||
|
|
||||||
{"__dGSJIIbBpr-SD0", false, nil},
|
|
||||||
{"__dGSJIIbBpr-SD", true, ErrKeyInvalidLength},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.key, func(t *testing.T) {
|
|
||||||
err := ValidatePasteKey(tt.key)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("ValidatePasteKey() got error = %v, want error %v", err != nil, tt.wantErr)
|
|
||||||
}
|
|
||||||
if (err != nil) && err != tt.errKind {
|
|
||||||
t.Errorf("ValidatePasteKey() error = %v, want errKind %v", err, tt.errKind)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,11 @@ var (
|
|||||||
LogLevel: logger.Warn,
|
LogLevel: logger.Warn,
|
||||||
Colorful: true,
|
Colorful: true,
|
||||||
})
|
})
|
||||||
gormConfig = gorm.Config{Logger: gormLogger, PrepareStmt: true}
|
gormConfig = gorm.Config{
|
||||||
|
Logger: gormLogger,
|
||||||
|
PrepareStmt: true,
|
||||||
|
DisableForeignKeyConstraintWhenMigrating: true,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// OpenDBFromEnvironment tries to open an SQL database, described by
|
// OpenDBFromEnvironment tries to open an SQL database, described by
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ type FileUpload struct {
|
|||||||
// UUID publically identifies this FileUpload.
|
// UUID publically identifies this FileUpload.
|
||||||
PubID uuid.UUID `gorm:"uniqueIndex"`
|
PubID uuid.UUID `gorm:"uniqueIndex"`
|
||||||
|
|
||||||
|
PasteID uint
|
||||||
|
|
||||||
// FileName contains the original filename of this FileUpload.
|
// FileName contains the original filename of this FileUpload.
|
||||||
FileName string
|
FileName string
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,69 @@ func Gormigrate(db *gorm.DB) *gormigrate.Gormigrate {
|
|||||||
}
|
}
|
||||||
return tx.AutoMigrate(&FileUpload{}, &Paste{})
|
return tx.AutoMigrate(&FileUpload{}, &Paste{})
|
||||||
},
|
},
|
||||||
Rollback: func(tx *gorm.DB) error {
|
},
|
||||||
return tx.Migrator().DropTable(&FileUpload{}, &Paste{})
|
{
|
||||||
|
ID: "202107231722",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
// Update the schema
|
||||||
|
type FileUpload struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
State FileUploadState `gorm:"index"`
|
||||||
|
PubID uuid.UUID `gorm:"uniqueIndex"`
|
||||||
|
PasteID uint
|
||||||
|
FileName string
|
||||||
|
ContentType string
|
||||||
|
Checksum uint32
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt gorm.DeletedAt
|
||||||
|
}
|
||||||
|
type Paste struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Type PasteType `gorm:"index"`
|
||||||
|
State PasteState `gorm:"index"`
|
||||||
|
Content []byte
|
||||||
|
URL string
|
||||||
|
FileUpload *FileUpload
|
||||||
|
Key string `gorm:"uniqueIndex"`
|
||||||
|
DeleteToken string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt gorm.DeletedAt
|
||||||
|
}
|
||||||
|
err := tx.AutoMigrate(&FileUpload{}, &Paste{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate the data
|
||||||
|
pastes := make([]Paste, 0)
|
||||||
|
if err := tx.Model(&Paste{}).Find(&pastes).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, p := range pastes {
|
||||||
|
switch p.Type {
|
||||||
|
case PasteTypeRedirect:
|
||||||
|
p.URL = string(p.Content)
|
||||||
|
tx.Model(p).Select("Content", "URL").Updates(p)
|
||||||
|
case PasteTypeFileUpload:
|
||||||
|
var id uuid.UUID
|
||||||
|
var fus []FileUpload
|
||||||
|
copy(id[:], p.Content)
|
||||||
|
if err := db.Unscoped().Limit(1).Where("pub_id = ?", id).Find(&fus).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.FileUpload = &fus[0]
|
||||||
|
tx.Model(p).Select("Content", "FileUpload").Updates(p)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently there is a bug in GORM, which causes a nil ptr
|
||||||
|
// dereference when you try dropping a column in an sqlite3
|
||||||
|
// database.
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PasteType describes the type of Paste (i.e. file, redirect, [...]).
|
// PasteType describes the type of Paste (i.e. file, redirect, [...]).
|
||||||
@@ -24,7 +24,8 @@ type Paste struct {
|
|||||||
ID uint `gorm:"primaryKey"`
|
ID uint `gorm:"primaryKey"`
|
||||||
Type PasteType `gorm:"index"`
|
Type PasteType `gorm:"index"`
|
||||||
State PasteState `gorm:"index"`
|
State PasteState `gorm:"index"`
|
||||||
Content []byte
|
URL string
|
||||||
|
FileUpload *FileUpload
|
||||||
Key string `gorm:"uniqueIndex"`
|
Key string `gorm:"uniqueIndex"`
|
||||||
DeleteToken string
|
DeleteToken string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
@@ -146,7 +147,7 @@ func ValidatePasteKey(key string) error {
|
|||||||
// the key format first.
|
// the key format first.
|
||||||
func GetPasteNoValidate(db *gorm.DB, key string) (*Paste, error) {
|
func GetPasteNoValidate(db *gorm.DB, key string) (*Paste, error) {
|
||||||
var ps []Paste
|
var ps []Paste
|
||||||
if err := db.Unscoped().Limit(1).Where("key = ?", key).Find(&ps).Error; err != nil {
|
if err := db.Unscoped().Preload(clause.Associations).Where("key = ?", key).Find(&ps).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(ps) == 0 {
|
if len(ps) == 0 {
|
||||||
@@ -163,16 +164,8 @@ func (p *Paste) Save(db *gorm.DB) error {
|
|||||||
// Delete deletes this Paste from the database.
|
// Delete deletes this Paste from the database.
|
||||||
func (p *Paste) Delete(db *gorm.DB, fs *FileStore) error {
|
func (p *Paste) Delete(db *gorm.DB, fs *FileStore) error {
|
||||||
// Remove the (maybe) attached file
|
// Remove the (maybe) attached file
|
||||||
if p.Type == PasteTypeFileUpload {
|
if p.FileUpload != nil {
|
||||||
fuID, err := uuid.FromBytes(p.Content)
|
if err := p.FileUpload.Delete(db, fs); err != nil {
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to parse uuid")
|
|
||||||
}
|
|
||||||
fu, err := GetFileUpload(db, fuID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to find file in database")
|
|
||||||
}
|
|
||||||
if err := fu.Delete(db, fs); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to remove file")
|
return errors.Wrap(err, "failed to remove file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +173,9 @@ func (p *Paste) Delete(db *gorm.DB, fs *FileStore) error {
|
|||||||
// Wipe the old paste
|
// Wipe the old paste
|
||||||
p.Type = PasteTypeUndef
|
p.Type = PasteTypeUndef
|
||||||
p.State = PasteStateDeleted
|
p.State = PasteStateDeleted
|
||||||
p.Content = []byte{}
|
p.URL = ""
|
||||||
|
p.FileUpload = nil
|
||||||
|
|
||||||
if err := db.Save(&p).Error; err != nil {
|
if err := db.Save(&p).Error; err != nil {
|
||||||
return errors.Wrap(err, "failed to wipe paste in database")
|
return errors.Wrap(err, "failed to wipe paste in database")
|
||||||
}
|
}
|
||||||
@@ -199,10 +194,9 @@ func (p *Paste) RedirectURL() *url.URL {
|
|||||||
if p.Type != PasteTypeRedirect {
|
if p.Type != PasteTypeRedirect {
|
||||||
panic("expected p.Type to be PasteTypeRedirect")
|
panic("expected p.Type to be PasteTypeRedirect")
|
||||||
}
|
}
|
||||||
rawurl := string(p.Content)
|
urlParse, err := url.Parse(p.URL)
|
||||||
urlParse, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(errors.Wrapf(err, "invalid URL ('%v') in database for key '%v'", rawurl, p.Key))
|
panic(errors.Wrapf(err, "invalid URL ('%v') in database for key '%v'", p.URL, p.Key))
|
||||||
}
|
}
|
||||||
return urlParse
|
return urlParse
|
||||||
}
|
}
|
||||||
|
|||||||
96
metrics.go
96
metrics.go
@@ -14,42 +14,59 @@ import (
|
|||||||
|
|
||||||
const metricNamespace = "rushlink"
|
const metricNamespace = "rushlink"
|
||||||
|
|
||||||
var metricRequestsTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
|
// metricURLsTotalGauge counts the number of requests that are handled by
|
||||||
|
// the application, partitioned by status code and HTTP method.
|
||||||
|
//
|
||||||
|
// This counter is updated by the router.
|
||||||
|
var metricRequestsTotalCounter = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
Namespace: metricNamespace,
|
Namespace: metricNamespace,
|
||||||
Subsystem: "http",
|
Subsystem: "http",
|
||||||
Name: "requests_total",
|
Name: "requests_total",
|
||||||
Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||||
}, []string{"code", "method"})
|
}, []string{"code", "method"})
|
||||||
|
|
||||||
func metricURLsTotal(database *db.Database) float64 {
|
// metricRequestsLatencyNanoSeconds keeps track of the request latencies for
|
||||||
var metric float64
|
// each http request.
|
||||||
if err := database.Transaction(func(tx *db.Database) error {
|
//
|
||||||
var count int64
|
// This historogram is updated by the router.
|
||||||
if err := database.Model(&db.Paste{}).Count(&count).Error; err != nil {
|
var metricRequestsLatencyNanoSeconds = prometheus.NewHistogramVec(
|
||||||
return err
|
prometheus.HistogramOpts{
|
||||||
}
|
Namespace: metricNamespace,
|
||||||
metric = float64(count)
|
Subsystem: "http",
|
||||||
return nil
|
Name: "request_duration_seconds",
|
||||||
}); err != nil {
|
Buckets: []float64{
|
||||||
log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
|
float64(500e-6),
|
||||||
return 0
|
float64(1e-3),
|
||||||
}
|
float64(2e-3),
|
||||||
return metric
|
float64(5e-3),
|
||||||
}
|
float64(10e-3),
|
||||||
|
float64(20e-3),
|
||||||
|
float64(50e-3),
|
||||||
|
},
|
||||||
|
Help: "The latency of each HTTP request, partitioned by status code and HTTP method.",
|
||||||
|
}, []string{"code", "method"})
|
||||||
|
|
||||||
|
// metricURLsTotalGauge measures the amount of pastes stored in the database,
|
||||||
|
// partitioned by type and state.
|
||||||
|
//
|
||||||
|
// Its values are computed on the fly by updateMetrics().
|
||||||
|
var metricURLsTotalGauge = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Namespace: metricNamespace,
|
||||||
|
Subsystem: "pastes",
|
||||||
|
Name: "urls_total",
|
||||||
|
Help: "The current amount of pastes in the database, partitioned by state and type.",
|
||||||
|
}, []string{"state", "type"})
|
||||||
|
|
||||||
// StartMetricsServer starts sering Prometheus metrics exports on addr
|
// StartMetricsServer starts sering Prometheus metrics exports on addr
|
||||||
func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
|
func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
|
||||||
prometheus.MustRegister(metricRequestsTotalCounter)
|
prometheus.MustRegister(metricRequestsTotalCounter)
|
||||||
|
prometheus.MustRegister(metricRequestsLatencyNanoSeconds)
|
||||||
prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
|
prometheus.MustRegister(metricURLsTotalGauge)
|
||||||
Namespace: metricNamespace,
|
|
||||||
Subsystem: "pastes",
|
|
||||||
Name: "urls_total",
|
|
||||||
Help: "The current amount of pastes in the database.",
|
|
||||||
}, func() float64 { return metricURLsTotal(database) }))
|
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.Handle("/metrics", promhttp.Handler()).Methods("GET")
|
router.Handle("/metrics", &MetricsHandler{database}).Methods("GET")
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
@@ -58,3 +75,34 @@ func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
|
|||||||
}
|
}
|
||||||
log.Fatal(srv.ListenAndServe())
|
log.Fatal(srv.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MetricsHandler struct {
|
||||||
|
db *db.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mh *MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
mh.updateMetrics()
|
||||||
|
promhttp.Handler().ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mh *MetricsHandler) updateMetrics() {
|
||||||
|
// Update metricURLsTotalGauge
|
||||||
|
results := make([](struct {
|
||||||
|
Type db.PasteType
|
||||||
|
State db.PasteState
|
||||||
|
Count float64
|
||||||
|
}), 0)
|
||||||
|
query := mh.db.Unscoped().Model(&db.Paste{}).Select("type", "state", "COUNT(*) as count").Group("type, state").Find(&results)
|
||||||
|
if err := query.Error; err != nil {
|
||||||
|
log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
metricURLsTotalGauge.Reset()
|
||||||
|
for _, r := range results {
|
||||||
|
labels := map[string]string{
|
||||||
|
"type": r.Type.String(),
|
||||||
|
"state": r.State.String(),
|
||||||
|
}
|
||||||
|
metricURLsTotalGauge.With(labels).Set(r.Count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
10
router.go
10
router.go
@@ -58,10 +58,18 @@ func (rl *rushlink) recoveryMiddleware(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
func (rl *rushlink) metricsMiddleware(next http.Handler) http.Handler {
|
func (rl *rushlink) metricsMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tick := time.Now()
|
||||||
srw := statusResponseWriter{Inner: w}
|
srw := statusResponseWriter{Inner: w}
|
||||||
next.ServeHTTP(&srw, r)
|
next.ServeHTTP(&srw, r)
|
||||||
|
tock := time.Now()
|
||||||
|
|
||||||
status := strconv.Itoa(srw.StatusCode)
|
status := strconv.Itoa(srw.StatusCode)
|
||||||
metricRequestsTotalCounter.WithLabelValues(status, r.Method).Inc()
|
labels := map[string]string{"code": status, "method": r.Method}
|
||||||
|
// Update requests counter metric
|
||||||
|
metricRequestsTotalCounter.With(labels).Inc()
|
||||||
|
// Update request latency metric
|
||||||
|
elapsed := tock.Sub(tick)
|
||||||
|
metricRequestsLatencyNanoSeconds.With(labels).Observe(elapsed.Seconds())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user