mirror of
https://github.com/axllent/mailpit.git
synced 2025-01-26 03:52:09 +02:00
feature: Switch backend storage to use SQLite
BREAKING CHANGE: This release includes a major backend storage change (SQLite) that will render any previously-saved messages useless. Please delete old data to free up space. For more information see https://github.com/axllent/mailpit/issues/10
This commit is contained in:
parent
9f5d329105
commit
eff483c1c4
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,5 +2,6 @@
|
|||||||
/send
|
/send
|
||||||
/server/ui/dist
|
/server/ui/dist
|
||||||
/Makefile
|
/Makefile
|
||||||
/mailpit
|
/mailpit*
|
||||||
*.old
|
*.old
|
||||||
|
*.db
|
||||||
|
@ -23,9 +23,9 @@ Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
|||||||
- Real-time web UI updates using web sockets for new mail
|
- Real-time web UI updates using web sockets for new mail
|
||||||
- Optional browser notifications for new mail (HTTPS only)
|
- Optional browser notifications for new mail (HTTPS only)
|
||||||
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
||||||
- Email storage either in memory or disk ([see wiki](https://github.com/axllent/mailpit/wiki/Email-storage))
|
- Email storage either in a temporary or persistent database ([see wiki](https://github.com/axllent/mailpit/wiki/Email-storage))
|
||||||
- Fast SMTP processing & storing - approximately 300-600 emails per second depending on CPU, network speed & email size
|
- Fast SMTP processing & storing - approximately 300-600 emails per second depending on CPU, network speed & email size
|
||||||
- Can handle tens of thousands of emails
|
- Can handle hundreds of thousands of emails
|
||||||
- Optional SMTP with STARTTLS & SMTP authentication ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-with-STARTTLS-and-authentication))
|
- Optional SMTP with STARTTLS & SMTP authentication ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-with-STARTTLS-and-authentication))
|
||||||
- Optional HTTPS for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/HTTPS))
|
- Optional HTTPS for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/HTTPS))
|
||||||
- Optional basic authentication for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/Basic-authentication))
|
- Optional basic authentication for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/Basic-authentication))
|
||||||
|
13
cmd/root.go
13
cmd/root.go
@ -1,6 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -72,8 +73,8 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||||
|
|
||||||
// defaults from envars if provided
|
// defaults from envars if provided
|
||||||
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
if len(os.Getenv("MP_DATA_FILE")) > 0 {
|
||||||
config.DataDir = os.Getenv("MP_DATA_DIR")
|
config.DataFile = os.Getenv("MP_DATA_FILE")
|
||||||
}
|
}
|
||||||
if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 {
|
if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 {
|
||||||
config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR")
|
config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR")
|
||||||
@ -116,7 +117,13 @@ func init() {
|
|||||||
config.UISSLKey = os.Getenv("MP_SSL_KEY")
|
config.UISSLKey = os.Getenv("MP_SSL_KEY")
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.Flags().StringVarP(&config.DataDir, "data", "d", config.DataDir, "Optional path to store persistent data")
|
// deprecated 2022/08/28
|
||||||
|
if len(os.Getenv("MP_DATA_DIR")) > 0 {
|
||||||
|
fmt.Println("MP_DATA_DIR has been deprecated, use MP_DATA_FILE")
|
||||||
|
config.DataFile = os.Getenv("MP_DATA_DIR")
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.Flags().StringVarP(&config.DataFile, "data", "d", config.DataFile, "Database file to store persistent data")
|
||||||
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port")
|
||||||
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
|
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
|
||||||
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store")
|
rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store")
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/tg123/go-htpasswd"
|
"github.com/tg123/go-htpasswd"
|
||||||
@ -16,8 +17,8 @@ var (
|
|||||||
// HTTPListen to listen on <interface>:<port>
|
// HTTPListen to listen on <interface>:<port>
|
||||||
HTTPListen = "0.0.0.0:8025"
|
HTTPListen = "0.0.0.0:8025"
|
||||||
|
|
||||||
// DataDir for mail (optional)
|
// DataFile for mail (optional)
|
||||||
DataDir string
|
DataFile string
|
||||||
|
|
||||||
// MaxMessages is the maximum number of messages a mailbox can have (auto-pruned every minute)
|
// MaxMessages is the maximum number of messages a mailbox can have (auto-pruned every minute)
|
||||||
MaxMessages = 500
|
MaxMessages = 500
|
||||||
@ -55,6 +56,10 @@ var (
|
|||||||
|
|
||||||
// VerifyConfig wil do some basic checking
|
// VerifyConfig wil do some basic checking
|
||||||
func VerifyConfig() error {
|
func VerifyConfig() error {
|
||||||
|
if DataFile != "" && isDir(DataFile) {
|
||||||
|
DataFile = filepath.Join(DataFile, "mailpit.db")
|
||||||
|
}
|
||||||
|
|
||||||
re := regexp.MustCompile(`^[a-zA-Z0-9\.\-]{3,}:\d{2,}$`)
|
re := regexp.MustCompile(`^[a-zA-Z0-9\.\-]{3,}:\d{2,}$`)
|
||||||
if !re.MatchString(SMTPListen) {
|
if !re.MatchString(SMTPListen) {
|
||||||
return errors.New("SMTP bind should be in the format of <ip>:<port>")
|
return errors.New("SMTP bind should be in the format of <ip>:<port>")
|
||||||
@ -131,3 +136,13 @@ func isFile(path string) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDir returns whether a path is a directory
|
||||||
|
func isDir(path string) bool {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if os.IsNotExist(err) || !info.IsDir() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
51
go.mod
51
go.mod
@ -3,54 +3,59 @@ module github.com/axllent/mailpit
|
|||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244
|
||||||
github.com/axllent/semver v0.0.1
|
github.com/axllent/semver v0.0.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/jhillyerd/enmime v0.10.0
|
github.com/jhillyerd/enmime v0.10.0
|
||||||
github.com/k3a/html2text v1.0.8
|
github.com/k3a/html2text v1.0.8
|
||||||
github.com/klauspost/compress v1.15.9
|
github.com/klauspost/compress v1.15.9
|
||||||
|
github.com/leporo/sqlf v1.3.0
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/mhale/smtpd v0.8.0
|
github.com/mhale/smtpd v0.8.0
|
||||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/tg123/go-htpasswd v1.2.0
|
github.com/tg123/go-htpasswd v1.2.0
|
||||||
|
modernc.org/sqlite v1.18.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cznic/ql v1.2.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.2 // indirect
|
|
||||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||||
github.com/golang/glog v1.0.0 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
|
||||||
github.com/google/flatbuffers v2.0.6+incompatible // indirect
|
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/google/orderedcode v0.0.1 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/kr/pretty v0.3.0 // indirect
|
github.com/kr/pretty v0.3.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rivo/uniseg v0.3.1 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/satori/go.uuid v1.2.0 // indirect
|
github.com/rivo/uniseg v0.3.4 // indirect
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/stretchr/testify v1.7.2 // indirect
|
github.com/stretchr/testify v1.7.2 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b // indirect
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 // indirect
|
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
lukechampine.com/uint128 v1.2.0 // indirect
|
||||||
|
modernc.org/cc/v3 v3.36.3 // indirect
|
||||||
|
modernc.org/ccgo/v3 v3.16.9 // indirect
|
||||||
|
modernc.org/libc v1.17.1 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.2.1 // indirect
|
||||||
|
modernc.org/opt v0.1.3 // indirect
|
||||||
|
modernc.org/strutil v1.1.3 // indirect
|
||||||
|
modernc.org/token v1.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
267
go.sum
267
go.sum
@ -1,102 +1,62 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244 h1:dqzm54OhCqY8RinR/cx+Ppb0y56Ds5I3wwWhx4XybDg=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/GuiaBolso/darwin v0.0.0-20191218124601-fd6d2aa3d244/go.mod h1:3sqgkckuISJ5rs1EpOp6vCvwOUKe/z9vPmyuIlq8Q/A=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
|
||||||
github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E=
|
github.com/axllent/semver v0.0.1 h1:QqF+KSGxgj8QZzSXAvKFqjGWE5792ksOnQhludToK8E=
|
||||||
github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
github.com/axllent/semver v0.0.1/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
||||||
github.com/brianvoe/gofakeit/v6 v6.17.0 h1:obbQTJeHfktJtiZzq0Q1bEpsNUs+yHrYlPVWt7BtmJ4=
|
|
||||||
github.com/brianvoe/gofakeit/v6 v6.17.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07 h1:UHFGPvSxX4C4YBApSPvmUfL8tTvWLj2ryqvT9K4Jcuk=
|
||||||
|
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||||
|
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f h1:7uSNgsgcarNk4oiN/nNkO0J7KAjlsF5Yv5Gf/tFdHas=
|
||||||
|
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
|
||||||
|
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4 h1:CVAqftqbj+exlab+8KJQrE+kNIVlQfJt58j4GxCMF1s=
|
||||||
|
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||||
|
github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00 h1:FHpbUtp2K8X53/b4aFNj4my5n+i3x+CQCZWNuHWH/+E=
|
||||||
|
github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4=
|
||||||
|
github.com/cznic/lldb v1.1.0 h1:AIA+ham6TSJ+XkMe8imQ/g8KPzMUVWAwqUQQdtuMsHs=
|
||||||
|
github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A=
|
||||||
|
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk=
|
||||||
|
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||||
|
github.com/cznic/ql v1.2.0 h1:lcKp95ZtdF0XkWhGnVIXGF8dVD2X+ClS08tglKtf+ak=
|
||||||
|
github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE=
|
||||||
|
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE=
|
||||||
|
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||||
|
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 h1:0rkFMAbn5KBKNpJyHQ6Prb95vIKanmAe62KxsrN+sqA=
|
||||||
|
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||||
|
github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc h1:YKKpTb2BrXN2GYyGaygIdis1vXbE7SSAG9axGWIMClg=
|
||||||
|
github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8=
|
||||||
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 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/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8=
|
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
||||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
|
||||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
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.3.1/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.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.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
|
||||||
github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw=
|
|
||||||
github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
|
||||||
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.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
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 v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
@ -106,47 +66,46 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
|||||||
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/k3a/html2text v1.0.8 h1:rVanLhKilpnJUJs/CNKWzMC4YaQINGxK0rSG8ssmnV0=
|
github.com/k3a/html2text v1.0.8 h1:rVanLhKilpnJUJs/CNKWzMC4YaQINGxK0rSG8ssmnV0=
|
||||||
github.com/k3a/html2text v1.0.8/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
github.com/k3a/html2text v1.0.8/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
|
||||||
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
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/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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/leporo/sqlf v1.3.0 h1:nAkuPYxMIJg/sUmcd1h4avV5iYo8tBTGEGOIR4BIZO8=
|
||||||
|
github.com/leporo/sqlf v1.3.0/go.mod h1:f4dHqIi1+nLl6k1IsNQ8QIEbGWK49th2ei1IxTXk+2E=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
|
||||||
github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0=
|
github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0=
|
||||||
github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4=
|
github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2 h1:PgOWohvpj4qNCyASJ7Q8Ke8ld/wsoi+dQJ05b1ebwus=
|
|
||||||
github.com/ostafen/clover/v2 v2.0.0-alpha.2/go.mod h1:7UyIG46NglzTDRKB4LJiS/enXpuo67Lj05eM8mdhL6M=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk=
|
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
|
||||||
github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
@ -156,143 +115,107 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
|||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
|
||||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
github.com/tg123/go-htpasswd v1.2.0 h1:UKp34m9H467/xklxUxU15wKRru7fwXoTojtxg25ITF0=
|
github.com/tg123/go-htpasswd v1.2.0 h1:UKp34m9H467/xklxUxU15wKRru7fwXoTojtxg25ITF0=
|
||||||
github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM=
|
github.com/tg123/go-htpasswd v1.2.0/go.mod h1:h7IzlfpvIWnVJhNZ0nQ9HaFxHb7pn5uFJYLlEUJa2sM=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
|
||||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
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-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-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0=
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4=
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||||
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
|
||||||
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-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.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.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
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 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||||
|
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
|
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||||
|
modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
|
||||||
|
modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||||
|
modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=
|
||||||
|
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
|
||||||
|
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||||
|
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||||
|
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
|
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
|
||||||
|
modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI=
|
||||||
|
modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
|
||||||
|
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
|
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||||
|
modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs=
|
||||||
|
modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||||
|
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
|
||||||
|
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
|
||||||
|
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||||
|
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||||
|
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||||
|
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
|
||||||
|
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||||
|
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||||
|
@ -20,12 +20,8 @@ type messagesResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return a list of available mailboxes
|
// Return a list of available mailboxes
|
||||||
func apiListMailboxes(w http.ResponseWriter, _ *http.Request) {
|
func apiMailboxStats(w http.ResponseWriter, _ *http.Request) {
|
||||||
res, err := storage.ListMailboxes()
|
res := storage.StatsGet()
|
||||||
if err != nil {
|
|
||||||
httpError(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, _ := json.Marshal(res)
|
bytes, _ := json.Marshal(res)
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
@ -33,24 +29,15 @@ func apiListMailboxes(w http.ResponseWriter, _ *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func apiListMailbox(w http.ResponseWriter, r *http.Request) {
|
func apiListMailbox(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
|
|
||||||
if !storage.MailboxExists(mailbox) {
|
|
||||||
fourOFour(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
start, limit := getStartLimit(r)
|
start, limit := getStartLimit(r)
|
||||||
|
|
||||||
messages, err := storage.List(mailbox, start, limit)
|
messages, err := storage.List(start, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := storage.StatsGet(mailbox)
|
stats := storage.StatsGet()
|
||||||
|
|
||||||
var res messagesResult
|
var res messagesResult
|
||||||
|
|
||||||
@ -72,25 +59,17 @@ func apiSearchMailbox(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
|
|
||||||
if !storage.MailboxExists(mailbox) {
|
|
||||||
fourOFour(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// we will only return up to 200 results
|
// we will only return up to 200 results
|
||||||
start := 0
|
start := 0
|
||||||
limit := 200
|
// limit := 200
|
||||||
|
|
||||||
messages, err := storage.Search(mailbox, search, start, limit)
|
messages, err := storage.Search(search)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := storage.StatsGet(mailbox)
|
stats := storage.StatsGet()
|
||||||
|
|
||||||
var res messagesResult
|
var res messagesResult
|
||||||
|
|
||||||
@ -109,10 +88,9 @@ func apiSearchMailbox(w http.ResponseWriter, r *http.Request) {
|
|||||||
func apiOpenMessage(w http.ResponseWriter, r *http.Request) {
|
func apiOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
|
||||||
msg, err := storage.GetMessage(mailbox, id)
|
msg, err := storage.GetMessage(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -127,11 +105,10 @@ func apiOpenMessage(w http.ResponseWriter, r *http.Request) {
|
|||||||
func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
partID := vars["partID"]
|
partID := vars["partID"]
|
||||||
|
|
||||||
a, err := storage.GetAttachmentPart(mailbox, id, partID)
|
a, err := storage.GetAttachmentPart(id, partID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -150,12 +127,11 @@ func apiDownloadAttachment(w http.ResponseWriter, r *http.Request) {
|
|||||||
func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
|
||||||
dl := r.FormValue("dl")
|
dl := r.FormValue("dl")
|
||||||
|
|
||||||
data, err := storage.GetMessageRaw(mailbox, id)
|
data, err := storage.GetMessageRaw(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -170,11 +146,7 @@ func apiDownloadSource(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Delete all messages in the mailbox
|
// Delete all messages in the mailbox
|
||||||
func apiDeleteAll(w http.ResponseWriter, r *http.Request) {
|
func apiDeleteAll(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
err := storage.DeleteAllMessages()
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
|
|
||||||
err := storage.DeleteAllMessages(mailbox)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -188,10 +160,9 @@ func apiDeleteAll(w http.ResponseWriter, r *http.Request) {
|
|||||||
func apiDeleteOne(w http.ResponseWriter, r *http.Request) {
|
func apiDeleteOne(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
|
||||||
err := storage.DeleteOneMessage(mailbox, id)
|
err := storage.DeleteOneMessage(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -205,10 +176,9 @@ func apiDeleteOne(w http.ResponseWriter, r *http.Request) {
|
|||||||
func apiUnreadOne(w http.ResponseWriter, r *http.Request) {
|
func apiUnreadOne(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
|
||||||
err := storage.UnreadMessage(mailbox, id)
|
err := storage.MarkUnread(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -220,11 +190,7 @@ func apiUnreadOne(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Mark single message as unread
|
// Mark single message as unread
|
||||||
func apiMarkAllRead(w http.ResponseWriter, r *http.Request) {
|
func apiMarkAllRead(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
err := storage.MarkAllRead()
|
||||||
|
|
||||||
mailbox := vars["mailbox"]
|
|
||||||
|
|
||||||
err := storage.MarkAllRead(mailbox)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err.Error())
|
httpError(w, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -34,17 +34,17 @@ func Listen() {
|
|||||||
go websockets.MessageHub.Run()
|
go websockets.MessageHub.Run()
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/api/mailboxes", middleWareFunc(apiListMailboxes))
|
r.HandleFunc("/api/stats", middleWareFunc(apiMailboxStats))
|
||||||
r.HandleFunc("/api/{mailbox}/messages", middleWareFunc(apiListMailbox))
|
r.HandleFunc("/api/messages", middleWareFunc(apiListMailbox))
|
||||||
r.HandleFunc("/api/{mailbox}/search", middleWareFunc(apiSearchMailbox))
|
r.HandleFunc("/api/search", middleWareFunc(apiSearchMailbox))
|
||||||
r.HandleFunc("/api/{mailbox}/delete", middleWareFunc(apiDeleteAll))
|
r.HandleFunc("/api/delete", middleWareFunc(apiDeleteAll))
|
||||||
r.HandleFunc("/api/{mailbox}/events", apiWebsocket)
|
r.HandleFunc("/api/events", apiWebsocket)
|
||||||
r.HandleFunc("/api/{mailbox}/read", apiMarkAllRead)
|
r.HandleFunc("/api/read", apiMarkAllRead)
|
||||||
r.HandleFunc("/api/{mailbox}/{id}/source", middleWareFunc(apiDownloadSource))
|
r.HandleFunc("/api/{id}/source", middleWareFunc(apiDownloadSource))
|
||||||
r.HandleFunc("/api/{mailbox}/{id}/part/{partID}", middleWareFunc(apiDownloadAttachment))
|
r.HandleFunc("/api/{id}/part/{partID}", middleWareFunc(apiDownloadAttachment))
|
||||||
r.HandleFunc("/api/{mailbox}/{id}/delete", middleWareFunc(apiDeleteOne))
|
r.HandleFunc("/api/{id}/delete", middleWareFunc(apiDeleteOne))
|
||||||
r.HandleFunc("/api/{mailbox}/{id}/unread", middleWareFunc(apiUnreadOne))
|
r.HandleFunc("/api/{id}/unread", middleWareFunc(apiUnreadOne))
|
||||||
r.HandleFunc("/api/{mailbox}/{id}", middleWareFunc(apiOpenMessage))
|
r.HandleFunc("/api/{id}", middleWareFunc(apiOpenMessage))
|
||||||
r.PathPrefix("/").Handler(middlewareHandler(http.FileServer(http.FS(serverRoot))))
|
r.PathPrefix("/").Handler(middlewareHandler(http.FileServer(http.FS(serverRoot))))
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentPath: window.location.hash,
|
currentPath: window.location.hash,
|
||||||
mailbox: "catchall",
|
|
||||||
items: [],
|
items: [],
|
||||||
limit: 50,
|
limit: 50,
|
||||||
total: 0,
|
total: 0,
|
||||||
@ -62,11 +61,11 @@ export default {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let params = {};
|
let params = {};
|
||||||
|
|
||||||
let uri = 'api/'+self.mailbox+'/messages';
|
let uri = 'api/messages';
|
||||||
if (self.search) {
|
if (self.search) {
|
||||||
self.searching = true;
|
self.searching = true;
|
||||||
self.items = [];
|
self.items = [];
|
||||||
uri = 'api/'+self.mailbox+'/search'
|
uri = 'api/search'
|
||||||
self.start = 0; // search is displayed on one page
|
self.start = 0; // search is displayed on one page
|
||||||
params['query'] = self.search;
|
params['query'] = self.search;
|
||||||
} else {
|
} else {
|
||||||
@ -131,7 +130,7 @@ export default {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let params = {};
|
let params = {};
|
||||||
|
|
||||||
let uri = 'api/' + self.mailbox + '/' + self.currentPath
|
let uri = 'api/' + self.currentPath
|
||||||
self.get(uri, params, function(response) {
|
self.get(uri, params, function(response) {
|
||||||
for (let i in self.items) {
|
for (let i in self.items) {
|
||||||
if (self.items[i].ID == self.currentPath) {
|
if (self.items[i].ID == self.currentPath) {
|
||||||
@ -149,7 +148,7 @@ export default {
|
|||||||
if (a.ContentID != '') {
|
if (a.ContentID != '') {
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('cid:'+a.ContentID, 'g'),
|
new RegExp('cid:'+a.ContentID, 'g'),
|
||||||
window.location.origin+'/api/'+self.mailbox+'/'+d.ID+'/part/'+a.PartID
|
window.location.origin+'/api/'+d.ID+'/part/'+a.PartID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,7 +160,7 @@ export default {
|
|||||||
if (a.ContentID != '') {
|
if (a.ContentID != '') {
|
||||||
d.HTML = d.HTML.replace(
|
d.HTML = d.HTML.replace(
|
||||||
new RegExp('cid:'+a.ContentID, 'g'),
|
new RegExp('cid:'+a.ContentID, 'g'),
|
||||||
window.location.origin+'/api/'+self.mailbox+'/'+d.ID+'/part/'+a.PartID
|
window.location.origin+'/api/'+d.ID+'/part/'+a.PartID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +172,7 @@ export default {
|
|||||||
|
|
||||||
deleteAll: function() {
|
deleteAll: function() {
|
||||||
let self = this;
|
let self = this;
|
||||||
let uri = 'api/' + self.mailbox + '/delete'
|
let uri = 'api/delete'
|
||||||
self.get(uri, false, function(response) {
|
self.get(uri, false, function(response) {
|
||||||
self.reloadMessages();
|
self.reloadMessages();
|
||||||
});
|
});
|
||||||
@ -184,7 +183,7 @@ export default {
|
|||||||
if (!self.message) {
|
if (!self.message) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let uri = 'api/' + self.mailbox + '/' + self.message.ID + '/delete'
|
let uri = 'api/' + self.message.ID + '/delete'
|
||||||
self.get(uri, false, function(response) {
|
self.get(uri, false, function(response) {
|
||||||
window.location.hash = "";
|
window.location.hash = "";
|
||||||
self.scrollInPlace = true;
|
self.scrollInPlace = true;
|
||||||
@ -198,7 +197,7 @@ export default {
|
|||||||
if (!self.message) {
|
if (!self.message) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let uri = 'api/' + self.mailbox + '/' + self.message.ID + '/unread'
|
let uri = 'api/' + self.message.ID + '/unread'
|
||||||
self.get(uri, false, function(response) {
|
self.get(uri, false, function(response) {
|
||||||
window.location.hash = "";
|
window.location.hash = "";
|
||||||
self.scrollInPlace = true;
|
self.scrollInPlace = true;
|
||||||
@ -208,7 +207,7 @@ export default {
|
|||||||
|
|
||||||
markAllRead: function() {
|
markAllRead: function() {
|
||||||
let self = this;
|
let self = this;
|
||||||
let uri = 'api/' + self.mailbox + '/read'
|
let uri = 'api/read'
|
||||||
self.get(uri, false, function(response) {
|
self.get(uri, false, function(response) {
|
||||||
window.location.hash = "";
|
window.location.hash = "";
|
||||||
self.scrollInPlace = true;
|
self.scrollInPlace = true;
|
||||||
@ -219,7 +218,7 @@ export default {
|
|||||||
// websocket connect
|
// websocket connect
|
||||||
connect: function () {
|
connect: function () {
|
||||||
let wsproto = location.protocol == 'https:' ? 'wss' : 'ws';
|
let wsproto = location.protocol == 'https:' ? 'wss' : 'ws';
|
||||||
let ws = new WebSocket(wsproto + "://" + document.location.host + "/api/"+this.mailbox+"/events");
|
let ws = new WebSocket(wsproto + "://" + document.location.host + "/api/events");
|
||||||
let self = this;
|
let self = this;
|
||||||
ws.onmessage = function (e) {
|
ws.onmessage = function (e) {
|
||||||
let response = JSON.parse(e.data);
|
let response = JSON.parse(e.data);
|
||||||
@ -246,7 +245,6 @@ export default {
|
|||||||
self.scrollInPlace = true;
|
self.scrollInPlace = true;
|
||||||
self.loadMessages();
|
self.loadMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onopen = function () {
|
ws.onopen = function () {
|
||||||
@ -337,7 +335,7 @@ export default {
|
|||||||
<button class="btn btn-outline-secondary me-2" title="Mark unread" v-on:click="markUnread">
|
<button class="btn btn-outline-secondary me-2" title="Mark unread" v-on:click="markUnread">
|
||||||
<i class="bi bi-envelope"></i> <span class="d-none d-md-inline">Mark unread</span>
|
<i class="bi bi-envelope"></i> <span class="d-none d-md-inline">Mark unread</span>
|
||||||
</button>
|
</button>
|
||||||
<a :href="'api/' + mailbox + '/' + message.ID + '/source?dl=1'" class="btn btn-outline-secondary me-2 float-end" title="Download message">
|
<a :href="'api/' + message.ID + '/source?dl=1'" class="btn btn-outline-secondary me-2 float-end" title="Download message">
|
||||||
<i class="bi bi-file-arrow-down-fill"></i> <span class="d-none d-md-inline">Download</span>
|
<i class="bi bi-file-arrow-down-fill"></i> <span class="d-none d-md-inline">Download</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -393,7 +391,7 @@ export default {
|
|||||||
<div class="row flex-fill" style="min-height:0">
|
<div class="row flex-fill" style="min-height:0">
|
||||||
<div class="d-none d-md-block col-lg-2 col-md-3 mh-100 position-relative" style="overflow-y: auto;">
|
<div class="d-none d-md-block col-lg-2 col-md-3 mh-100 position-relative" style="overflow-y: auto;">
|
||||||
<ul class="list-unstyled mt-3 mb-5">
|
<ul class="list-unstyled mt-3 mb-5">
|
||||||
<li v-if="isConnected" title="Messages will auto-load" class="mb-2">
|
<li v-if="isConnected" title="Messages will auto-load" class="mb-3">
|
||||||
<i class="bi bi-power text-success"></i>
|
<i class="bi bi-power text-success"></i>
|
||||||
Connected
|
Connected
|
||||||
</li>
|
</li>
|
||||||
@ -406,7 +404,7 @@ export default {
|
|||||||
<i class="bi bi-envelope me-1" v-if="isConnected"></i>
|
<i class="bi bi-envelope me-1" v-if="isConnected"></i>
|
||||||
<i class="bi bi-arrow-clockwise me-1" v-else></i>
|
<i class="bi bi-arrow-clockwise me-1" v-else></i>
|
||||||
Inbox
|
Inbox
|
||||||
<span class="position-absolute mt-2 ms-4 start-100 translate-middle badge rounded-pill text-bg-secondary" title="Unread messages" v-if="unread">
|
<span style="margin-top: -5px; margin-left: 5px;" class="position-absolute badge rounded-pill text-bg-secondary" title="Unread messages" v-if="unread">
|
||||||
{{ formatNumber(unread) }}
|
{{ formatNumber(unread) }}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
@ -484,7 +482,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Message v-if="message" :message="message" :mailbox="mailbox"></Message>
|
<Message v-if="message" :message="message"></Message>
|
||||||
</div>
|
</div>
|
||||||
<div id="loading" v-if="loading">
|
<div id="loading" v-if="loading">
|
||||||
<div class="d-flex justify-content-center align-items-center h-100">
|
<div class="d-flex justify-content-center align-items-center h-100">
|
||||||
|
@ -5,8 +5,7 @@ import moment from 'moment'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
message: Object,
|
message: Object
|
||||||
mailbox: Object,
|
|
||||||
},
|
},
|
||||||
mixins: [commonMixins],
|
mixins: [commonMixins],
|
||||||
data() {
|
data() {
|
||||||
@ -45,7 +44,7 @@ export default {
|
|||||||
|
|
||||||
var tabEl = document.getElementById('nav-source-tab');
|
var tabEl = document.getElementById('nav-source-tab');
|
||||||
tabEl.addEventListener('shown.bs.tab', function (event) {
|
tabEl.addEventListener('shown.bs.tab', function (event) {
|
||||||
self.srcURI = 'api/' + self.mailbox + '/' + self.message.ID + '/source';
|
self.srcURI = 'api/' + self.message.ID + '/source';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -181,7 +180,7 @@ export default {
|
|||||||
<div class="tab-pane fade" id="nav-mime" role="tabpanel" aria-labelledby="nav-mime-tab"
|
<div class="tab-pane fade" id="nav-mime" role="tabpanel" aria-labelledby="nav-mime-tab"
|
||||||
tabindex="0">
|
tabindex="0">
|
||||||
<div v-if="allAttachments(message)" v-for="part in allAttachments(message)" class="mime-part mb-2">
|
<div v-if="allAttachments(message)" v-for="part in allAttachments(message)" class="mime-part mb-2">
|
||||||
<a :href="'api/'+mailbox+'/'+message.ID+'/part/'+part.PartID" type="button"
|
<a :href="'api/'+message.ID+'/part/'+part.PartID" type="button"
|
||||||
class="btn btn-outline-secondary btn-sm me-2" target="_blank">
|
class="btn btn-outline-secondary btn-sm me-2" target="_blank">
|
||||||
<i class="bi bi-file-arrow-down-fill"></i>
|
<i class="bi bi-file-arrow-down-fill"></i>
|
||||||
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
{{ part.FileName != '' ? part.FileName : '[ unknown ]' }}
|
||||||
|
@ -49,7 +49,7 @@ func (h *Hub) Run() {
|
|||||||
close(client.send)
|
close(client.send)
|
||||||
}
|
}
|
||||||
case message := <-h.Broadcast:
|
case message := <-h.Broadcast:
|
||||||
logger.Log().Debugf("Message received: %s", message)
|
// logger.Log().Debugf("[broadcast] %s", message)
|
||||||
for client := range h.Clients {
|
for client := range h.Clients {
|
||||||
select {
|
select {
|
||||||
case client.send <- message:
|
case client.send <- message:
|
||||||
|
@ -19,7 +19,7 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := storage.Store(storage.DefaultMailbox, data); err != nil {
|
if _, err := storage.Store(data); err != nil {
|
||||||
// Value with size 4800709 exceeded 1048576 limit
|
// Value with size 4800709 exceeded 1048576 limit
|
||||||
re := regexp.MustCompile(`(Value with size \d+ exceeded \d+ limit)`)
|
re := regexp.MustCompile(`(Value with size \d+ exceeded \d+ limit)`)
|
||||||
tooLarge := re.FindStringSubmatch(err.Error())
|
tooLarge := re.FindStringSubmatch(err.Error())
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,145 +5,104 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/axllent/mailpit/config"
|
"github.com/axllent/mailpit/config"
|
||||||
"github.com/jhillyerd/enmime"
|
"github.com/jhillyerd/enmime"
|
||||||
"github.com/ostafen/clover/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testTextEmail []byte
|
testTextEmail []byte
|
||||||
testMimeEmail []byte
|
testMimeEmail []byte
|
||||||
testRuns = 1000
|
testRuns = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTextEmailInserts(t *testing.T) {
|
func TestTextEmailInserts(t *testing.T) {
|
||||||
setup(false)
|
setup()
|
||||||
t.Log("Testing memory storage")
|
defer Close()
|
||||||
|
|
||||||
|
t.Log("Testing text email storage")
|
||||||
|
|
||||||
RepeatTest:
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
assertEqualStats(t, 0, 0)
|
assertEqualStats(t, 0, 0)
|
||||||
|
|
||||||
for i := 0; i < testRuns; i++ {
|
for i := 0; i < testRuns; i++ {
|
||||||
if _, err := Store(DefaultMailbox, testTextEmail); err != nil {
|
if _, err := Store(testTextEmail); err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := Count(DefaultMailbox)
|
assertEqual(t, CountTotal(), testRuns, "Incorrect number of text emails stored")
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqual(t, count, testRuns, "incorrect number of text emails stored")
|
t.Logf("Inserted %d text emails in %s", testRuns, time.Since(start))
|
||||||
|
|
||||||
t.Logf("inserted %d text emails in %s", testRuns, time.Since(start))
|
|
||||||
|
|
||||||
assertEqualStats(t, testRuns, testRuns)
|
assertEqualStats(t, testRuns, testRuns)
|
||||||
|
|
||||||
delStart := time.Now()
|
delStart := time.Now()
|
||||||
if err := DeleteAllMessages(DefaultMailbox); err != nil {
|
if err := DeleteAllMessages(); err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err = Count(DefaultMailbox)
|
assertEqual(t, CountTotal(), 0, "incorrect number of text emails deleted")
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqual(t, count, 0, "incorrect number of text emails deleted")
|
|
||||||
|
|
||||||
t.Logf("deleted %d text emails in %s", testRuns, time.Since(delStart))
|
t.Logf("deleted %d text emails in %s", testRuns, time.Since(delStart))
|
||||||
|
|
||||||
assertEqualStats(t, 0, 0)
|
assertEqualStats(t, 0, 0)
|
||||||
|
|
||||||
db.Close()
|
|
||||||
if config.DataDir == "" {
|
|
||||||
setup(true)
|
|
||||||
t.Logf("Testing physical storage to %s", config.DataDir)
|
|
||||||
defer os.RemoveAll(config.DataDir)
|
|
||||||
goto RepeatTest
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMimeEmailInserts(t *testing.T) {
|
func TestMimeEmailInserts(t *testing.T) {
|
||||||
setup(false)
|
setup()
|
||||||
t.Log("Testing memory storage")
|
defer Close()
|
||||||
|
|
||||||
|
t.Log("Testing mime email storage")
|
||||||
|
|
||||||
RepeatTest:
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
assertEqualStats(t, 0, 0)
|
assertEqualStats(t, 0, 0)
|
||||||
|
|
||||||
for i := 0; i < testRuns; i++ {
|
for i := 0; i < testRuns; i++ {
|
||||||
if _, err := Store(DefaultMailbox, testMimeEmail); err != nil {
|
if _, err := Store(testMimeEmail); err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := Count(DefaultMailbox)
|
assertEqual(t, CountTotal(), testRuns, "Incorrect number of mime emails stored")
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqual(t, count, testRuns, "incorrect number of emails with mime attachments stored")
|
t.Logf("Inserted %d text emails in %s", testRuns, time.Since(start))
|
||||||
|
|
||||||
t.Logf("inserted %d emails with mime attachments in %s", testRuns, time.Since(start))
|
|
||||||
|
|
||||||
assertEqualStats(t, testRuns, testRuns)
|
assertEqualStats(t, testRuns, testRuns)
|
||||||
|
|
||||||
delStart := time.Now()
|
delStart := time.Now()
|
||||||
if err := DeleteAllMessages(DefaultMailbox); err != nil {
|
if err := DeleteAllMessages(); err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err = Count(DefaultMailbox)
|
assertEqual(t, CountTotal(), 0, "incorrect number of mime emails deleted")
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqual(t, count, 0, "incorrect number of emails with mime attachments deleted")
|
t.Logf("deleted %d mime emails in %s", testRuns, time.Since(delStart))
|
||||||
|
|
||||||
t.Logf("deleted %d emails with mime attachments in %s", testRuns, time.Since(delStart))
|
|
||||||
|
|
||||||
assertEqualStats(t, 0, 0)
|
assertEqualStats(t, 0, 0)
|
||||||
|
|
||||||
db.Close()
|
|
||||||
if config.DataDir == "" {
|
|
||||||
setup(true)
|
|
||||||
t.Logf("Testing physical storage to %s", config.DataDir)
|
|
||||||
defer os.RemoveAll(config.DataDir)
|
|
||||||
goto RepeatTest
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRetrieveMimeEmail(t *testing.T) {
|
func TestRetrieveMimeEmail(t *testing.T) {
|
||||||
setup(false)
|
setup()
|
||||||
t.Log("Testing memory storage")
|
defer Close()
|
||||||
|
|
||||||
RepeatTest:
|
t.Log("Testing mime email retrieval")
|
||||||
id, err := Store(DefaultMailbox, testMimeEmail)
|
|
||||||
|
id, err := Store(testMimeEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := GetMessage(DefaultMailbox, id)
|
msg, err := GetMessage(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@ -160,79 +119,26 @@ RepeatTest:
|
|||||||
assertEqual(t, len(msg.Inline), 1, "incorrect number of inline attachments")
|
assertEqual(t, len(msg.Inline), 1, "incorrect number of inline attachments")
|
||||||
assertEqual(t, msg.Inline[0].FileName, "inline-image.jpg", "inline attachment filename does not match")
|
assertEqual(t, msg.Inline[0].FileName, "inline-image.jpg", "inline attachment filename does not match")
|
||||||
|
|
||||||
attachmentData, err := GetAttachmentPart(DefaultMailbox, id, msg.Attachments[0].PartID)
|
attachmentData, err := GetAttachmentPart(id, msg.Attachments[0].PartID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assertEqual(t, len(attachmentData.Content), msg.Attachments[0].Size, "attachment size does not match")
|
assertEqual(t, len(attachmentData.Content), msg.Attachments[0].Size, "attachment size does not match")
|
||||||
|
|
||||||
inlineData, err := GetAttachmentPart(DefaultMailbox, id, msg.Inline[0].PartID)
|
inlineData, err := GetAttachmentPart(id, msg.Inline[0].PartID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assertEqual(t, len(inlineData.Content), msg.Inline[0].Size, "inline attachment size does not match")
|
assertEqual(t, len(inlineData.Content), msg.Inline[0].Size, "inline attachment size does not match")
|
||||||
|
|
||||||
db.Close()
|
|
||||||
|
|
||||||
if config.DataDir == "" {
|
|
||||||
setup(true)
|
|
||||||
t.Logf("Testing physical storage to %s", config.DataDir)
|
|
||||||
defer os.RemoveAll(config.DataDir)
|
|
||||||
goto RepeatTest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDatabaseStats(t *testing.T) {
|
|
||||||
setup(false)
|
|
||||||
t.Log("Testing database stats")
|
|
||||||
assertEqualStats(t, 0, 0)
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
if _, err := Store(DefaultMailbox, testTextEmail); err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqualStats(t, 100, 100)
|
|
||||||
|
|
||||||
// mark 10 as read
|
|
||||||
docs, err := db.FindAll(
|
|
||||||
clover.NewQuery(DefaultMailbox).
|
|
||||||
Limit(10),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range docs {
|
|
||||||
_, err := GetMessage(DefaultMailbox, d.ObjectId())
|
|
||||||
if err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqualStats(t, 100, 90)
|
|
||||||
|
|
||||||
if err := MarkAllRead(DefaultMailbox); err != nil {
|
|
||||||
t.Log("error ", err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEqualStats(t, 100, 0)
|
|
||||||
|
|
||||||
db.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearch(t *testing.T) {
|
func TestSearch(t *testing.T) {
|
||||||
setup(false)
|
setup()
|
||||||
t.Log("Testing memory storage")
|
defer Close()
|
||||||
|
|
||||||
RepeatTest:
|
t.Log("Testing search")
|
||||||
for i := 0; i < testRuns; i++ {
|
for i := 0; i < testRuns; i++ {
|
||||||
msg := enmime.Builder().
|
msg := enmime.Builder().
|
||||||
From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)).
|
From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)).
|
||||||
@ -253,7 +159,7 @@ RepeatTest:
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Store(DefaultMailbox, buf.Bytes()); err != nil {
|
if _, err := Store(buf.Bytes()); err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@ -274,7 +180,7 @@ RepeatTest:
|
|||||||
search = fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i)
|
search = fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
summaries, err := Search(DefaultMailbox, search, 0, 10)
|
summaries, err := Search(search)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@ -290,57 +196,43 @@ RepeatTest:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// search something that will return 200 rsults
|
// search something that will return 200 rsults
|
||||||
summaries, err := Search(DefaultMailbox, "This is the email body", 0, 50)
|
summaries, err := Search("This is the email body")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("error ", err)
|
t.Log("error ", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
assertEqual(t, len(summaries), 50, "50 search results expected")
|
assertEqual(t, len(summaries), testRuns, "search results expected")
|
||||||
|
|
||||||
db.Close()
|
|
||||||
|
|
||||||
if config.DataDir == "" {
|
|
||||||
setup(true)
|
|
||||||
t.Logf("Testing physical storage to %s", config.DataDir)
|
|
||||||
defer os.RemoveAll(config.DataDir)
|
|
||||||
goto RepeatTest
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkImportText(b *testing.B) {
|
func BenchmarkImportText(b *testing.B) {
|
||||||
setup(false)
|
setup()
|
||||||
|
defer Close()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := Store(DefaultMailbox, testTextEmail); err != nil {
|
if _, err := Store(testTextEmail); err != nil {
|
||||||
b.Log("error ", err)
|
b.Log("error ", err)
|
||||||
b.Fail()
|
b.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkImportMime(b *testing.B) {
|
func BenchmarkImportMime(b *testing.B) {
|
||||||
setup(false)
|
setup()
|
||||||
|
defer Close()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := Store(DefaultMailbox, testMimeEmail); err != nil {
|
if _, err := Store(testMimeEmail); err != nil {
|
||||||
b.Log("error ", err)
|
b.Log("error ", err)
|
||||||
b.Fail()
|
b.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(dataDir bool) {
|
func setup() {
|
||||||
config.NoLogging = true
|
config.NoLogging = true
|
||||||
config.MaxMessages = 0
|
config.MaxMessages = 0
|
||||||
|
config.DataFile = ""
|
||||||
if dataDir {
|
|
||||||
config.DataDir = fmt.Sprintf("%s-%d", path.Join(os.TempDir(), "mailpit-tests"), time.Now().UnixNano())
|
|
||||||
} else {
|
|
||||||
config.DataDir = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := InitDB(); err != nil {
|
if err := InitDB(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -368,7 +260,7 @@ func assertEqual(t *testing.T, a interface{}, b interface{}, message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertEqualStats(t *testing.T, total int, unread int) {
|
func assertEqualStats(t *testing.T, total int, unread int) {
|
||||||
s := StatsGet(DefaultMailbox)
|
s := StatsGet()
|
||||||
if total != s.Total {
|
if total != s.Total {
|
||||||
t.Fatal(fmt.Sprintf("Incorrect total mailbox stats: \"%d\" != \"%d\"", total, s.Total))
|
t.Fatal(fmt.Sprintf("Incorrect total mailbox stats: \"%d\" != \"%d\"", total, s.Total))
|
||||||
}
|
}
|
||||||
|
115
storage/stats.go
115
storage/stats.go
@ -1,115 +0,0 @@
|
|||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/axllent/mailpit/data"
|
|
||||||
"github.com/axllent/mailpit/logger"
|
|
||||||
"github.com/ostafen/clover/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
mailboxStats = map[string]data.MailboxStats{}
|
|
||||||
statsLock = sync.RWMutex{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// StatsGet returns the total/unread statistics for a mailbox
|
|
||||||
func StatsGet(mailbox string) data.MailboxStats {
|
|
||||||
mailbox = sanitizeMailboxName(mailbox)
|
|
||||||
|
|
||||||
statsLock.Lock()
|
|
||||||
defer statsLock.Unlock()
|
|
||||||
s, ok := mailboxStats[mailbox]
|
|
||||||
if !ok {
|
|
||||||
return data.MailboxStats{
|
|
||||||
Total: 0,
|
|
||||||
Unread: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh will completely refresh the existing stats for a given mailbox
|
|
||||||
func statsRefresh(mailbox string) error {
|
|
||||||
logger.Log().Debugf("[stats] refreshing stats for %s", mailbox)
|
|
||||||
|
|
||||||
total, err := db.Count(clover.NewQuery(mailbox))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
unread, err := db.Count(clover.NewQuery(mailbox).Where(clover.Field("Read").IsFalse()))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
statsLock.Lock()
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
|
||||||
Total: total,
|
|
||||||
Unread: unread,
|
|
||||||
}
|
|
||||||
statsLock.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func statsAddNewMessage(mailbox string) {
|
|
||||||
statsLock.Lock()
|
|
||||||
s, ok := mailboxStats[mailbox]
|
|
||||||
if ok {
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
|
||||||
Total: s.Total + 1,
|
|
||||||
Unread: s.Unread + 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statsLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete one message from the totals. If the message was unread,
|
|
||||||
// then it will also deduct one from the Unread status.
|
|
||||||
func statsDeleteOneMessage(mailbox string, unread bool) {
|
|
||||||
statsLock.Lock()
|
|
||||||
s, ok := mailboxStats[mailbox]
|
|
||||||
if ok {
|
|
||||||
// deduct from the totals
|
|
||||||
if s.Total > 0 {
|
|
||||||
s.Total = s.Total - 1
|
|
||||||
}
|
|
||||||
// only deduct if the original was unread
|
|
||||||
if unread && s.Unread > 0 {
|
|
||||||
s.Unread = s.Unread - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
|
||||||
Total: s.Total,
|
|
||||||
Unread: s.Unread,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statsLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark one message as read
|
|
||||||
func statsReadOneMessage(mailbox string) {
|
|
||||||
statsLock.Lock()
|
|
||||||
s, ok := mailboxStats[mailbox]
|
|
||||||
if ok {
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
|
||||||
Total: s.Total,
|
|
||||||
Unread: s.Unread - 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statsLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark one message as unread
|
|
||||||
func statsUnreadOneMessage(mailbox string) {
|
|
||||||
statsLock.Lock()
|
|
||||||
s, ok := mailboxStats[mailbox]
|
|
||||||
if ok {
|
|
||||||
mailboxStats[mailbox] = data.MailboxStats{
|
|
||||||
Total: s.Total,
|
|
||||||
Unread: s.Unread + 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statsLock.Unlock()
|
|
||||||
}
|
|
122
storage/utils.go
122
storage/utils.go
@ -1,7 +1,10 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -11,7 +14,7 @@ import (
|
|||||||
"github.com/axllent/mailpit/server/websockets"
|
"github.com/axllent/mailpit/server/websockets"
|
||||||
"github.com/jhillyerd/enmime"
|
"github.com/jhillyerd/enmime"
|
||||||
"github.com/k3a/html2text"
|
"github.com/k3a/html2text"
|
||||||
"github.com/ostafen/clover/v2"
|
"github.com/leporo/sqlf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Return a header field as a []*mail.Address, or "null" is not found/empty
|
// Return a header field as a []*mail.Address, or "null" is not found/empty
|
||||||
@ -47,55 +50,116 @@ func createSearchText(env *enmime.Envelope) string {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanString removed unwanted characters from stored search text and search queries
|
// CleanString removes unwanted characters from stored search text and search queries
|
||||||
func cleanString(str string) string {
|
func cleanString(str string) string {
|
||||||
// remove/replace new lines
|
// remove/replace new lines
|
||||||
re := regexp.MustCompile(`(\r?\n|\t|>|<|"|:|\,|;)`)
|
re := regexp.MustCompile(`(\r?\n|\t|>|<|"|:|\,|;)`)
|
||||||
str = re.ReplaceAllString(str, " ")
|
str = re.ReplaceAllString(str, " ")
|
||||||
|
|
||||||
// remove duplicate whitespace and trim
|
// remove duplicate whitespace and trim
|
||||||
return strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(str)), " "))
|
return strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(str)), " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-prune runs every minute to automatically delete oldest messages
|
// Auto-prune runs every minute to automatically delete oldest messages
|
||||||
// if total is greater than the threshold
|
// if total is greater than the threshold
|
||||||
func pruneCron() {
|
func dbCron() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(60 * time.Second)
|
time.Sleep(60 * time.Second)
|
||||||
mailboxes, err := db.ListCollections()
|
start := time.Now()
|
||||||
if err != nil {
|
|
||||||
logger.Log().Errorf("[db] %s", err)
|
// check if database contains deleted data and has not beein in use
|
||||||
|
// for 5 minutes, if so VACUUM
|
||||||
|
currentTime := time.Now()
|
||||||
|
diff := currentTime.Sub(dbLastAction)
|
||||||
|
if dbDataDeleted && diff.Minutes() > 5 {
|
||||||
|
dbDataDeleted = false
|
||||||
|
_, err := db.Exec("VACUUM")
|
||||||
|
if err == nil {
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
logger.Log().Debugf("[db] compressed idle database in %s", elapsed)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range mailboxes {
|
if config.MaxMessages > 0 {
|
||||||
total, _ := db.Count(clover.NewQuery(m))
|
q := sqlf.Select("ID").
|
||||||
if total > config.MaxMessages {
|
From("mailbox").
|
||||||
limit := total - config.MaxMessages
|
OrderBy("Sort DESC").
|
||||||
if limit > 5000 {
|
Limit(5000).
|
||||||
limit = 5000
|
Offset(config.MaxMessages)
|
||||||
|
|
||||||
|
ids := []string{}
|
||||||
|
if err := q.Query(nil, db, func(row *sql.Rows) {
|
||||||
|
var id string
|
||||||
|
|
||||||
|
if err := row.Scan(&id); err != nil {
|
||||||
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
start := time.Now()
|
ids = append(ids, id)
|
||||||
if err := db.Delete(clover.NewQuery(m).
|
|
||||||
Sort(clover.SortOption{Field: "Created", Direction: 1}).
|
}); err != nil {
|
||||||
Limit(limit)); err != nil {
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
logger.Log().Warnf("Error pruning %s: %s", m, err.Error())
|
continue
|
||||||
continue
|
}
|
||||||
}
|
|
||||||
elapsed := time.Since(start)
|
if len(ids) == 0 {
|
||||||
logger.Log().Infof("Pruned %d messages from %s in %s", limit, m, elapsed)
|
continue
|
||||||
_ = statsRefresh(m)
|
}
|
||||||
if !strings.HasSuffix(m, "_data") {
|
|
||||||
websockets.Broadcast("prune", nil)
|
tx, err := db.BeginTx(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
args[i] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Query(`DELETE FROM mailbox WHERE ID IN (?`+strings.Repeat(",?", len(ids)-1)+`)`, args...) // #nosec
|
||||||
|
if err != nil {
|
||||||
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Query(`DELETE FROM mailbox_data WHERE ID IN (?`+strings.Repeat(",?", len(ids)-1)+`)`, args...) // #nosec
|
||||||
|
if err != nil {
|
||||||
|
logger.Log().Errorf("[db] %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Log().Errorf(err.Error())
|
||||||
|
if err := tx.Rollback(); err != nil {
|
||||||
|
logger.Log().Errorf(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbDataDeleted = true
|
||||||
|
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
logger.Log().Debugf("[db] auto-pruned %d messages in %s", len(ids), elapsed)
|
||||||
|
|
||||||
|
websockets.Broadcast("prune", nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SanitizeMailboxName returns a clean mailbox name
|
// IsFile returns whether a path is a file
|
||||||
// allowing only `alphanumeric` characters and `-“
|
func isFile(path string) bool {
|
||||||
func sanitizeMailboxName(mailbox string) string {
|
info, err := os.Stat(path)
|
||||||
re := regexp.MustCompile(`[^a-zA-Z0-9\-]`)
|
if os.IsNotExist(err) || !info.Mode().IsRegular() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return re.ReplaceAllString(mailbox, "")
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// escPercentChar replaces `%` with `%%` for SQL searches
|
||||||
|
func escPercentChar(s string) string {
|
||||||
|
return strings.ReplaceAll(s, "%", "%%")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user