diff --git a/cmd/pasty/main.go b/cmd/pasty/main.go index 2379afd..d22f389 100644 --- a/cmd/pasty/main.go +++ b/cmd/pasty/main.go @@ -1,52 +1,65 @@ package main import ( - "log" - "time" - + "context" "github.com/lus/pasty/internal/config" + "github.com/lus/pasty/internal/meta" "github.com/lus/pasty/internal/storage" - "github.com/lus/pasty/internal/web" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" + "os/signal" ) func main() { - // Load the configuration - log.Println("Loading the application configuration...") - config.Load() + // Set up the logger + zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs + if !meta.IsProdEnvironment() { + log.Logger = log.Output(zerolog.ConsoleWriter{ + Out: os.Stderr, + }) + log.Warn().Msg("This distribution was compiled for development mode and is thus not meant to be run in production!") + } - // Load the configured storage driver - log.Println("Loading the configured storage driver...") - err := storage.Load() + // Load the configuration + cfg, err := config.Load() if err != nil { - panic(err) + log.Fatal().Err(err).Msg("Could not load the configuration.") + } + + // Adjust the log level + if !meta.IsProdEnvironment() { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + } else { + level, err := zerolog.ParseLevel(cfg.LogLevel) + if err != nil { + log.Warn().Msg("An invalid log level was configured. Falling back to 'info'.") + level = zerolog.InfoLevel + } + zerolog.SetGlobalLevel(level) + } + + // Initialize the configured storage driver + driver, ok := storage.ResolveDriver(cfg.StorageDriver) + if !ok { + log.Fatal().Str("driver_name", cfg.StorageDriver).Msg("An invalid storage driver name was given.") + return + } + log.Info().Str("driver_name", cfg.StorageDriver).Msg("Initializing the storage driver...") + if err := driver.Initialize(context.Background(), cfg); err != nil { + log.Fatal().Err(err).Str("driver_name", cfg.StorageDriver).Msg("The storage driver could not be initialized.") + return } defer func() { - log.Println("Terminating the storage driver...") - err := storage.Current.Terminate() - if err != nil { - log.Fatalln(err) + log.Info().Msg("Shutting down the storage driver...") + if err := driver.Close(); err != nil { + log.Err(err).Str("driver_name", cfg.StorageDriver).Msg("Could not shut down the storage driver.") } }() - // Schedule the AutoDelete task - if config.Current.AutoDelete.Enabled { - log.Println("Scheduling the AutoDelete task...") - go func() { - for { - // Run the cleanup sequence - deleted, err := storage.Current.Cleanup() - if err != nil { - log.Fatalln(err) - } - log.Printf("AutoDelete: Deleted %d expired pastes", deleted) - - // Wait until the process should repeat - time.Sleep(config.Current.AutoDelete.TaskInterval) - } - }() - } - - // Serve the web resources - log.Println("Serving the web resources...") - panic(web.Serve()) + // Wait for an interrupt signal + log.Info().Msg("The application has been started. Use Ctrl+C to shut it down.") + shutdownChan := make(chan os.Signal, 1) + signal.Notify(shutdownChan, os.Interrupt) + <-shutdownChan } diff --git a/cmd/transfer/main.go b/cmd/transfer/main.go deleted file mode 100644 index 002bd50..0000000 --- a/cmd/transfer/main.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "log" - "os" - - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/storage" -) - -func main() { - // Validate the command line arguments - if len(os.Args) != 3 { - panic("Invalid command line arguments") - } - - // Load the configuration - log.Println("Loading the application configuration...") - config.Load() - - // Create and initialize the first (from) driver - from, err := storage.GetDriver(storage.Type(os.Args[1])) - if err != nil { - panic(err) - } - err = from.Initialize() - if err != nil { - panic(err) - } - - // Create and initialize the second (to) driver - to, err := storage.GetDriver(storage.Type(os.Args[2])) - if err != nil { - panic(err) - } - err = to.Initialize() - if err != nil { - panic(err) - } - - // Retrieve a list of IDs from the first (from) driver - ids, err := from.ListIDs() - if err != nil { - panic(err) - } - - // Transfer every paste to the second (to) driver - for _, id := range ids { - log.Println("Transferring ID " + id + "...") - - // Retrieve the paste - paste, err := from.Get(id) - if err != nil { - log.Println("[ERR]", err.Error()) - continue - } - - // Move the content of the deletion token field to the modification field - if paste.DeletionToken != "" { - if paste.ModificationToken == "" { - paste.ModificationToken = paste.DeletionToken - } - paste.DeletionToken = "" - log.Println("[INFO] Paste " + id + " was a legacy one.") - } - - // Initialize a new metadata map if the old one is null - if paste.Metadata == nil { - paste.Metadata = make(map[string]interface{}) - } - - // Save the paste - err = to.Save(paste) - if err != nil { - log.Println("[ERR]", err.Error()) - continue - } - - log.Println("Transferred ID " + id + ".") - } -} diff --git a/go.mod b/go.mod index d377f6f..996f579 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,32 @@ module github.com/lus/pasty -go 1.16 +go 1.20 require ( github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b - github.com/fasthttp/router v1.2.4 - github.com/go-stack/stack v1.8.1 // indirect github.com/golang-migrate/migrate/v4 v4.14.2-0.20201125065321-a53e6fc42574 - github.com/golang/snappy v0.0.4 // indirect github.com/jackc/pgx/v4 v4.11.0 github.com/johejo/golang-migrate-extra v0.0.0-20210217013041-51a992e50d16 github.com/joho/godotenv v1.3.0 - github.com/klauspost/compress v1.15.1 // indirect - github.com/minio/minio-go/v7 v7.0.5 - github.com/ulule/limiter/v3 v3.5.0 - github.com/valyala/fasthttp v1.16.0 - github.com/xdg-go/scram v1.1.1 // indirect - github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.mongodb.org/mongo-driver v1.8.4 - golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + github.com/kelseyhightower/envconfig v1.4.0 + github.com/rs/zerolog v1.29.1 +) + +require ( + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.8.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.0.6 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.7.0 // indirect + github.com/jackc/puddle v1.1.3 // indirect + github.com/lib/pq v1.8.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + golang.org/x/text v0.3.3 // indirect ) diff --git a/go.sum b/go.sum index 950fa9f..4e31eb1 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b h1:rcCpjI1OMGtBY8nnBvExeM1pXNoaM35zqmXBGpgJR2o= github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b/go.mod h1:GFtu6vaWaRJV5EvSFaVqgq/3Iq95xyYElBV/aupGzUo= -github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -96,6 +94,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -126,16 +125,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 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/fasthttp/router v1.2.4 h1:RBWbCv4vVf+boczSZh/rX9PDSdR9F8I9zSnVJx5YJfU= -github.com/fasthttp/router v1.2.4/go.mod h1:Au2V1CaqqAdzQQcPKrbkFAsImd1aHpadrce21AIPnvE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -145,18 +140,12 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -199,8 +188,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -211,8 +198,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -228,11 +213,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -272,7 +255,6 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -292,7 +274,6 @@ github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye47 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -336,30 +317,18 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.6/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -369,7 +338,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -384,6 +352,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -391,21 +361,15 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= -github.com/minio/minio-go/v7 v7.0.5 h1:I2NIJ2ojwJqD/YByemC1M59e1b4FW9kS7NlOar7HPV4= -github.com/minio/minio-go/v7 v7.0.5/go.mod h1:TA0CQCjJZHM5SJj9IjqR0NmpmQJ6bCbXifAJ3mUU6Hw= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -413,12 +377,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= @@ -437,10 +398,8 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= @@ -491,16 +450,16 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= +github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/savsgio/gotils v0.0.0-20200616100644-13ff1fd2c28c h1:KKqhycXW1WVNkX7r4ekTV2gFkbhdyihlWD8c0/FiWmk= -github.com/savsgio/gotils v0.0.0-20200616100644-13ff1fd2c28c/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= @@ -512,10 +471,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -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/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= @@ -533,41 +489,16 @@ github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulule/limiter/v3 v3.5.0 h1:QRAebbswjlezHIfiSQgM8+jMxaz/zsrxGRuiUJ43MHo= -github.com/ulule/limiter/v3 v3.5.0/go.mod h1:TgOUQZKZ2KHjemqrC8UHUbKPqpTmSY43/2wbQ7YN1h8= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasthttp v1.15.1/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= -github.com/valyala/fasthttp v1.16.0 h1:9zAqOYLl8Tuy3E5R6ckzGDJ1g8+pw15oQp2iL9Jl6gQ= -github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -576,8 +507,6 @@ gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.8.4 h1:NruvZPPL0PBcRJKmbswoWSrmHeUvzdxA3GCPfD/NEOA= -go.mongodb.org/mongo-driver v1.8.4/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -607,16 +536,13 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -670,8 +596,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -683,15 +607,13 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -707,8 +629,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -733,7 +653,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -752,27 +671,21 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -792,7 +705,6 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -836,7 +748,6 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -932,15 +843,12 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -949,9 +857,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/config/config.go b/internal/config/config.go index 72f6fb4..17c74c2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,123 +1,55 @@ package config import ( - "strings" + "github.com/joho/godotenv" + "github.com/kelseyhightower/envconfig" "time" - - "github.com/lus/pasty/internal/env" ) -// Config represents the general application configuration structure +// Config represents the general application configuration type Config struct { - WebAddress string - StorageType string - HastebinSupport bool - IDLength int - IDCharacters string - ModificationTokens bool - ModificationTokenMaster string - ModificationTokenLength int - ModificationTokenCharacters string - RateLimit string - LengthCap int - AutoDelete *AutoDeleteConfig + LogLevel string `default:"info" split_words:"true"` + WebAddress string `default:":8080" split_words:"true"` + StorageDriver string `default:"sqlite" split_words:"true"` + HastebinSupport bool `default:"false" split_words:"true"` + IDLength int `default:"6" split_words:"true"` + IDCharacters string `default:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" split_words:"true"` + ModificationTokens bool `default:"true" split_words:"true"` + ModificationTokenMaster string `split_words:"true"` + ModificationTokenLength int `default:"12" split_words:"true"` + ModificationTokenCharacters string `default:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" split_words:"true"` + RateLimit string `default:"30-M" split_words:"true"` + LengthCap int `default:"50000" split_words:"true"` + AutoDelete *AutoDeleteConfig `split_words:"true"` Reports *ReportConfig - File *FileConfig Postgres *PostgresConfig - MongoDB *MongoDBConfig - S3 *S3Config } // AutoDeleteConfig represents the configuration specific for the AutoDelete behaviour type AutoDeleteConfig struct { - Enabled bool - Lifetime time.Duration - TaskInterval time.Duration -} - -// FileConfig represents the configuration specific for the file storage driver -type FileConfig struct { - Path string -} - -// PostgresConfig represents the configuration specific for the Postgres storage driver -type PostgresConfig struct { - DSN string -} - -// MongoDBConfig represents the configuration specific for the MongoDB storage driver -type MongoDBConfig struct { - DSN string - Database string - Collection string -} - -// S3Config represents the configuration specific for the S3 storage driver -type S3Config struct { - Endpoint string - AccessKeyID string - SecretAccessKey string - SecretToken string - Secure bool - Region string - Bucket string + Enabled bool `default:"false"` + Lifetime time.Duration `default:"720h"` + TaskInterval time.Duration `default:"5m" split_words:"true"` } // ReportConfig represents the configuration specific for the report system type ReportConfig struct { - Reports bool - ReportWebhook string - ReportWebhookToken string + Enabled bool `default:"false" split_words:"true"` + WebhookURL string `split_words:"true"` + WebhookToken string `split_words:"true"` } -// Current holds the currently loaded config -var Current *Config +// PostgresConfig represents the configuration specific for the Postgres storage driver +type PostgresConfig struct { + DSN string `default:"postgres://pasty:pasty@localhost/pasty"` +} // Load loads the current config from environment variables and an optional .env file -func Load() { - env.Load() - - Current = &Config{ - WebAddress: env.MustString("WEB_ADDRESS", ":8080"), - StorageType: strings.ToLower(env.MustString("STORAGE_TYPE", "file")), - HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false), - IDLength: env.MustInt("ID_LENGTH", 6), - IDCharacters: env.MustString("ID_CHARACTERS", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), - ModificationTokens: env.MustBool("MODIFICATION_TOKENS", true), - ModificationTokenMaster: env.MustString("MODIFICATION_TOKEN_MASTER", ""), - ModificationTokenLength: env.MustInt("MODIFICATION_TOKEN_LENGTH", 12), - ModificationTokenCharacters: env.MustString("MODIFICATION_TOKEN_CHARACTERS", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), - RateLimit: env.MustString("RATE_LIMIT", "30-M"), - LengthCap: env.MustInt("LENGTH_CAP", 50000), - AutoDelete: &AutoDeleteConfig{ - Enabled: env.MustBool("AUTODELETE", false), - Lifetime: env.MustDuration("AUTODELETE_LIFETIME", 720*time.Hour), - TaskInterval: env.MustDuration("AUTODELETE_TASK_INTERVAL", 5*time.Minute), - }, - Reports: &ReportConfig{ - Reports: env.MustBool("REPORTS", false), - ReportWebhook: env.MustString("REPORT_WEBHOOK", ""), - ReportWebhookToken: env.MustString("REPORT_WEBHOOK_TOKEN", ""), - }, - File: &FileConfig{ - Path: env.MustString("STORAGE_FILE_PATH", "./data"), - }, - Postgres: &PostgresConfig{ - DSN: env.MustString("STORAGE_POSTGRES_DSN", "postgres://pasty:pasty@localhost/pasty"), - }, - MongoDB: &MongoDBConfig{ - DSN: env.MustString("STORAGE_MONGODB_CONNECTION_STRING", "mongodb://pasty:pasty@localhost/pasty"), - Database: env.MustString("STORAGE_MONGODB_DATABASE", "pasty"), - Collection: env.MustString("STORAGE_MONGODB_COLLECTION", "pastes"), - }, - S3: &S3Config{ - Endpoint: env.MustString("STORAGE_S3_ENDPOINT", ""), - AccessKeyID: env.MustString("STORAGE_S3_ACCESS_KEY_ID", ""), - SecretAccessKey: env.MustString("STORAGE_S3_SECRET_ACCESS_KEY", ""), - SecretToken: env.MustString("STORAGE_S3_SECRET_TOKEN", ""), - Secure: env.MustBool("STORAGE_S3_SECURE", true), - Region: env.MustString("STORAGE_S3_REGION", ""), - Bucket: env.MustString("STORAGE_S3_BUCKET", "pasty"), - }, +func Load() (*Config, error) { + _ = godotenv.Overload() + cfg := new(Config) + if err := envconfig.Process("pasty", cfg); err != nil { + return nil, err } + return cfg, nil } diff --git a/internal/env/env.go b/internal/env/env.go deleted file mode 100644 index 4a568a9..0000000 --- a/internal/env/env.go +++ /dev/null @@ -1,42 +0,0 @@ -package env - -import ( - "os" - "strconv" - "time" - - "github.com/joho/godotenv" - "github.com/lus/pasty/internal/static" -) - -// Load loads an optional .env file -func Load() { - godotenv.Load() -} - -// MustString returns the content of the environment variable with the given key or the given fallback -func MustString(key, fallback string) string { - value, found := os.LookupEnv(static.EnvironmentVariablePrefix + key) - if !found { - return fallback - } - return value -} - -// MustBool uses MustString and parses it into a boolean -func MustBool(key string, fallback bool) bool { - parsed, _ := strconv.ParseBool(MustString(key, strconv.FormatBool(fallback))) - return parsed -} - -// MustInt uses MustString and parses it into an integer -func MustInt(key string, fallback int) int { - parsed, _ := strconv.Atoi(MustString(key, strconv.Itoa(fallback))) - return parsed -} - -// MustDuration uses MustString and parses it into a duration -func MustDuration(key string, fallback time.Duration) time.Duration { - parsed, _ := time.ParseDuration(MustString(key, fallback.String())) - return parsed -} diff --git a/internal/meta/metadata.go b/internal/meta/metadata.go new file mode 100644 index 0000000..59cbcf1 --- /dev/null +++ b/internal/meta/metadata.go @@ -0,0 +1,13 @@ +package meta + +import "strings" + +const devEnvironmentName = "dev" + +var ( + Environment = devEnvironmentName +) + +func IsProdEnvironment() bool { + return strings.ToLower(Environment) != devEnvironmentName +} diff --git a/internal/paste/paste.go b/internal/paste/paste.go index 3582fa7..99f7461 100644 --- a/internal/paste/paste.go +++ b/internal/paste/paste.go @@ -23,7 +23,7 @@ func (paste *Paste) HashModificationToken() error { return nil } -// CheckModificationToken checks whether or not the given modification token is correct +// CheckModificationToken checks whether the given modification token is correct func (paste *Paste) CheckModificationToken(modificationToken string) bool { match, err := argon2id.ComparePasswordAndHash(modificationToken, paste.ModificationToken) return err == nil && match diff --git a/internal/randx/string.go b/internal/randx/string.go new file mode 100644 index 0000000..8962838 --- /dev/null +++ b/internal/randx/string.go @@ -0,0 +1,14 @@ +package randx + +import ( + "math/rand" +) + +// String returns a random string with the given length +func String(characters string, length int) string { + bytes := make([]byte, length) + for i := range bytes { + bytes[i] = characters[rand.Int63()%int64(len(characters))] + } + return string(bytes) +} diff --git a/internal/report/report.go b/internal/report/report.go deleted file mode 100644 index 544b687..0000000 --- a/internal/report/report.go +++ /dev/null @@ -1,57 +0,0 @@ -package report - -import ( - "encoding/json" - "fmt" - - "github.com/lus/pasty/internal/config" - "github.com/valyala/fasthttp" -) - -// ReportRequest represents a report request sent to the report webhook -type ReportRequest struct { - Paste string `json:"paste"` - Reason string `json:"reason"` -} - -// ReportResponse represents a report response received from the report webhook -type ReportResponse struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -// SendReport sends a report request to the report webhook -func SendReport(reportRequest *ReportRequest) (*ReportResponse, error) { - request := fasthttp.AcquireRequest() - defer fasthttp.ReleaseRequest(request) - - response := fasthttp.AcquireResponse() - defer fasthttp.ReleaseResponse(response) - - request.Header.SetMethod(fasthttp.MethodPost) - request.SetRequestURI(config.Current.Reports.ReportWebhook) - if config.Current.Reports.ReportWebhookToken != "" { - request.Header.Set("Authorization", "Bearer "+config.Current.Reports.ReportWebhookToken) - } - - data, err := json.Marshal(reportRequest) - if err != nil { - return nil, err - } - request.SetBody(data) - - if err := fasthttp.Do(request, response); err != nil { - return nil, err - } - - status := response.StatusCode() - if status < 200 || status > 299 { - return nil, fmt.Errorf("the report webhook responded with an unexpected error: %d (%s)", status, string(response.Body())) - } - - reportResponse := new(ReportResponse) - if err := json.Unmarshal(response.Body(), reportResponse); err != nil { - return nil, err - } - return reportResponse, nil -} diff --git a/internal/static/static.go b/internal/static/static.go deleted file mode 100644 index e5b2528..0000000 --- a/internal/static/static.go +++ /dev/null @@ -1,11 +0,0 @@ -package static - -// These variables represent the values that may be changed using ldflags -var ( - Version = "dev" - EnvironmentVariablePrefix = "PASTY_" - - // TempFrontendPath defines the path where pasty loads the web frontend from; it will be removed any time soon - // TODO: Remove this when issue #37 is fixed - TempFrontendPath = "./web" -) diff --git a/internal/storage/driver.go b/internal/storage/driver.go index 384632c..d55e0e6 100644 --- a/internal/storage/driver.go +++ b/internal/storage/driver.go @@ -1,24 +1,18 @@ package storage import ( - "fmt" + "context" + "github.com/lus/pasty/internal/config" "strings" - "github.com/lus/pasty/internal/config" "github.com/lus/pasty/internal/paste" - "github.com/lus/pasty/internal/storage/file" - "github.com/lus/pasty/internal/storage/mongodb" "github.com/lus/pasty/internal/storage/postgres" - "github.com/lus/pasty/internal/storage/s3" ) -// Current holds the current storage driver -var Current Driver - // Driver represents a storage driver type Driver interface { - Initialize() error - Terminate() error + Initialize(ctx context.Context, cfg *config.Config) error + Close() error ListIDs() ([]string, error) Get(id string) (*paste.Paste, error) Save(paste *paste.Paste) error @@ -26,35 +20,12 @@ type Driver interface { Cleanup() (int, error) } -// Load loads the current storage driver -func Load() error { - // Define the driver to use - driver, err := GetDriver(config.Current.StorageType) - if err != nil { - return err - } - - // Initialize the driver - err = driver.Initialize() - if err != nil { - return err - } - Current = driver - return nil -} - -// GetDriver returns the driver with the given type if it exists -func GetDriver(storageType string) (Driver, error) { - switch strings.TrimSpace(strings.ToLower(storageType)) { - case "file": - return new(file.FileDriver), nil +// ResolveDriver returns the driver with the given name if it exists +func ResolveDriver(name string) (Driver, bool) { + switch strings.TrimSpace(strings.ToLower(name)) { case "postgres": - return new(postgres.PostgresDriver), nil - case "mongodb": - return new(mongodb.MongoDBDriver), nil - case "s3": - return new(s3.S3Driver), nil + return new(postgres.Driver), true default: - return nil, fmt.Errorf("invalid storage type '%s'", storageType) + return nil, false } } diff --git a/internal/storage/file/file_driver.go b/internal/storage/file/file_driver.go deleted file mode 100644 index aa40ade..0000000 --- a/internal/storage/file/file_driver.go +++ /dev/null @@ -1,145 +0,0 @@ -package file - -import ( - "encoding/base64" - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/paste" -) - -// FileDriver represents the file storage driver -type FileDriver struct { - filePath string -} - -// Initialize initializes the file storage driver -func (driver *FileDriver) Initialize() error { - driver.filePath = config.Current.File.Path - return os.MkdirAll(driver.filePath, os.ModePerm) -} - -// Terminate terminates the file storage driver (does nothing, because the file storage driver does not need any termination) -func (driver *FileDriver) Terminate() error { - return nil -} - -// ListIDs returns a list of all existing paste IDs -func (driver *FileDriver) ListIDs() ([]string, error) { - // Define the IDs slice - var ids []string - - // Fill the IDs slice - err := filepath.Walk(driver.filePath, func(_ string, info os.FileInfo, err error) error { - // Check if a walking error occurred - if err != nil { - return err - } - - // Only count JSON files - if !strings.HasSuffix(info.Name(), ".json") { - return nil - } - - // Decode the file name - decoded, err := base64.StdEncoding.DecodeString(strings.TrimSuffix(info.Name(), ".json")) - if err != nil { - return err - } - - // Append the ID to the IDs slice - ids = append(ids, string(decoded)) - return nil - }) - if err != nil { - return nil, err - } - - // Return the IDs slice - return ids, nil -} - -// Get loads a paste -func (driver *FileDriver) Get(id string) (*paste.Paste, error) { - // Read the file - id = base64.StdEncoding.EncodeToString([]byte(id)) - data, err := ioutil.ReadFile(filepath.Join(driver.filePath, id+".json")) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, err - } - - // Unmarshal the file into a paste - paste := new(paste.Paste) - err = json.Unmarshal(data, &paste) - if err != nil { - return nil, err - } - return paste, nil -} - -// Save saves a paste -func (driver *FileDriver) Save(paste *paste.Paste) error { - // Marshal the paste - jsonBytes, err := json.Marshal(paste) - if err != nil { - return err - } - - // Create the file to save the paste to - id := base64.StdEncoding.EncodeToString([]byte(paste.ID)) - file, err := os.Create(filepath.Join(driver.filePath, id+".json")) - if err != nil { - return err - } - defer file.Close() - - // Write the JSON data into the file - _, err = file.Write(jsonBytes) - return err -} - -// Delete deletes a paste -func (driver *FileDriver) Delete(id string) error { - id = base64.StdEncoding.EncodeToString([]byte(id)) - return os.Remove(filepath.Join(driver.filePath, id+".json")) -} - -// Cleanup cleans up the expired pastes -func (driver *FileDriver) Cleanup() (int, error) { - // Retrieve all paste IDs - ids, err := driver.ListIDs() - if err != nil { - return 0, err - } - - // Define the amount of deleted items - deleted := 0 - - // Loop through all pastes - for _, id := range ids { - // Retrieve the paste object - paste, err := driver.Get(id) - if err != nil { - return deleted, err - } - - // Delete the paste if it is expired - lifetime := config.Current.AutoDelete.Lifetime - if paste.Created+int64(lifetime.Seconds()) < time.Now().Unix() { - err = driver.Delete(id) - if err != nil { - return deleted, err - } - deleted++ - } - } - return deleted, nil -} diff --git a/internal/storage/id_generation.go b/internal/storage/id_generation.go deleted file mode 100644 index 99b05eb..0000000 --- a/internal/storage/id_generation.go +++ /dev/null @@ -1,20 +0,0 @@ -package storage - -import ( - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/utils" -) - -// AcquireID generates a new unique ID -func AcquireID() (string, error) { - for { - id := utils.RandomString(config.Current.IDCharacters, config.Current.IDLength) - paste, err := Current.Get(id) - if err != nil { - return "", err - } - if paste == nil { - return id, nil - } - } -} diff --git a/internal/storage/mongodb/mongodb_driver.go b/internal/storage/mongodb/mongodb_driver.go deleted file mode 100644 index 951312e..0000000 --- a/internal/storage/mongodb/mongodb_driver.go +++ /dev/null @@ -1,171 +0,0 @@ -package mongodb - -import ( - "context" - "time" - - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/paste" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/mongo/readpref" -) - -// MongoDBDriver represents the MongoDB storage driver -type MongoDBDriver struct { - client *mongo.Client - database string - collection string -} - -// Initialize initializes the MongoDB storage driver -func (driver *MongoDBDriver) Initialize() error { - // Define the context for the following database operation - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - // Connect to the MongoDB host - client, err := mongo.Connect(ctx, options.Client().ApplyURI(config.Current.MongoDB.DSN)) - if err != nil { - return err - } - - // Ping the MongoDB host - err = client.Ping(ctx, readpref.Primary()) - if err != nil { - return err - } - - // Set the driver attributes - driver.client = client - driver.database = config.Current.MongoDB.Database - driver.collection = config.Current.MongoDB.Collection - return nil -} - -// Terminate terminates the MongoDB storage driver -func (driver *MongoDBDriver) Terminate() error { - return driver.client.Disconnect(context.TODO()) -} - -// ListIDs returns a list of all existing paste IDs -func (driver *MongoDBDriver) ListIDs() ([]string, error) { - // Define the collection to use for this database operation - collection := driver.client.Database(driver.database).Collection(driver.collection) - - // Define the context for the following database operation - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // Retrieve all paste documents - result, err := collection.Find(ctx, bson.M{}) - if err != nil { - return nil, err - } - - // Decode all paste documents - var pasteSlice []paste.Paste - err = result.All(ctx, &pasteSlice) - if err != nil { - return nil, err - } - - // Read and return the IDs of all paste objects - var ids []string - for _, paste := range pasteSlice { - ids = append(ids, paste.ID) - } - return ids, nil -} - -// Get loads a paste -func (driver *MongoDBDriver) Get(id string) (*paste.Paste, error) { - // Define the collection to use for this database operation - collection := driver.client.Database(driver.database).Collection(driver.collection) - - // Define the context for the following database operation - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // Try to retrieve the corresponding paste document - filter := bson.M{"_id": id} - result := collection.FindOne(ctx, filter) - err := result.Err() - if err != nil { - if err == mongo.ErrNoDocuments { - return nil, nil - } - return nil, err - } - - // Return the retrieved paste object - paste := new(paste.Paste) - err = result.Decode(paste) - if err != nil { - return nil, err - } - return paste, nil -} - -// Save saves a paste -func (driver *MongoDBDriver) Save(paste *paste.Paste) error { - // Define the collection to use for this database operation - collection := driver.client.Database(driver.database).Collection(driver.collection) - - // Define the context for the following database operation - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // Upsert the paste object - filter := bson.M{"_id": paste.ID} - _, err := collection.UpdateOne(ctx, filter, bson.M{"$set": paste}, options.Update().SetUpsert(true)) - return err -} - -// Delete deletes a paste -func (driver *MongoDBDriver) Delete(id string) error { - // Define the collection to use for this database operation - collection := driver.client.Database(driver.database).Collection(driver.collection) - - // Define the context for the following database operation - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - // Delete the document - filter := bson.M{"_id": id} - _, err := collection.DeleteOne(ctx, filter) - return err -} - -// Cleanup cleans up the expired pastes -func (driver *MongoDBDriver) Cleanup() (int, error) { - // Retrieve all paste IDs - ids, err := driver.ListIDs() - if err != nil { - return 0, err - } - - // Define the amount of deleted items - deleted := 0 - - // Loop through all pastes - for _, id := range ids { - // Retrieve the paste object - paste, err := driver.Get(id) - if err != nil { - return 0, err - } - - // Delete the paste if it is expired - lifetime := config.Current.AutoDelete.Lifetime - if paste.Created+int64(lifetime.Seconds()) < time.Now().Unix() { - err = driver.Delete(id) - if err != nil { - return 0, err - } - deleted++ - } - } - return deleted, nil -} diff --git a/internal/storage/postgres/postgres_driver.go b/internal/storage/postgres/postgres_driver.go index f43c61e..9b70e7d 100644 --- a/internal/storage/postgres/postgres_driver.go +++ b/internal/storage/postgres/postgres_driver.go @@ -18,14 +18,15 @@ import ( //go:embed migrations/*.sql var migrations embed.FS -// PostgresDriver represents the Postgres storage driver -type PostgresDriver struct { - pool *pgxpool.Pool +// Driver represents the Postgres storage driver +type Driver struct { + pool *pgxpool.Pool + autoDeleteLifetime time.Duration } // Initialize initializes the Postgres storage driver -func (driver *PostgresDriver) Initialize() error { - pool, err := pgxpool.Connect(context.Background(), config.Current.Postgres.DSN) +func (driver *Driver) Initialize(ctx context.Context, cfg *config.Config) error { + pool, err := pgxpool.Connect(ctx, cfg.Postgres.DSN) if err != nil { return err } @@ -35,7 +36,7 @@ func (driver *PostgresDriver) Initialize() error { return err } - migrator, err := migrate.NewWithSourceInstance("iofs", source, config.Current.Postgres.DSN) + migrator, err := migrate.NewWithSourceInstance("iofs", source, cfg.Postgres.DSN) if err != nil { return err } @@ -45,17 +46,18 @@ func (driver *PostgresDriver) Initialize() error { } driver.pool = pool + driver.autoDeleteLifetime = cfg.AutoDelete.Lifetime return nil } -// Terminate terminates the Postgres storage driver -func (driver *PostgresDriver) Terminate() error { +// Close terminates the Postgres storage driver +func (driver *Driver) Close() error { driver.pool.Close() return nil } // ListIDs returns a list of all existing paste IDs -func (driver *PostgresDriver) ListIDs() ([]string, error) { +func (driver *Driver) ListIDs() ([]string, error) { query := "SELECT id FROM pastes" rows, err := driver.pool.Query(context.Background(), query) @@ -76,7 +78,7 @@ func (driver *PostgresDriver) ListIDs() ([]string, error) { } // Get loads a paste -func (driver *PostgresDriver) Get(id string) (*paste.Paste, error) { +func (driver *Driver) Get(id string) (*paste.Paste, error) { query := "SELECT * FROM pastes WHERE id = $1" row := driver.pool.QueryRow(context.Background(), query, id) @@ -92,7 +94,7 @@ func (driver *PostgresDriver) Get(id string) (*paste.Paste, error) { } // Save saves a paste -func (driver *PostgresDriver) Save(paste *paste.Paste) error { +func (driver *Driver) Save(paste *paste.Paste) error { query := ` INSERT INTO pastes (id, content, "modificationToken", created, metadata) VALUES ($1, $2, $3, $4, $5) @@ -108,7 +110,7 @@ func (driver *PostgresDriver) Save(paste *paste.Paste) error { } // Delete deletes a paste -func (driver *PostgresDriver) Delete(id string) error { +func (driver *Driver) Delete(id string) error { query := "DELETE FROM pastes WHERE id = $1" _, err := driver.pool.Exec(context.Background(), query, id) @@ -116,10 +118,10 @@ func (driver *PostgresDriver) Delete(id string) error { } // Cleanup cleans up the expired pastes -func (driver *PostgresDriver) Cleanup() (int, error) { +func (driver *Driver) Cleanup() (int, error) { query := "DELETE FROM pastes WHERE created < $1" - tag, err := driver.pool.Exec(context.Background(), query, time.Now().Add(-config.Current.AutoDelete.Lifetime).Unix()) + tag, err := driver.pool.Exec(context.Background(), query, time.Now().Add(-driver.autoDeleteLifetime).Unix()) if err != nil { return 0, err } diff --git a/internal/storage/s3/s3_driver.go b/internal/storage/s3/s3_driver.go deleted file mode 100644 index bf524a5..0000000 --- a/internal/storage/s3/s3_driver.go +++ /dev/null @@ -1,136 +0,0 @@ -package s3 - -import ( - "bytes" - "context" - "encoding/json" - "io/ioutil" - "strings" - "time" - - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/paste" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" -) - -// S3Driver represents the AWS S3 storage driver -type S3Driver struct { - client *minio.Client - bucket string -} - -// Initialize initializes the AWS S3 storage driver -func (driver *S3Driver) Initialize() error { - client, err := minio.New(config.Current.S3.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(config.Current.S3.AccessKeyID, config.Current.S3.SecretAccessKey, config.Current.S3.SecretToken), - Secure: config.Current.S3.Secure, - Region: config.Current.S3.Region, - }) - if err != nil { - return err - } - driver.client = client - driver.bucket = config.Current.S3.Bucket - return nil -} - -// Terminate terminates the AWS S3 storage driver (does nothing, because the AWS S3 storage driver does not need any termination) -func (driver *S3Driver) Terminate() error { - return nil -} - -// ListIDs returns a list of all existing paste IDs -func (driver *S3Driver) ListIDs() ([]string, error) { - // Define the IDs slice - var ids []string - - // Fill the IDs slice - channel := driver.client.ListObjects(context.Background(), driver.bucket, minio.ListObjectsOptions{}) - for object := range channel { - if object.Err != nil { - return nil, object.Err - } - ids = append(ids, strings.TrimSuffix(object.Key, ".json")) - } - - // Return the IDs slice - return ids, nil -} - -// Get loads a paste -func (driver *S3Driver) Get(id string) (*paste.Paste, error) { - // Read the object - object, err := driver.client.GetObject(context.Background(), driver.bucket, id+".json", minio.GetObjectOptions{}) - if err != nil { - return nil, err - } - data, err := ioutil.ReadAll(object) - if err != nil { - if minio.ToErrorResponse(err).Code == "NoSuchKey" { - return nil, nil - } - return nil, err - } - - // Unmarshal the object into a paste - paste := new(paste.Paste) - err = json.Unmarshal(data, &paste) - if err != nil { - return nil, err - } - return paste, nil -} - -// Save saves a paste -func (driver *S3Driver) Save(paste *paste.Paste) error { - // Marshal the paste - jsonBytes, err := json.Marshal(paste) - if err != nil { - return err - } - - // Put the object - reader := bytes.NewReader(jsonBytes) - _, err = driver.client.PutObject(context.Background(), driver.bucket, paste.ID+".json", reader, reader.Size(), minio.PutObjectOptions{ - ContentType: "application/json", - }) - return err -} - -// Delete deletes a paste -func (driver *S3Driver) Delete(id string) error { - return driver.client.RemoveObject(context.Background(), driver.bucket, id+".json", minio.RemoveObjectOptions{}) -} - -// Cleanup cleans up the expired pastes -func (driver *S3Driver) Cleanup() (int, error) { - // Retrieve all paste IDs - ids, err := driver.ListIDs() - if err != nil { - return 0, err - } - - // Define the amount of deleted items - deleted := 0 - - // Loop through all pastes - for _, id := range ids { - // Retrieve the paste object - paste, err := driver.Get(id) - if err != nil { - return 0, err - } - - // Delete the paste if it is expired - lifetime := config.Current.AutoDelete.Lifetime - if paste.Created+int64(lifetime.Seconds()) < time.Now().Unix() { - err = driver.Delete(id) - if err != nil { - return 0, err - } - deleted++ - } - } - return deleted, nil -} diff --git a/internal/utils/random_string.go b/internal/utils/random_string.go deleted file mode 100644 index 3798ec5..0000000 --- a/internal/utils/random_string.go +++ /dev/null @@ -1,16 +0,0 @@ -package utils - -import ( - "math/rand" - "time" -) - -// RandomString returns a random string with the given length -func RandomString(characters string, length int) string { - rand.Seed(time.Now().UnixNano()) - bytes := make([]byte, length) - for i := range bytes { - bytes[i] = characters[rand.Int63()%int64(len(characters))] - } - return string(bytes) -} diff --git a/internal/web/logger.go b/internal/web/logger.go deleted file mode 100644 index 035c994..0000000 --- a/internal/web/logger.go +++ /dev/null @@ -1,9 +0,0 @@ -package web - -// nilLogger represents a logger that does not print anything -type nilLogger struct { -} - -// Printf prints nothing -func (logger *nilLogger) Printf(string, ...interface{}) { -} diff --git a/internal/web/router.go b/internal/web/router.go deleted file mode 100644 index 9cb8efc..0000000 --- a/internal/web/router.go +++ /dev/null @@ -1,137 +0,0 @@ -package web - -import ( - "encoding/json" - "path/filepath" - "strings" - - routing "github.com/fasthttp/router" - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/static" - "github.com/lus/pasty/internal/storage" - v2 "github.com/lus/pasty/internal/web/v2" - "github.com/ulule/limiter/v3" - limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp" - "github.com/ulule/limiter/v3/drivers/store/memory" - "github.com/valyala/fasthttp" -) - -// Serve serves the web resources -func Serve() error { - // Create the router - router := routing.New() - - // Define the 404 handler - router.NotFound = func(ctx *fasthttp.RequestCtx) { - ctx.SetStatusCode(fasthttp.StatusNotFound) - ctx.SetBodyString("not found") - } - - // Route the frontend requests - frontend := frontendHandler() - raw := rawHandler() - router.GET("/{path:*}", func(ctx *fasthttp.RequestCtx) { - path := string(ctx.Path()) - if !strings.HasPrefix(path, "/api") && (strings.Count(path, "/") == 1 || strings.HasPrefix(path, "/assets")) { - if strings.HasPrefix(path, "/assets/js/") { - ctx.SetContentType("text/javascript") - } - frontend(ctx) - return - } else if strings.HasSuffix(strings.TrimSuffix(path, "/"), "/raw") { - raw(ctx) - return - } - router.NotFound(ctx) - }) - - // Set up the rate limiter - rate, err := limiter.NewRateFromFormatted(config.Current.RateLimit) - if err != nil { - return err - } - rateLimiter := limiter.New(memory.NewStore(), rate) - rateLimiterMiddleware := limitFasthttp.NewMiddleware(rateLimiter) - - // Route the API endpoints - apiRoute := router.Group("/api") - { - v2Route := apiRoute.Group("/v2") - { - pasteLifetime := int64(-1) - if config.Current.AutoDelete.Enabled { - pasteLifetime = config.Current.AutoDelete.Lifetime.Milliseconds() - } - v2Route.GET("/info", func(ctx *fasthttp.RequestCtx) { - jsonData, _ := json.Marshal(map[string]interface{}{ - "version": static.Version, - "modificationTokens": config.Current.ModificationTokens, - "reports": config.Current.Reports.Reports, - "pasteLifetime": pasteLifetime, - }) - ctx.SetBody(jsonData) - }) - v2.InitializePastesController(v2Route.Group("/pastes"), rateLimiterMiddleware) - } - } - - // Route the hastebin documents route if hastebin support is enabled - if config.Current.HastebinSupport { - // TODO: Reimplement hastebin support - } - - // Serve the web resources - return (&fasthttp.Server{ - Handler: func(ctx *fasthttp.RequestCtx) { - // Add the CORS headers - ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET,POST,DELETE,OPTIONS") - ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") - - // Call the router handler - router.Handler(ctx) - }, - Logger: new(nilLogger), - }).ListenAndServe(config.Current.WebAddress) -} - -// frontendHandler handles the frontend routing -func frontendHandler() fasthttp.RequestHandler { - // Create the file server - fs := &fasthttp.FS{ - Root: static.TempFrontendPath, - IndexNames: []string{"index.html"}, - CacheDuration: 0, - } - fs.PathNotFound = func(ctx *fasthttp.RequestCtx) { - if strings.HasPrefix(string(ctx.Path()), "/assets") { - ctx.SetStatusCode(fasthttp.StatusNotFound) - ctx.SetBodyString("not found") - return - } - ctx.SendFile(filepath.Join(fs.Root, "index.html")) - } - return fs.NewRequestHandler() -} - -func rawHandler() fasthttp.RequestHandler { - return func(ctx *fasthttp.RequestCtx) { - path := string(ctx.Path()) - pathSanitized := strings.TrimPrefix(strings.TrimSuffix(path, "/"), "/") - pasteID := strings.TrimSuffix(pathSanitized, "/raw") - - paste, err := storage.Current.Get(pasteID) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - - if paste == nil { - ctx.SetStatusCode(fasthttp.StatusNotFound) - ctx.SetBodyString("paste not found") - return - } - - ctx.SetBodyString(paste.Content) - } -} diff --git a/internal/web/v2/pastes.go b/internal/web/v2/pastes.go deleted file mode 100644 index 5ba44f8..0000000 --- a/internal/web/v2/pastes.go +++ /dev/null @@ -1,273 +0,0 @@ -package v2 - -import ( - "encoding/json" - "strings" - "time" - - "github.com/fasthttp/router" - "github.com/lus/pasty/internal/config" - "github.com/lus/pasty/internal/paste" - "github.com/lus/pasty/internal/report" - "github.com/lus/pasty/internal/storage" - "github.com/lus/pasty/internal/utils" - limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp" - "github.com/valyala/fasthttp" -) - -// InitializePastesController initializes the '/v2/pastes/*' controller -func InitializePastesController(group *router.Group, rateLimiterMiddleware *limitFasthttp.Middleware) { - // moms spaghetti - group.GET("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(endpointGetPaste))) - group.POST("", rateLimiterMiddleware.Handle(endpointCreatePaste)) - group.PATCH("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointModifyPaste)))) - group.DELETE("/{id}", rateLimiterMiddleware.Handle(middlewareInjectPaste(middlewareValidateModificationToken(endpointDeletePaste)))) - - if config.Current.Reports.Reports { - group.POST("/{id}/report", rateLimiterMiddleware.Handle(middlewareInjectPaste(endpointReportPaste))) - } -} - -// middlewareInjectPaste retrieves and injects the paste with the specified ID -func middlewareInjectPaste(next fasthttp.RequestHandler) fasthttp.RequestHandler { - return func(ctx *fasthttp.RequestCtx) { - pasteID := ctx.UserValue("id").(string) - - paste, err := storage.Current.Get(pasteID) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - if paste == nil { - ctx.SetStatusCode(fasthttp.StatusNotFound) - ctx.SetBodyString("paste not found") - return - } - - if paste.Metadata == nil { - paste.Metadata = map[string]interface{}{} - } - - ctx.SetUserValue("_paste", paste) - - next(ctx) - } -} - -// middlewareValidateModificationToken extracts and validates a given modification token for an injected paste -func middlewareValidateModificationToken(next fasthttp.RequestHandler) fasthttp.RequestHandler { - return func(ctx *fasthttp.RequestCtx) { - paste := ctx.UserValue("_paste").(*paste.Paste) - - authHeaderSplit := strings.SplitN(string(ctx.Request.Header.Peek("Authorization")), " ", 2) - if len(authHeaderSplit) < 2 || authHeaderSplit[0] != "Bearer" { - ctx.SetStatusCode(fasthttp.StatusUnauthorized) - ctx.SetBodyString("unauthorized") - return - } - - modificationToken := authHeaderSplit[1] - if config.Current.ModificationTokenMaster != "" && modificationToken == config.Current.ModificationTokenMaster { - next(ctx) - return - } - valid := paste.CheckModificationToken(modificationToken) - if !valid { - ctx.SetStatusCode(fasthttp.StatusUnauthorized) - ctx.SetBodyString("unauthorized") - return - } - - next(ctx) - } -} - -// endpointGetPaste handles the 'GET /v2/pastes/{id}' endpoint -func endpointGetPaste(ctx *fasthttp.RequestCtx) { - paste := ctx.UserValue("_paste").(*paste.Paste) - paste.ModificationToken = "" - - jsonData, err := json.Marshal(paste) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - ctx.SetBody(jsonData) -} - -type endpointCreatePastePayload struct { - Content string `json:"content"` - Metadata map[string]interface{} `json:"metadata"` -} - -// endpointCreatePaste handles the 'POST /v2/pastes' endpoint -func endpointCreatePaste(ctx *fasthttp.RequestCtx) { - // Read, parse and validate the request payload - payload := new(endpointCreatePastePayload) - if err := json.Unmarshal(ctx.PostBody(), payload); err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - if payload.Content == "" { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - ctx.SetBodyString("missing paste content") - return - } - if config.Current.LengthCap > 0 && len(payload.Content) > config.Current.LengthCap { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - ctx.SetBodyString("too large paste content") - return - } - - // Acquire a new paste ID - id, err := storage.AcquireID() - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - - // Prepare the paste object - if payload.Metadata == nil { - payload.Metadata = map[string]interface{}{} - } - paste := &paste.Paste{ - ID: id, - Content: payload.Content, - Created: time.Now().Unix(), - Metadata: payload.Metadata, - } - - // Create a new modification token if enabled - modificationToken := "" - if config.Current.ModificationTokens { - modificationToken = utils.RandomString(config.Current.ModificationTokenCharacters, config.Current.ModificationTokenLength) - paste.ModificationToken = modificationToken - - err = paste.HashModificationToken() - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - } - - // Save the paste - err = storage.Current.Save(paste) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - - // Respond with the paste - pasteCopy := *paste - pasteCopy.ModificationToken = modificationToken - jsonData, err := json.Marshal(pasteCopy) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - ctx.SetStatusCode(fasthttp.StatusCreated) - ctx.SetBody(jsonData) -} - -type endpointModifyPastePayload struct { - Content *string `json:"content"` - Metadata map[string]interface{} `json:"metadata"` -} - -// endpointModifyPaste handles the 'PATCH /v2/pastes/{id}' endpoint -func endpointModifyPaste(ctx *fasthttp.RequestCtx) { - // Read, parse and validate the request payload - payload := new(endpointModifyPastePayload) - if err := json.Unmarshal(ctx.PostBody(), payload); err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - if payload.Content != nil && *payload.Content == "" { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - ctx.SetBodyString("missing paste content") - return - } - if payload.Content != nil && config.Current.LengthCap > 0 && len(*payload.Content) > config.Current.LengthCap { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - ctx.SetBodyString("too large paste content") - return - } - - // Modify the paste itself - paste := ctx.UserValue("_paste").(*paste.Paste) - if payload.Content != nil { - paste.Content = *payload.Content - } - if payload.Metadata != nil { - for key, value := range payload.Metadata { - if value == nil { - delete(paste.Metadata, key) - continue - } - paste.Metadata[key] = value - } - } - - // Save the modified paste - if err := storage.Current.Save(paste); err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } -} - -// endpointDeletePaste handles the 'DELETE /v2/pastes/{id}' endpoint -func endpointDeletePaste(ctx *fasthttp.RequestCtx) { - paste := ctx.UserValue("_paste").(*paste.Paste) - if err := storage.Current.Delete(paste.ID); err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } -} - -type endpointReportPastePayload struct { - Reason string `json:"reason"` -} - -func endpointReportPaste(ctx *fasthttp.RequestCtx) { - // Read, parse and validate the request payload - payload := new(endpointReportPastePayload) - if err := json.Unmarshal(ctx.PostBody(), payload); err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - if payload.Reason == "" { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - ctx.SetBodyString("missing report reason") - return - } - - request := &report.ReportRequest{ - Paste: ctx.UserValue("_paste").(*paste.Paste).ID, - Reason: payload.Reason, - } - response, err := report.SendReport(request) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - - jsonData, err := json.Marshal(response) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(err.Error()) - return - } - ctx.SetBody(jsonData) -}