diff --git a/README.md b/README.md index cf12794..16966fd 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,11 @@ Simple standalone file upload server with api and cli - use global map of api endpoints like /file/get/ etc - create cobra client commands (upload, list, delete, edit) +## BUGS + +### upctl HTTP 413 weird behavior + +- with -d reports correctly the 413, w/o it, it reports the timeout before. ## curl commands diff --git a/upctl/cfg/config.go b/upctl/cfg/config.go index d2ddd4a..3115293 100644 --- a/upctl/cfg/config.go +++ b/upctl/cfg/config.go @@ -26,11 +26,17 @@ const Version string = "v0.0.1" var VERSION string // maintained by -x type Config struct { + // globals Endpoint string Debug bool Retries int - Expire string Apikey string + + // upload + Expire string + + // list + Apicontext string } func Getversion() string { diff --git a/upctl/cmd/list.go b/upctl/cmd/list.go new file mode 100644 index 0000000..72a9d78 --- /dev/null +++ b/upctl/cmd/list.go @@ -0,0 +1,42 @@ +/* +Copyright © 2023 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/tlinden/up/upctl/cfg" + "github.com/tlinden/up/upctl/lib" +) + +func ListCommand(conf *cfg.Config) *cobra.Command { + var listCmd = &cobra.Command{ + Use: "list [options] [file ..]", + Short: "list uploads", + Long: `List uploads.`, + RunE: func(cmd *cobra.Command, args []string) error { + // errors at this stage do not cause the usage to be shown + cmd.SilenceUsage = true + + return lib.List(conf, args) + }, + } + + // options + listCmd.PersistentFlags().StringVarP(&conf.Apicontext, "apicontext", "", "", "Filter by given API context") + + return listCmd +} diff --git a/upctl/cmd/root.go b/upctl/cmd/root.go index 93da3f5..10b7d7e 100644 --- a/upctl/cmd/root.go +++ b/upctl/cmd/root.go @@ -87,6 +87,7 @@ func Execute() { rootCmd.PersistentFlags().StringVarP(&conf.Apikey, "apikey", "a", "", "Api key to use") rootCmd.AddCommand(UploadCommand(&conf)) + rootCmd.AddCommand(ListCommand(&conf)) err := rootCmd.Execute() if err != nil { diff --git a/upctl/lib/client.go b/upctl/lib/client.go index 696804e..d235b7c 100644 --- a/upctl/lib/client.go +++ b/upctl/lib/client.go @@ -39,6 +39,10 @@ type Request struct { Url string } +type ListParams struct { + Apicontext string `json:"apicontext"` +} + func Setup(c *cfg.Config, path string) *Request { client := req.C() if c.Debug { @@ -59,7 +63,7 @@ func Setup(c *cfg.Config, path string) *Request { AddRetryHook(func(resp *req.Response, err error) { req := resp.Request.RawRequest if c.Debug { - fmt.Println("Retrying endpoint request:", req.Method, req.URL) + fmt.Println("Retrying endpoint request:", req.Method, req.URL, err) } }) } @@ -68,7 +72,7 @@ func Setup(c *cfg.Config, path string) *Request { client.SetCommonBearerAuthToken(c.Apikey) } - return &Request{Url: c.Endpoint + "/file/", R: R} + return &Request{Url: c.Endpoint + path, R: R} } @@ -129,22 +133,55 @@ func Upload(c *cfg.Config, args []string) error { return err } + return HandleResponse(c, resp) +} + +func HandleResponse(c *cfg.Config, resp *req.Response) error { // we expect a json response r := Response{} - json.Unmarshal([]byte(resp.String()), &r) + + if err := json.Unmarshal([]byte(resp.String()), &r); err != nil { + // text output! + r.Message = resp.String() + } if c.Debug { - trace := resp.TraceInfo() // Use `resp.Request.TraceInfo()` to avoid unnecessary struct copy in production. - fmt.Println(trace.Blame()) // Print out exactly where the http request is slowing down. + trace := resp.Request.TraceInfo() + fmt.Println(trace.Blame()) fmt.Println("----------") fmt.Println(trace) } if !r.Success { - return errors.New(r.Message) + if len(r.Message) == 0 { + if resp.Err != nil { + return resp.Err + } else { + return errors.New("Unknown error") + } + } else { + return errors.New(r.Message) + } } + // all right fmt.Println(r.Message) - return nil } + +func List(c *cfg.Config, args []string) error { + rq := Setup(c, "/list/") + + params := &ListParams{Apicontext: c.Apicontext} + resp, err := rq.R. + SetBodyJsonMarshal(params). + Get(rq.Url) + + fmt.Println("") + + if err != nil { + return err + } + + return HandleResponse(c, resp) +} diff --git a/upd/api/auth.go b/upd/api/auth.go index ca5fad2..8909b98 100644 --- a/upd/api/auth.go +++ b/upd/api/auth.go @@ -20,45 +20,88 @@ package api import ( "crypto/sha256" "crypto/subtle" + "errors" "github.com/gofiber/fiber/v2" - //"github.com/gofiber/keyauth/v2" + "github.com/gofiber/keyauth/v2" + "github.com/tlinden/up/upd/cfg" "regexp" - "strings" ) -var Authurls []*regexp.Regexp -var Apikeys []string +// these vars can be savely global, since they don't change ever +var ( + errMissing = &fiber.Error{ + Code: 403000, + Message: "Missing API key", + } -func AuthSetApikeys(keys []string) { + errInvalid = &fiber.Error{ + Code: 403001, + Message: "Invalid API key", + } + + Authurls []*regexp.Regexp + Apikeys []cfg.Apicontext +) + +// fill from server: accepted keys +func AuthSetApikeys(keys []cfg.Apicontext) { Apikeys = keys } +// fill from server: endpoints we need to authenticate func AuthSetEndpoints(prefix string, version string, endpoints []string) { for _, endpoint := range endpoints { Authurls = append(Authurls, regexp.MustCompile("^"+prefix+version+endpoint)) } } -func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { - for _, apiKey := range Apikeys { - hashedAPIKey := sha256.Sum256([]byte(apiKey)) +// make sure we always return JSON encoded errors +func AuthErrHandler(ctx *fiber.Ctx, err error) error { + ctx.Status(fiber.StatusForbidden) + + if err == errMissing { + return ctx.JSON(errMissing) + } + + return ctx.JSON(errInvalid) +} + +// validator hook, called by fiber via server keyauth.New() +func AuthValidateAPIKey(c *fiber.Ctx, key string) (bool, error) { + // create a new session, it will be thrown away if something fails + sess, err := Sessionstore.Get(c) + if err != nil { + return false, errors.New("Unable to initialize session store from context!") + } + + // if Apikeys is empty, the server works unauthenticated + // FIXME: maybe always reject? + if len(Apikeys) == 0 { + sess.Set("apicontext", "default") + + if err := sess.Save(); err != nil { + return false, errors.New("Unable to save session store!") + } + + return true, nil + } + + // actual key comparision + for _, apicontext := range Apikeys { + hashedAPIKey := sha256.Sum256([]byte(apicontext.Key)) hashedKey := sha256.Sum256([]byte(key)) if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { + // apikey matches, register apicontext for later use by the handlers + sess.Set("apicontext", apicontext.Context) + + if err := sess.Save(); err != nil { + return false, errors.New("Unable to save session store!") + } + return true, nil } } - return true, nil - //return false, keyauth.ErrMissingOrMalformedAPIKey -} -func authFilter(c *fiber.Ctx) bool { - originalURL := strings.ToLower(c.OriginalURL()) - - for _, pattern := range Authurls { - if pattern.MatchString(originalURL) { - return false - } - } - return true + return false, keyauth.ErrMissingOrMalformedAPIKey } diff --git a/upd/api/db.go b/upd/api/db.go index d1a5be5..4936d3c 100644 --- a/upd/api/db.go +++ b/upd/api/db.go @@ -20,6 +20,7 @@ package api import ( "encoding/json" "fmt" + "github.com/alecthomas/repr" bolt "go.etcd.io/bbolt" ) @@ -37,6 +38,11 @@ type Upload struct { File string `json:"file"` // final filename (visible to the downloader) Members []string `json:"members"` // contains multiple files, so File is an archive Uploaded Timestamp `json:"uploaded"` + Context string `json:"context"` +} + +type Uploads struct { + Entries []*Upload `json:"uploads"` } func NewDb(file string) (*Db, error) { @@ -125,22 +131,30 @@ func (db *Db) Delete(id string) error { return err } -func (db *Db) Iterate(iterator func(id string, upload Upload)) error { - var upload Upload +func (db *Db) List(apicontext string) (*Uploads, error) { + uploads := &Uploads{} err := db.bolt.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(Bucket)) err := bucket.ForEach(func(id, j []byte) error { + upload := &Upload{} if err := json.Unmarshal(j, &upload); err != nil { return fmt.Errorf("unable to unmarshal json: %s", err) } - iterator(string(id), upload) + if apicontext != "" { + if apicontext == upload.Context { + uploads.Entries = append(uploads.Entries, upload) + } + } else { + uploads.Entries = append(uploads.Entries, upload) + } + return nil }) return err // might be nil as well }) - return err + return uploads, err } diff --git a/upd/api/handlers.go b/upd/api/handlers.go index fc70f5f..72a3557 100644 --- a/upd/api/handlers.go +++ b/upd/api/handlers.go @@ -18,10 +18,12 @@ along with this program. If not, see . package api import ( + "github.com/alecthomas/repr" "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/tlinden/up/upd/cfg" + "encoding/json" "os" "path/filepath" "time" @@ -89,8 +91,22 @@ func FilePut(c *fiber.Ctx, cfg *cfg.Config, db *Db) (string, error) { } entry.File = Newfilename + // retrieve the API Context name from the session, assuming is has + // been successfully authenticated. However, if there are no api + // contexts defined, we'll use 'default' (set in + // auth.validateAPIKey()) + sess, err := Sessionstore.Get(c) + if err != nil { + return "", fiber.NewError(500, "Unable to initialize session store from context!") + } + apicontext := sess.Get("apicontext") + if apicontext != nil { + entry.Context = apicontext.(string) + } + Log("Now serving %s from %s/%s", returnUrl, cfg.StorageDir, id) Log("Expire set to: %s", entry.Expire) + Log("Uploaded with API-Context %s", entry.Context) // we do this in the background to not thwart the server go db.Insert(id, entry) @@ -167,3 +183,24 @@ func FileDelete(c *fiber.Ctx, cfg *cfg.Config, db *Db) error { return nil } + +func List(c *fiber.Ctx, cfg *cfg.Config, db *Db) (string, error) { + apicontext, err := Untaint(c.Params("apicontext"), `[^a-zA-Z0-9\-]`) + if err != nil { + return "", fiber.NewError(403, "Invalid api context provided!") + } + + uploads, err := db.List(apicontext) + repr.Print(uploads) + if err != nil { + return "", fiber.NewError(500, "Unable to list uploads: "+err.Error()) + } + + jsonlist, err := json.Marshal(uploads) + if err != nil { + return "", fiber.NewError(500, "json marshalling failure: "+err.Error()) + } + + Log(string(jsonlist)) + return string(jsonlist), nil +} diff --git a/upd/api/server.go b/upd/api/server.go index 297233b..d4dbaf7 100644 --- a/upd/api/server.go +++ b/upd/api/server.go @@ -18,14 +18,21 @@ along with this program. If not, see . package api import ( + "errors" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/requestid" + "github.com/gofiber/fiber/v2/middleware/session" "github.com/gofiber/keyauth/v2" "github.com/tlinden/up/upd/cfg" ) +// sessions are context specific and can be global savely +var Sessionstore *session.Store + func Runserver(cfg *cfg.Config, args []string) error { + Sessionstore = session.New() + router := fiber.New(fiber.Config{ CaseSensitive: true, StrictRouting: true, @@ -49,10 +56,11 @@ func Runserver(cfg *cfg.Config, args []string) error { defer db.Close() AuthSetEndpoints(cfg.ApiPrefix, ApiVersion, []string{"/file"}) - AuthSetApikeys(cfg.Apikeys) + AuthSetApikeys(cfg.Apicontext) auth := keyauth.New(keyauth.Config{ - Validator: validateAPIKey, + Validator: AuthValidateAPIKey, + ErrorHandler: AuthErrHandler, }) shallExpire := true @@ -76,6 +84,11 @@ func Runserver(cfg *cfg.Config, args []string) error { api.Delete("/file/:id/", auth, func(c *fiber.Ctx) error { return FileDelete(c, cfg, db) }) + + api.Get("/list/", auth, func(c *fiber.Ctx) error { + msg, err := List(c, cfg, db) + return SendResponse(c, msg, err) + }) } // public routes @@ -96,11 +109,16 @@ func Runserver(cfg *cfg.Config, args []string) error { } func SendResponse(c *fiber.Ctx, msg string, err error) error { - // FIXME: - // respect fiber.NewError(500, "Could not process uploaded file[s]!") if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(Result{ - Code: fiber.StatusBadRequest, + code := fiber.StatusInternalServerError + + var e *fiber.Error + if errors.As(err, &e) { + code = e.Code + } + + return c.Status(code).JSON(Result{ + Code: code, Message: err.Error(), Success: false, }) diff --git a/upd/cfg/config.go b/upd/cfg/config.go index 86fb389..c62c723 100644 --- a/upd/cfg/config.go +++ b/upd/cfg/config.go @@ -25,25 +25,31 @@ const Version string = "v0.0.1" var VERSION string // maintained by -x +type Apicontext struct { + Context string `koanf:"context"` // aka name or tenant + Key string `koanf:"key"` +} + +// holds the whole configs, filled by commandline flags, env and config file type Config struct { - ApiPrefix string - Debug bool - Listen string - StorageDir string - Url string - DbFile string + ApiPrefix string `koanf:"apiprefix"` + Debug bool `koanf:"debug"` + Listen string `koanf:"listen"` + StorageDir string `koanf:"storagedir"` + Url string `koanf:"url"` + DbFile string `koanf:"dbfile"` // fiber settings, see: // https://docs.gofiber.io/api/fiber/#config - Prefork bool - AppName string - BodyLimit int - V4only bool - V6only bool + Prefork bool `koanf:"prefork"` + AppName string `koanf:"appname"` + BodyLimit int `koanf:"bodylimit"` + V4only bool `koanf:"ipv4"` + V6only bool `koanf:"ipv6"` Network string // only settable via config - Apikeys []string + Apicontext []Apicontext `koanf:"apicontext"` } func Getversion() string { diff --git a/upd/cmd/root.go b/upd/cmd/root.go index bb9fd3a..cc33edd 100644 --- a/upd/cmd/root.go +++ b/upd/cmd/root.go @@ -19,12 +19,21 @@ package cmd import ( "errors" "fmt" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" + + "github.com/knadh/koanf/parsers/hcl" + "github.com/knadh/koanf/providers/env" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/providers/posflag" + "github.com/knadh/koanf/v2" + + flag "github.com/spf13/pflag" + + "github.com/alecthomas/repr" "github.com/tlinden/up/upd/api" "github.com/tlinden/up/upd/cfg" + "os" + "path/filepath" "strings" ) @@ -32,126 +41,93 @@ var ( cfgFile string ) -func completion(cmd *cobra.Command, mode string) error { - switch mode { - case "bash": - return cmd.Root().GenBashCompletion(os.Stdout) - case "zsh": - return cmd.Root().GenZshCompletion(os.Stdout) - case "fish": - return cmd.Root().GenFishCompletion(os.Stdout, true) - case "powershell": - return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) - default: - return errors.New("Invalid shell parameter! Valid ones: bash|zsh|fish|powershell") - } -} - -func Execute() { +func Execute() error { var ( - conf cfg.Config - ShowVersion bool - ShowCompletion string + conf cfg.Config + ShowVersion bool ) - var rootCmd = &cobra.Command{ - Use: "upd [options]", - Short: "upload daemon", - Long: `Run an upload daemon reachable via REST API`, - RunE: func(cmd *cobra.Command, args []string) error { - if ShowVersion { - fmt.Println(cfg.Getversion()) - return nil - } - - if len(ShowCompletion) > 0 { - return completion(cmd, ShowCompletion) - } - - conf.ApplyDefaults() - - // actual execution starts here - return api.Runserver(&conf, args) - }, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - return initConfig(cmd) - }, + f := flag.NewFlagSet("config", flag.ContinueOnError) + f.Usage = func() { + fmt.Println(f.FlagUsages()) + os.Exit(0) } - // options - rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "v", false, "Print program version") - rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "custom config file") - rootCmd.PersistentFlags().BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging") - rootCmd.PersistentFlags().StringVarP(&conf.Listen, "listen", "l", ":8080", "listen to custom ip:port (use [ip]:port for ipv6)") - rootCmd.PersistentFlags().StringVarP(&conf.StorageDir, "storagedir", "s", "/tmp", "storage directory for uploaded files") - rootCmd.PersistentFlags().StringVarP(&conf.ApiPrefix, "apiprefix", "a", "/api", "API endpoint path") - rootCmd.PersistentFlags().StringVarP(&conf.Url, "url", "u", "", "HTTP endpoint w/o path") - rootCmd.PersistentFlags().StringVarP(&conf.DbFile, "dbfile", "D", "/tmp/uploads.db", "Bold database file to use") + f.BoolVarP(&ShowVersion, "version", "v", false, "Print program version") + f.StringVarP(&cfgFile, "config", "c", "", "custom config file") + f.BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging") + f.StringVarP(&conf.Listen, "listen", "l", ":8080", "listen to custom ip:port (use [ip]:port for ipv6)") + f.StringVarP(&conf.StorageDir, "storagedir", "s", "/tmp", "storage directory for uploaded files") + f.StringVarP(&conf.ApiPrefix, "apiprefix", "a", "/api", "API endpoint path") + f.StringVarP(&conf.Url, "url", "u", "", "HTTP endpoint w/o path") + f.StringVarP(&conf.DbFile, "dbfile", "D", "/tmp/uploads.db", "Bold database file to use") // server settings - rootCmd.PersistentFlags().BoolVarP(&conf.V4only, "ipv4", "4", false, "Only listen on ipv4") - rootCmd.PersistentFlags().BoolVarP(&conf.V6only, "ipv6", "6", false, "Only listen on ipv6") - rootCmd.MarkFlagsMutuallyExclusive("ipv4", "ipv6") + f.BoolVarP(&conf.V4only, "ipv4", "4", false, "Only listen on ipv4") + f.BoolVarP(&conf.V6only, "ipv6", "6", false, "Only listen on ipv6") - rootCmd.PersistentFlags().BoolVarP(&conf.Prefork, "prefork", "p", false, "Prefork server threads") - rootCmd.PersistentFlags().StringVarP(&conf.AppName, "appname", "n", "upd "+conf.GetVersion(), "App name to say hi as") - rootCmd.PersistentFlags().IntVarP(&conf.BodyLimit, "bodylimit", "b", 10250000000, "Max allowed upload size in bytes") - rootCmd.PersistentFlags().StringSliceVarP(&conf.Apikeys, "apikey", "", []string{}, "Api key[s] to allow access") - err := rootCmd.Execute() - if err != nil { - os.Exit(1) + f.BoolVarP(&conf.Prefork, "prefork", "p", false, "Prefork server threads") + f.StringVarP(&conf.AppName, "appname", "n", "upd "+conf.GetVersion(), "App name to say hi as") + f.IntVarP(&conf.BodyLimit, "bodylimit", "b", 10250000000, "Max allowed upload size in bytes") + f.StringSliceP("apikeys", "", []string{}, "Api key[s] to allow access") + + f.Parse(os.Args[1:]) + + // exclude -6 and -4 + if conf.V4only && conf.V6only { + return errors.New("You cannot mix -4 and -6!") } -} -// initialize viper, read config and ENV, bind flags -func initConfig(cmd *cobra.Command) error { - v := viper.New() - viper.SetConfigType("hcl") + // config provider + var k = koanf.New(".") - if cfgFile != "" { - // Use config file from the flag. - v.SetConfigFile(cfgFile) + // Load the config files provided in the commandline or the default locations + configfiles := []string{} + configfile, _ := f.GetString("config") + if configfile != "" { + configfiles = []string{configfile} } else { - v.SetConfigName("upd") - - // default location[s] - v.AddConfigPath(".") - v.AddConfigPath("$HOME/.config/upd") - v.AddConfigPath("/etc") - v.AddConfigPath("/usr/local/etc") - + configfiles = []string{ + "/etc/upd.hcl", "/usr/local/etc/upd.hcl", // unix variants + filepath.Join(os.Getenv("HOME"), ".config", "upd", "upd.hcl"), + filepath.Join(os.Getenv("HOME"), ".upd"), + "upd.hcl", + } } - // ignore read errors, report all others - if err := v.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - return err + for _, cfgfile := range configfiles { + if _, err := os.Stat(cfgfile); err == nil { + if err := k.Load(file.Provider(cfgfile), hcl.Parser(true)); err != nil { + return errors.New("error loading config file: " + err.Error()) + } } - fmt.Println(err) + // else: we ignore the file if it doesn't exists } - fmt.Println("Using config file:", v.ConfigFileUsed()) + // env overrides config file + k.Load(env.Provider("UPD_", ".", func(s string) string { + return strings.Replace(strings.ToLower( + strings.TrimPrefix(s, "UPD_")), "_", ".", -1) + }), nil) - v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - v.AutomaticEnv() - v.SetEnvPrefix("upd") + // command line overrides env + if err := k.Load(posflag.Provider(f, ".", k), nil); err != nil { + return errors.New("error loading config: " + err.Error()) + } - // map flags to viper - bindFlags(cmd, v) + // fetch values + k.Unmarshal("", &conf) - return nil -} - -// bind flags to viper settings (env+cfgfile) -func bindFlags(cmd *cobra.Command, v *viper.Viper) { - cmd.Flags().VisitAll(func(f *pflag.Flag) { - // map flag name to config variable - configName := f.Name - - // use config variable if flag is not set and config is set - if !f.Changed && v.IsSet(configName) { - val := v.Get(configName) - cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) - } - }) + if conf.Debug { + repr.Print(conf) + } + + switch { + case ShowVersion: + fmt.Println(cfg.Getversion()) + return nil + default: + conf.ApplyDefaults() + return api.Runserver(&conf, flag.Args()) + } } diff --git a/upd/go.mod b/upd/go.mod index ea68896..63c6793 100644 --- a/upd/go.mod +++ b/upd/go.mod @@ -11,6 +11,7 @@ require ( ) require ( + github.com/alecthomas/repr v0.2.0 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -24,12 +25,20 @@ require ( github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.9 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/parsers/hcl v0.1.0 // indirect + github.com/knadh/koanf/providers/env v0.1.0 // indirect + github.com/knadh/koanf/providers/file v0.1.0 // indirect + github.com/knadh/koanf/providers/posflag v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.0.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect diff --git a/upd/go.sum b/upd/go.sum index 4070075..8701957 100644 --- a/upd/go.sum +++ b/upd/go.sum @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -158,6 +160,18 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/parsers/hcl v0.1.0 h1:PuAAdRMXbxmhwzZftiQBEtWIKc3EbRHk/Fi+olo02z4= +github.com/knadh/koanf/parsers/hcl v0.1.0/go.mod h1:7ClRvH1oP5ne8SfaDZZBK28/o9r4rek0PC4Vrc8qdvE= +github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= +github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= +github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c= +github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA= +github.com/knadh/koanf/providers/posflag v0.1.0 h1:mKJlLrKPcAP7Ootf4pBZWJ6J+4wHYujwipe7Ie3qW6U= +github.com/knadh/koanf/providers/posflag v0.1.0/go.mod h1:SYg03v/t8ISBNrMBRMlojH8OsKowbkXV7giIbBVgbz0= +github.com/knadh/koanf/v2 v2.0.0 h1:XPQ5ilNnwnNaHrfQ1YpTVhUAjcGHnEKA+lRpipQv02Y= +github.com/knadh/koanf/v2 v2.0.0/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -179,8 +193,12 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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= diff --git a/upd/main.go b/upd/main.go index be63f84..54fac11 100644 --- a/upd/main.go +++ b/upd/main.go @@ -19,8 +19,12 @@ package main import ( "github.com/tlinden/up/upd/cmd" + "log" ) func main() { - cmd.Execute() + err := cmd.Execute() + if err != nil { + log.Fatalf(err.Error()) + } } diff --git a/upd/nokeys.hcl b/upd/nokeys.hcl new file mode 100644 index 0000000..42fbd25 --- /dev/null +++ b/upd/nokeys.hcl @@ -0,0 +1,6 @@ +# -*-ruby-*- +listen = ":8080" +bodylimit = 10000 + + + diff --git a/upd/upd.hcl b/upd/upd.hcl index a07c2b3..ef61dbb 100644 --- a/upd/upd.hcl +++ b/upd/upd.hcl @@ -1,6 +1,16 @@ # -*-ruby-*- listen = ":8080" -apikeys = [ - "0fddbff5d8010f81cd28a7d77f3e38981b13d6164c2fd6e1c3f60a4287630c37", - "970b391f22f515d96b3e9b86a2c62c627968828e47b356994d2e583188b4190a" +bodylimit = 10000 + +apicontext = [ + { + context = "default" + key = "0fddbff5d8010f81cd28a7d77f3e38981b13d6164c2fd6e1c3f60a4287630c37", + }, + { + context = "foo", + key = "970b391f22f515d96b3e9b86a2c62c627968828e47b356994d2e583188b4190a" + } ] + +