From 3b37a67e02d2ee7f387d9d0bf7721bf202ce625c Mon Sep 17 00:00:00 2001 From: "T. von Dein" Date: Tue, 23 Dec 2025 14:38:45 +0100 Subject: [PATCH] Replace fiber with net.http.ServeMux (#23) --- anydb.1 | 4 +- anydb.pod | 7 +- app/attr.go | 11 ++- cfg/config.go | 43 +++++++++++- cmd/crud.go | 4 +- go.mod | 9 +-- go.sum | 20 ++---- rest/handlers.go | 175 +++++++++++++++++++++++++---------------------- rest/log.go | 98 ++++++++++++++++++++++++++ rest/serve.go | 118 +++++++++++++------------------- 10 files changed, 306 insertions(+), 183 deletions(-) create mode 100644 rest/log.go diff --git a/anydb.1 b/anydb.1 index 1fd4492..5963217 100644 --- a/anydb.1 +++ b/anydb.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "ANYDB 1" -.TH ANYDB 1 "2025-02-11" "1" "User Commands" +.TH ANYDB 1 "2025-11-03" "1" "User Commands" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/anydb.pod b/anydb.pod index d7ab50f..836585e 100644 --- a/anydb.pod +++ b/anydb.pod @@ -569,7 +569,7 @@ Some curl example calls to the API: Post a new key: curl -X PUT localhost:8787/anydb/v1/ \ -H 'Content-Type: application/json' \ - -d '{"key":"foo","val":"bar"}' + -d '{"key":"foo","data":"bar"}' Retrieve the value: @@ -579,6 +579,11 @@ List all keys: curl localhost:8787/anydb/v1/ +Delete an entry: + + curl -s -X DELETE http://localhost:8787/anydb/v1/foo + + =head1 BUGS In order to report a bug, unexpected behavior, feature requests diff --git a/app/attr.go b/app/attr.go index ecc08bf..5866d21 100644 --- a/app/attr.go +++ b/app/attr.go @@ -1,5 +1,5 @@ /* -Copyright © 2024 Thomas von Dein +Copyright © 2024-2025 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 @@ -28,6 +28,7 @@ type DbAttr struct { Key string Preview string Val []byte + Data string // alias Args []string Tags []string File string @@ -63,6 +64,12 @@ func (attr *DbAttr) ParseKV() error { } } + attr.SetPreview() + + return nil +} + +func (attr *DbAttr) SetPreview() { switch { case attr.Binary: attr.Preview = "" @@ -82,8 +89,6 @@ func (attr *DbAttr) ParseKV() error { attr.Preview = string(attr.Val) } } - - return nil } func (attr *DbAttr) GetFileValue() error { diff --git a/cfg/config.go b/cfg/config.go index c7b4402..f860ffe 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -19,14 +19,18 @@ package cfg import ( "fmt" "io" + "log/slog" "os" + "runtime/debug" - "github.com/pelletier/go-toml" "codeberg.org/scip/anydb/app" "codeberg.org/scip/anydb/common" + "github.com/lmittmann/tint" + "github.com/pelletier/go-toml" + "github.com/tlinden/yadu" ) -var Version string = "v0.2.6" +var Version string = "v0.3.0" type BucketConfig struct { Encrypt bool @@ -58,6 +62,8 @@ func (conf *Config) GetConfig(files []string) error { } } + conf.SetLogger() + return nil } @@ -114,3 +120,36 @@ func (conf *Config) ParseConfigFile(file string) error { return nil } + +func (conf *Config) SetLogger() { + if conf.Debug { + buildInfo, _ := debug.ReadBuildInfo() + opts := &yadu.Options{ + Level: slog.LevelDebug, + AddSource: true, + } + + slog.SetLogLoggerLevel(slog.LevelDebug) + + handler := yadu.NewHandler(os.Stdout, opts) + debuglogger := slog.New(handler).With( + slog.Group("program_info", + slog.Int("pid", os.Getpid()), + slog.String("go_version", buildInfo.GoVersion), + ), + ) + slog.SetDefault(debuglogger) + + slog.Debug("parsed config", "conf", conf) + } else { + opts := &tint.Options{ + Level: slog.LevelInfo, + AddSource: false, + } + + handler := tint.NewHandler(os.Stderr, opts) + logger := slog.New(handler) + + slog.SetDefault(logger) + } +} diff --git a/cmd/crud.go b/cmd/crud.go index 5b90920..27f0c8f 100644 --- a/cmd/crud.go +++ b/cmd/crud.go @@ -1,5 +1,5 @@ /* -Copyright © 2024 Thomas von Dein +Copyright © 2024-2025 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 @@ -21,10 +21,10 @@ import ( "os" "strings" - "github.com/spf13/cobra" "codeberg.org/scip/anydb/app" "codeberg.org/scip/anydb/cfg" "codeberg.org/scip/anydb/output" + "github.com/spf13/cobra" ) func Set(conf *cfg.Config) *cobra.Command { diff --git a/go.mod b/go.mod index dd0ddfe..6ecfbd5 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.24.1 require ( github.com/dustin/go-humanize v1.0.1 - github.com/gofiber/fiber/v2 v2.52.9 github.com/inconshreveable/mousetrap v1.1.0 + github.com/lmittmann/tint v1.1.2 github.com/olekukonko/tablewriter v1.1.0 github.com/pelletier/go-toml v1.9.5 github.com/rogpeppe/go-internal v1.14.1 @@ -20,10 +20,8 @@ require ( ) require ( - github.com/andybalholm/brotli v1.1.1 // indirect + github.com/alecthomas/repr v0.5.2 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -31,9 +29,6 @@ require ( github.com/olekukonko/ll v0.0.9 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.9 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.55.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/tools v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 6e1ea9f..f243fcd 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= +github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -7,16 +7,12 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= -github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w= +github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -46,14 +42,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tlinden/yadu v0.1.3 h1:5cRCUmj+l5yvlM2irtpFBIJwVV2DPEgYSaWvF19FtcY= github.com/tlinden/yadu v0.1.3/go.mod h1:l3bRmHKL9zGAR6pnBHY2HRPxBecf7L74BoBgOOpTcUA= -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.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= -github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= diff --git a/rest/handlers.go b/rest/handlers.go index ecc9d02..6ade7d4 100644 --- a/rest/handlers.go +++ b/rest/handlers.go @@ -1,5 +1,5 @@ /* -Copyright © 2024 Thomas von Dein +Copyright © 2024-2025 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 @@ -19,7 +19,10 @@ package rest import ( //"github.com/alecthomas/repr" - "github.com/gofiber/fiber/v2" + "encoding/json" + "log" + "net/http" + "codeberg.org/scip/anydb/app" "codeberg.org/scip/anydb/cfg" ) @@ -40,101 +43,113 @@ type SingleResponse struct { Entry *app.DbEntry } -func RestList(c *fiber.Ctx, conf *cfg.Config) error { +func RestList(resp http.ResponseWriter, req *http.Request, conf *cfg.Config) { attr := new(app.DbAttr) - if len(c.Body()) > 0 { - if err := c.BodyParser(attr); err != nil { - return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ - "errors": err.Error(), - }) - + err := json.NewDecoder(req.Body).Decode(&attr) + if err != nil { + if err.Error() != `EOF` { + http.Error(resp, err.Error(), http.StatusBadRequest) + return } } // get list entries, err := conf.DB.List(attr, attr.Fulltext) if err != nil { - return JsonStatus(c, fiber.StatusForbidden, - "Unable to list keys: "+err.Error()) + JsonStatus(resp, http.StatusForbidden, "Unable to list keys: "+err.Error()) + return } - return c.Status(fiber.StatusOK).JSON( + resp.Header().Set("Content-Type", "application/json") + + err = json.NewEncoder(resp).Encode( ListResponse{ + Code: http.StatusOK, Success: true, - Code: fiber.StatusOK, Entries: entries, - }, - ) -} - -func RestGet(c *fiber.Ctx, conf *cfg.Config) error { - if c.Params("key") == "" { - return JsonStatus(c, fiber.StatusForbidden, - "key not provided") - } - - // get list - entry, err := conf.DB.Get(&app.DbAttr{Key: c.Params("key")}) - if err != nil { - return JsonStatus(c, fiber.StatusForbidden, - "Unable to get key: "+err.Error()) - } - if entry.Key == "" { - return JsonStatus(c, fiber.StatusForbidden, - "Key does not exist") - } - - return c.Status(fiber.StatusOK).JSON( - SingleResponse{ - Success: true, - Code: fiber.StatusOK, - Entry: entry, - }, - ) -} - -func RestDelete(c *fiber.Ctx, conf *cfg.Config) error { - if c.Params("key") == "" { - return JsonStatus(c, fiber.StatusForbidden, - "key not provided") - } - - // get list - err := conf.DB.Del(&app.DbAttr{Key: c.Params("key")}) - if err != nil { - return JsonStatus(c, fiber.StatusForbidden, - "Unable to delete key: "+err.Error()) - } - - return c.Status(fiber.StatusOK).JSON( - Result{ - Success: true, - Code: fiber.StatusOK, - Message: "key deleted", - }, - ) -} - -func RestSet(c *fiber.Ctx, conf *cfg.Config) error { - attr := new(app.DbAttr) - if err := c.BodyParser(attr); err != nil { - return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ - "errors": err.Error(), }) - } - - err := conf.DB.Set(attr) if err != nil { - return JsonStatus(c, fiber.StatusForbidden, - "Unable to set key: "+err.Error()) + log.Fatal(err) + } +} + +func RestGet(resp http.ResponseWriter, req *http.Request, key string, conf *cfg.Config) { + if key == "" { + JsonStatus(resp, http.StatusForbidden, "key not provided") + return } - return c.Status(fiber.StatusOK).JSON( - Result{ + // get list + entry, err := conf.DB.Get(&app.DbAttr{Key: key}) + if err != nil { + JsonStatus(resp, http.StatusForbidden, "Unable to get key: "+err.Error()) + return + } + if entry.Key == "" { + JsonStatus(resp, http.StatusForbidden, "Key does not exist") + return + } + + resp.Header().Set("Content-Type", "application/json") + + err = json.NewEncoder(resp).Encode( + SingleResponse{ + Code: http.StatusOK, Success: true, - Code: fiber.StatusOK, - }, - ) + Entry: entry, + }) + + if err != nil { + log.Fatal(err) + } +} + +func RestDelete(resp http.ResponseWriter, req *http.Request, key string, conf *cfg.Config) { + if key == "" { + JsonStatus(resp, http.StatusForbidden, "key not provided") + return + } + + // get list + err := conf.DB.Del(&app.DbAttr{Key: key}) + if err != nil { + JsonStatus(resp, http.StatusForbidden, "Unable to delete key: "+err.Error()) + return + } + + JsonStatus(resp, http.StatusOK, "key deleted") +} + +func RestSet(resp http.ResponseWriter, req *http.Request, conf *cfg.Config) { + attr := new(app.DbAttr) + + err := json.NewDecoder(req.Body).Decode(&attr) + if err != nil { + http.Error(resp, err.Error(), http.StatusBadRequest) + return + } + + // attr.Data is a string, thus the decoder doesn't expect it to be + // base64 encoded. However, internally we need []byte, therefore + // we copy a cast to .Val. We also need to setup the .Preview + // value here. + attr.Val = []byte(attr.Data) + attr.SetPreview() + + err = conf.DB.Set(attr) + if err != nil { + JsonStatus(resp, http.StatusForbidden, "Unable to set key: "+err.Error()) + return + } + + JsonStatus(resp, http.StatusOK, "key added/updated") +} + +func Home(resp http.ResponseWriter) { + _, err := resp.Write([]byte("Use the REST API on " + apiprefix + "\r\n")) + if err != nil { + log.Fatal(err) + } } diff --git a/rest/log.go b/rest/log.go new file mode 100644 index 0000000..577cc6a --- /dev/null +++ b/rest/log.go @@ -0,0 +1,98 @@ +/* + This logging middleware is based on + +https://github.com/elithrar/admission-control/blob/v0.6.3/request_logger.go + + by Matt Silverlock licensed under the Apache-2.0 license. + + I am using slog and added a couple of small modifications. +*/ +package rest + +import ( + "log/slog" + "net/http" + "runtime/debug" + "time" +) + +// responseWriter is a minimal wrapper for http.ResponseWriter that allows the +// written HTTP status code to be captured for logging. +type responseWriter struct { + http.ResponseWriter + status int + size int + wroteHeader bool +} + +func wrapResponseWriter(w http.ResponseWriter) *responseWriter { + return &responseWriter{ResponseWriter: w} +} + +func (rw *responseWriter) Status() int { + return rw.status +} + +func (rw *responseWriter) Size() int { + return rw.size +} + +func (rw *responseWriter) WriteHeader(code int) { + if rw.wroteHeader { + return + } + + rw.status = code + rw.ResponseWriter.WriteHeader(code) + rw.wroteHeader = true +} + +func (rw *responseWriter) Write(data []byte) (int, error) { + + written, err := rw.ResponseWriter.Write(data) + rw.size += written + + return written, err +} + +// LoggingMiddleware logs the incoming HTTP request & its duration. +func LogHandler() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(resp http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + resp.WriteHeader(http.StatusInternalServerError) + slog.Info( + "internal server error", + "err", err, + "trace", string(debug.Stack()), + ) + } + }() + + start := time.Now() + wrapped := wrapResponseWriter(resp) + next.ServeHTTP(wrapped, req) + + header := wrapped.Header()["Content-Type"] + contenttype := "" + if header == nil { + contenttype = "text/plain" + } else { + contenttype = header[0] + } + + slog.Info("request", + "ip", req.RemoteAddr, + "status", wrapped.status, + "method", req.Method, + "path", req.URL.EscapedPath(), + "size", wrapped.Size(), + "content-type", contenttype, + "duration", time.Since(start), + ) + } + + return http.HandlerFunc(fn) + } +} diff --git a/rest/serve.go b/rest/serve.go index fed2881..fb8f0ab 100644 --- a/rest/serve.go +++ b/rest/serve.go @@ -1,5 +1,5 @@ /* -Copyright © 2024 Thomas von Dein +Copyright © 2024-2025 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 @@ -17,10 +17,10 @@ along with this program. If not, see . package rest import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/compress" - "github.com/gofiber/fiber/v2/middleware/cors" - "github.com/gofiber/fiber/v2/middleware/logger" + "encoding/json" + "log" + "net/http" + "codeberg.org/scip/anydb/cfg" ) @@ -31,84 +31,62 @@ type Result struct { Code int `json:"code"` } +const apiprefix = `/anydb/v1/` + func Runserver(conf *cfg.Config, args []string) error { // setup api server - router := SetupServer(conf) + mux := http.NewServeMux() - // public rest api routes - api := router.Group("/anydb/v1") - { - api.Get("/", func(c *fiber.Ctx) error { - return RestList(c, conf) - }) - - api.Post("/", func(c *fiber.Ctx) error { - // same thing as above but allows to supply parameters, see app.Dbattr{} - return RestList(c, conf) - }) - - api.Get("/:key", func(c *fiber.Ctx) error { - return RestGet(c, conf) - }) - - api.Delete("/:key", func(c *fiber.Ctx) error { - return RestDelete(c, conf) - }) - - api.Put("/", func(c *fiber.Ctx) error { - return RestSet(c, conf) - }) - } - - // public routes - { - router.Get("/", func(c *fiber.Ctx) error { - return c.Send([]byte("Use the REST API")) - }) - } - - return router.Listen(conf.Listen) -} - -func SetupServer(conf *cfg.Config) *fiber.App { - // disable colors - fiber.DefaultColors = fiber.Colors{} - - router := fiber.New(fiber.Config{ - CaseSensitive: true, - StrictRouting: true, - Immutable: true, - ServerHeader: "anydb serve", - AppName: "anydb", + // just in case someone tries to access the non-api url + mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { + Home(w) }) - router.Use(logger.New(logger.Config{ - Format: "${pid} ${ip}:${port} ${status} - ${method} ${path}\n", - DisableColors: true, - })) + mux.HandleFunc("GET "+apiprefix+"{$}", func(w http.ResponseWriter, r *http.Request) { + RestList(w, r, conf) + }) - router.Use(cors.New(cors.Config{ - AllowMethods: "GET,PUT,POST,DELETE", - ExposeHeaders: "Content-Type,Accept", - })) + mux.HandleFunc("POST "+apiprefix+"{$}", func(w http.ResponseWriter, r *http.Request) { + RestList(w, r, conf) + }) - router.Use(compress.New(compress.Config{ - Level: compress.LevelBestSpeed, - })) + mux.HandleFunc("GET "+apiprefix+"{key}", func(w http.ResponseWriter, r *http.Request) { + key := r.PathValue("key") + RestGet(w, r, key, conf) + }) - return router + mux.HandleFunc("DELETE "+apiprefix+"{key}", func(w http.ResponseWriter, r *http.Request) { + key := r.PathValue("key") + RestDelete(w, r, key, conf) + }) + + mux.HandleFunc("PUT "+apiprefix, func(w http.ResponseWriter, r *http.Request) { + RestSet(w, r, conf) + }) + + logger := LogHandler() + + return http.ListenAndServe(conf.Listen, logger(mux)) } /* Wrapper to respond with proper json status, message and code, shall be prepared and called by the handlers directly. */ -func JsonStatus(c *fiber.Ctx, code int, msg string) error { - success := code == fiber.StatusOK +func JsonStatus(resp http.ResponseWriter, code int, msg string) { + success := code == http.StatusOK - return c.Status(code).JSON(Result{ - Code: code, - Message: msg, - Success: success, - }) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(code) + + err := json.NewEncoder(resp).Encode( + Result{ + Code: code, + Message: msg, + Success: success, + }) + + if err != nil { + log.Fatal(err) + } }