diff --git a/go.mod b/go.mod index fb907c3..23c9ec3 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/go-gormigrate/gormigrate/v2 v2.1.7 github.com/go-sql-driver/mysql v1.10.1 github.com/goccy/go-json v0.11.2 - github.com/gofiber/fiber/v2 v2.52.15 github.com/gofiber/fiber/v3 v3.5.0 github.com/gorilla/websocket v1.5.3 github.com/lib/pq v1.12.3 @@ -52,10 +51,8 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.51.1 // indirect github.com/aws/smithy-go v1.28.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v7 v7.0.1 // indirect - github.com/cenkalti/backoff/v7 v7.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -73,6 +70,8 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/gofiber/schema v1.8.3 // indirect + github.com/gofiber/utils/v2 v2.4.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.4 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -93,7 +92,6 @@ require ( github.com/magiconair/properties v1.18.12 // indirect github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.24 // indirect - github.com/mattn/go-runewidth v0.0.30 // indirect github.com/mattn/go-sqlite3 v1.14.52 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.3.3 // indirect @@ -109,6 +107,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect + github.com/philhofer/fwd v1.2.0 // indirect github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0 // indirect github.com/prometheus/client_model v0.6.3 // indirect github.com/prometheus/common v0.72.0 // indirect @@ -121,6 +120,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tinylib/msgp v1.6.4 // indirect github.com/tklauser/go-sysconf v0.4.0 // indirect github.com/tklauser/numcpus v0.12.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/go.sum b/go.sum index 42ddad9..84f359b 100644 --- a/go.sum +++ b/go.sum @@ -48,11 +48,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v7 v7.0.1/go.mod h1:qcKBGwsu4hpxHtQ8tWYsQ+ifzx2+sS+Xx/3jfe30lI8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= -github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudsoda/go-smb2 v0.0.0-20260918041005-0c5d69b69701 h1:xiTow1n7oNJPzn9QH+U2sT65mO2F12zNNlY4ZzOew70= github.com/cloudsoda/go-smb2 v0.0.0-20260918041005-0c5d69b69701/go.mod h1:1pQXB0vAlzRlqcY7LYKOOZMw0wKfJPFxTLsJRF2Gswo= github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc h1:0xCWmFKBmarCqqqLeM7jFBSw/Or81UEElFqO8MY+GDs= @@ -86,6 +83,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= +github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64= github.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/go-gormigrate/gormigrate/v2 v2.1.7 h1:PdT4jVPbRb4R+0Ey2R0yJOdctVf4Whiq1Qi4necaZdg= @@ -104,9 +103,12 @@ github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPE github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.11.2 h1:jdZv93Tt4ioR8yW1CoNsvSxrcZlCXAUU1aZXN7gpXUA= github.com/goccy/go-json v0.11.2/go.mod h1:3NdmfEkZlB7YI5UFw/qdFKq8XN1aiWR0YyRPWZNQltY= -github.com/gofiber/fiber/v2 v2.52.15 h1:Cov1uKeVPyu9q0jSrN60W+A8XNX+/WK8J7cy5osHLIk= -github.com/gofiber/fiber/v2 v2.52.15/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/fiber/v3 v3.5.0 h1:dk7TOUH6DXJGtOLsN2XEG+0ZML7cznzHILTVozbNEK8= github.com/gofiber/fiber/v3 v3.5.0/go.mod h1:GOVDTW+gjJvfe0iJyVujbQ1Lnx+JUjFySJRI/9/xX/w= +github.com/gofiber/schema v1.8.3 h1:06ZedxIYjngzc0095PYy7uWnFnbRflWFpikvZH61fDc= +github.com/gofiber/schema v1.8.3/go.mod h1:jWnnZdhcW1mHyV+VnfRxKJDPNcepJsTZ9RIWxrr32Ng= +github.com/gofiber/utils/v2 v2.4.1 h1:E2X9G8O5Mn7b2GDb0JU3IUk42Rw2npuhhepIbuJQ2po= +github.com/gofiber/utils/v2 v2.4.1/go.mod h1:I+RTsgMUdzFuifVc3LOEkfh32wQW9BfRl7l5RYjamW4= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -165,8 +167,6 @@ github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= -github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk= -github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/mattn/go-sqlite3 v1.14.52 h1:wVbm2Qnf4OXkqhBTSPuCRZDRnxfbVrrmiCEroVdog8U= github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= @@ -203,6 +203,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0 h1:XA01Vk/wv9YikCi1V51yRzIHPMT5of9+cMpZoZvDn/M= github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= @@ -221,6 +223,8 @@ github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/shamaton/msgpack/v3 v3.2.0 h1:1q2Ms+MWmuRju+PuDMSFDB7p7621npeX4zprJN5Zck8= +github.com/shamaton/msgpack/v3 v3.2.0/go.mod h1:sgBYvEiyz8JR1NC3yGRoPVME9xXovpnh3l/plW1nfRo= github.com/shirou/gopsutil/v4 v4.26.9 h1:CaBo/hBFqvlJoLvAQEKdHdf5GD97/MN2ACfyv63+2Ig= github.com/shirou/gopsutil/v4 v4.26.9/go.mod h1:nKH+8wX2zxr/mDbsR+AAy6Qd2z4ZOQ7wfQaEp+yrYmg= github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= @@ -257,6 +261,8 @@ github.com/testcontainers/testcontainers-go/modules/mysql v0.44.0 h1:oJPJPxNE6YQ github.com/testcontainers/testcontainers-go/modules/mysql v0.44.0/go.mod h1:MSOAU6ukCpehJVHQDN1k9JgOZXZuqHD+2pT20M3JkIg= github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 h1:8fdv/9y3JMxjQ+ULAcOG8RtgeNu5t9XF9LolSXDuTwM= github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0/go.mod h1:CFr2LncGYokw+OKjXcr8ARCKG1SaC2UEnGxFBovE86g= +github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= +github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= @@ -265,6 +271,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.74.0 h1:wMS9fnO2QTALozYx5pId2Vi7ZwU/epUkY8i/KPWCHoU= github.com/valyala/fasthttp v1.74.0/go.mod h1:3ARmLamUcw7ElxVtC8PXaGzQ6VEuvnetlkrwIklQBSE= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= diff --git a/pkg/app/app.go b/pkg/app/app.go index dac62fa..6dea084 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -15,8 +15,9 @@ import ( "syscall" "time" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" + "github.com/gofiber/fiber/v3/middleware/static" "github.com/lukaszraczylo/gohoarder/pkg/analytics" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/cache" @@ -554,10 +555,10 @@ func (a *App) setupServer() error { frontendDir := "frontend/dist" if _, err := os.Stat(frontendDir); err == nil { log.Info().Str("dir", frontendDir).Msg("Serving frontend static files") - a.app.Static("/", frontendDir) + a.app.Use("/", static.New(frontendDir)) } else { log.Warn().Msg("Frontend dist directory not found, frontend won't be served") - a.app.Get("/", func(c *fiber.Ctx) error { + a.app.Get("/", func(c fiber.Ctx) error { return c.Type("html").SendString(` GoHoarder @@ -607,7 +608,7 @@ func (a *App) Run() error { Str("addr", addr). Str("cert_file", a.config.Server.TLS.CertFile). Msg("Starting Fiber server with TLS") - if err := a.app.ListenTLS(addr, a.config.Server.TLS.CertFile, a.config.Server.TLS.KeyFile); err != nil { + if err := a.app.Listen(addr, fiber.ListenConfig{CertFile: a.config.Server.TLS.CertFile, CertKeyFile: a.config.Server.TLS.KeyFile}); err != nil { errChan <- err } return @@ -643,6 +644,7 @@ func (a *App) Run() error { // Graceful shutdown return a.Shutdown() } + // Shutdown gracefully shuts down the application func (a *App) Shutdown() error { log.Info().Msg("Starting graceful shutdown") diff --git a/pkg/app/handlers.go b/pkg/app/handlers.go index bda0d99..3918cbb 100644 --- a/pkg/app/handlers.go +++ b/pkg/app/handlers.go @@ -4,7 +4,7 @@ import ( "strings" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/internal/version" "github.com/lukaszraczylo/gohoarder/pkg/metadata" "github.com/lukaszraczylo/gohoarder/pkg/websocket" @@ -12,7 +12,7 @@ import ( ) // handlePackages handles /api/packages endpoint -func (a *App) handlePackages(c *fiber.Ctx) error { +func (a *App) handlePackages(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, DELETE, OPTIONS") @@ -38,7 +38,7 @@ func (a *App) handlePackages(c *fiber.Ctx) error { } // handleListPackages returns list of cached packages -func (a *App) handleListPackages(c *fiber.Ctx) error { +func (a *App) handleListPackages(c fiber.Ctx) error { ctx := c.Context() // Get packages from metadata store @@ -201,7 +201,7 @@ func (a *App) handleListPackages(c *fiber.Ctx) error { } // handleDeletePackage deletes a cached package -func (a *App) handleDeletePackage(c *fiber.Ctx) error { +func (a *App) handleDeletePackage(c fiber.Ctx) error { ctx := c.Context() // Parse path: /api/packages/{registry}/{name}/{version} @@ -331,7 +331,7 @@ func (a *App) handleDeletePackage(c *fiber.Ctx) error { } // handleStats handles /api/stats endpoint -func (a *App) handleStats(c *fiber.Ctx) error { +func (a *App) handleStats(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") @@ -421,7 +421,7 @@ func (a *App) handleStats(c *fiber.Ctx) error { // handleTimeSeriesStats handles /api/stats/timeseries endpoint // Returns time-series download statistics for charts -func (a *App) handleTimeSeriesStats(c *fiber.Ctx) error { +func (a *App) handleTimeSeriesStats(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") @@ -463,7 +463,7 @@ func (a *App) handleTimeSeriesStats(c *fiber.Ctx) error { // handleConfig handles /api/config endpoint // Returns runtime configuration for the frontend -func (a *App) handleConfig(c *fiber.Ctx) error { +func (a *App) handleConfig(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") @@ -497,7 +497,7 @@ func (a *App) handleConfig(c *fiber.Ctx) error { } // handleInfo handles /api/info endpoint -func (a *App) handleInfo(c *fiber.Ctx) error { +func (a *App) handleInfo(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") diff --git a/pkg/app/handlers_admin.go b/pkg/app/handlers_admin.go index e40cdf5..73e1675 100644 --- a/pkg/app/handlers_admin.go +++ b/pkg/app/handlers_admin.go @@ -3,7 +3,7 @@ package app import ( "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/lukaszraczylo/gohoarder/pkg/metadata" @@ -14,7 +14,7 @@ import ( // requireAdmin middleware checks for admin authentication. It accepts the API // key via either the Authorization Bearer header or the X-API-Key header // (shared extractAPIKey), so admin clients are not limited to the bearer form. -func (a *App) requireAdmin(c *fiber.Ctx) error { +func (a *App) requireAdmin(c fiber.Ctx) error { // Get API key from Authorization or X-API-Key header apiKey, ok := extractAPIKey(c) if !ok { @@ -43,7 +43,7 @@ func (a *App) requireAdmin(c *fiber.Ctx) error { } // handleAdminBypasses handles /api/admin/bypasses endpoint -func (a *App) handleAdminBypasses(c *fiber.Ctx) error { +func (a *App) handleAdminBypasses(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS") @@ -74,7 +74,7 @@ func (a *App) handleAdminBypasses(c *fiber.Ctx) error { } // handleListBypasses lists all CVE bypasses -func (a *App) handleListBypasses(c *fiber.Ctx) error { +func (a *App) handleListBypasses(c fiber.Ctx) error { ctx := c.Context() // Parse query parameters @@ -112,11 +112,11 @@ type CreateBypassRequest struct { } // handleCreateBypass creates a new CVE bypass -func (a *App) handleCreateBypass(c *fiber.Ctx) error { +func (a *App) handleCreateBypass(c fiber.Ctx) error { ctx := c.Context() var req CreateBypassRequest - if err := c.BodyParser(&req); err != nil { + if err := c.Bind().Body(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid JSON in request body"}) } @@ -179,7 +179,7 @@ func (a *App) handleCreateBypass(c *fiber.Ctx) error { } // handleGetBypass gets a specific bypass by ID -func (a *App) handleGetBypass(c *fiber.Ctx) error { +func (a *App) handleGetBypass(c fiber.Ctx) error { ctx := c.Context() // Extract ID from parameter @@ -217,7 +217,7 @@ type UpdateBypassRequest struct { } // handleUpdateBypass updates a bypass (activate/deactivate or extend expiration) -func (a *App) handleUpdateBypass(c *fiber.Ctx) error { +func (a *App) handleUpdateBypass(c fiber.Ctx) error { ctx := c.Context() // Extract ID from parameter @@ -228,7 +228,7 @@ func (a *App) handleUpdateBypass(c *fiber.Ctx) error { } var req UpdateBypassRequest - if err := c.BodyParser(&req); err != nil { + if err := c.Bind().Body(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid JSON in request body"}) } @@ -284,7 +284,7 @@ func (a *App) handleUpdateBypass(c *fiber.Ctx) error { } // handleDeleteBypass deletes a bypass -func (a *App) handleDeleteBypass(c *fiber.Ctx) error { +func (a *App) handleDeleteBypass(c fiber.Ctx) error { ctx := c.Context() // Extract ID from parameter diff --git a/pkg/app/handlers_analytics.go b/pkg/app/handlers_analytics.go index dbfbf02..4eb1ee6 100644 --- a/pkg/app/handlers_analytics.go +++ b/pkg/app/handlers_analytics.go @@ -3,12 +3,12 @@ package app import ( "strconv" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/rs/zerolog/log" ) // handleAnalyticsTopPackages returns the most downloaded packages -func (a *App) handleAnalyticsTopPackages(c *fiber.Ctx) error { +func (a *App) handleAnalyticsTopPackages(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -29,7 +29,7 @@ func (a *App) handleAnalyticsTopPackages(c *fiber.Ctx) error { } // handleAnalyticsTrendingPackages returns trending packages -func (a *App) handleAnalyticsTrendingPackages(c *fiber.Ctx) error { +func (a *App) handleAnalyticsTrendingPackages(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -50,7 +50,7 @@ func (a *App) handleAnalyticsTrendingPackages(c *fiber.Ctx) error { } // handleAnalyticsTrends returns download trends over time -func (a *App) handleAnalyticsTrends(c *fiber.Ctx) error { +func (a *App) handleAnalyticsTrends(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -62,7 +62,7 @@ func (a *App) handleAnalyticsTrends(c *fiber.Ctx) error { } // handleAnalyticsTotalStats returns overall statistics -func (a *App) handleAnalyticsTotalStats(c *fiber.Ctx) error { +func (a *App) handleAnalyticsTotalStats(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -72,7 +72,7 @@ func (a *App) handleAnalyticsTotalStats(c *fiber.Ctx) error { } // handleAnalyticsRegistryStats returns per-registry statistics -func (a *App) handleAnalyticsRegistryStats(c *fiber.Ctx) error { +func (a *App) handleAnalyticsRegistryStats(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -89,7 +89,7 @@ func (a *App) handleAnalyticsRegistryStats(c *fiber.Ctx) error { } // handleAnalyticsPackageStats returns statistics for a specific package -func (a *App) handleAnalyticsPackageStats(c *fiber.Ctx) error { +func (a *App) handleAnalyticsPackageStats(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") @@ -115,7 +115,7 @@ func (a *App) handleAnalyticsPackageStats(c *fiber.Ctx) error { } // handleAnalyticsSearch searches for packages matching a query -func (a *App) handleAnalyticsSearch(c *fiber.Ctx) error { +func (a *App) handleAnalyticsSearch(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") diff --git a/pkg/app/handlers_analytics_test.go b/pkg/app/handlers_analytics_test.go index ebf63b0..58c27c9 100644 --- a/pkg/app/handlers_analytics_test.go +++ b/pkg/app/handlers_analytics_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/analytics" "github.com/stretchr/testify/suite" ) diff --git a/pkg/app/handlers_auth.go b/pkg/app/handlers_auth.go index 38eb61f..046aea2 100644 --- a/pkg/app/handlers_auth.go +++ b/pkg/app/handlers_auth.go @@ -3,7 +3,7 @@ package app import ( "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/rs/zerolog/log" ) @@ -16,11 +16,11 @@ type GenerateAPIKeyRequest struct { } // handleGenerateAPIKey generates a new API key -func (a *App) handleGenerateAPIKey(c *fiber.Ctx) error { +func (a *App) handleGenerateAPIKey(c fiber.Ctx) error { c.Set("Content-Type", "application/json") var req GenerateAPIKeyRequest - if err := c.BodyParser(&req); err != nil { + if err := c.Bind().Body(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "invalid JSON in request body", }) @@ -82,7 +82,7 @@ func (a *App) handleGenerateAPIKey(c *fiber.Ctx) error { } // handleListAPIKeys lists all API keys -func (a *App) handleListAPIKeys(c *fiber.Ctx) error { +func (a *App) handleListAPIKeys(c fiber.Ctx) error { c.Set("Content-Type", "application/json") keys := a.authManager.ListAPIKeys() @@ -108,7 +108,7 @@ func (a *App) handleListAPIKeys(c *fiber.Ctx) error { } // handleRevokeAPIKey revokes an API key -func (a *App) handleRevokeAPIKey(c *fiber.Ctx) error { +func (a *App) handleRevokeAPIKey(c fiber.Ctx) error { c.Set("Content-Type", "application/json") keyID := c.Params("key_id") diff --git a/pkg/app/handlers_auth_test.go b/pkg/app/handlers_auth_test.go index 28ce01c..24c8dd6 100644 --- a/pkg/app/handlers_auth_test.go +++ b/pkg/app/handlers_auth_test.go @@ -5,8 +5,9 @@ import ( "encoding/json" "net/http/httptest" "testing" + "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/stretchr/testify/suite" ) @@ -114,7 +115,7 @@ func (s *AuthHandlersTestSuite) TestHandleGenerateAPIKey() { req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes)) req.Header.Set("Content-Type", "application/json") - resp, err := s.app.Test(req, 5000) // 5 second timeout for CI environments + resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(tt.expectedStatus, resp.StatusCode) @@ -144,7 +145,7 @@ func (s *AuthHandlersTestSuite) TestHandleListAPIKeys() { _, _, _ = s.authManager.GenerateAPIKey("test-key-3", auth.RoleAdmin, nil) req := httptest.NewRequest("GET", "/api/admin/keys", nil) - resp, err := s.app.Test(req, 5000) // 5 second timeout for CI environments + resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(200, resp.StatusCode) @@ -189,7 +190,7 @@ func (s *AuthHandlersTestSuite) TestHandleRevokeAPIKey() { for _, tt := range tests { s.Run(tt.name, func() { req := httptest.NewRequest("DELETE", "/api/admin/keys/"+tt.keyID, nil) - resp, err := s.app.Test(req, 5000) // 5 second timeout for CI environments + resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(tt.expectedStatus, resp.StatusCode) @@ -207,7 +208,7 @@ func (s *AuthHandlersTestSuite) TestHandleGenerateAPIKeyInvalidJSON() { req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader([]byte("invalid json"))) req.Header.Set("Content-Type", "application/json") - resp, err := s.app.Test(req, 5000) // 5 second timeout for CI environments + resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(400, resp.StatusCode) } @@ -221,7 +222,7 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() { req1 := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes)) req1.Header.Set("Content-Type", "application/json") - resp1, err := s.app.Test(req1, 5000) // 5 second timeout for CI environments + resp1, err := s.app.Test(req1, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(201, resp1.StatusCode) @@ -235,7 +236,7 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() { // List keys - should include our new key req2 := httptest.NewRequest("GET", "/api/admin/keys", nil) - resp2, err := s.app.Test(req2, 5000) // 5 second timeout for CI environments + resp2, err := s.app.Test(req2, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(200, resp2.StatusCode) @@ -257,13 +258,13 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() { // Revoke the key req3 := httptest.NewRequest("DELETE", "/api/admin/keys/"+keyID, nil) - resp3, err := s.app.Test(req3, 5000) // 5 second timeout for CI environments + resp3, err := s.app.Test(req3, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(200, resp3.StatusCode) // List keys again - should not include the revoked key req4 := httptest.NewRequest("GET", "/api/admin/keys", nil) - resp4, err := s.app.Test(req4, 5000) // 5 second timeout for CI environments + resp4, err := s.app.Test(req4, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments s.Require().NoError(err) s.Equal(200, resp4.StatusCode) diff --git a/pkg/app/handlers_prewarming.go b/pkg/app/handlers_prewarming.go index c51638f..804ab66 100644 --- a/pkg/app/handlers_prewarming.go +++ b/pkg/app/handlers_prewarming.go @@ -1,12 +1,12 @@ package app import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/rs/zerolog/log" ) // handlePrewarmingStatus returns the status of the pre-warming worker -func (a *App) handlePrewarmingStatus(c *fiber.Ctx) error { +func (a *App) handlePrewarmingStatus(c fiber.Ctx) error { c.Set("Content-Type", "application/json") status := a.prewarmWorker.GetStatus() @@ -15,7 +15,7 @@ func (a *App) handlePrewarmingStatus(c *fiber.Ctx) error { } // handlePrewarmingTrigger manually triggers a pre-warming cycle -func (a *App) handlePrewarmingTrigger(c *fiber.Ctx) error { +func (a *App) handlePrewarmingTrigger(c fiber.Ctx) error { c.Set("Content-Type", "application/json") ctx := c.Context() @@ -36,11 +36,11 @@ type PrewarmPackageRequest struct { } // handlePrewarmingPackage pre-warms a specific package -func (a *App) handlePrewarmingPackage(c *fiber.Ctx) error { +func (a *App) handlePrewarmingPackage(c fiber.Ctx) error { c.Set("Content-Type", "application/json") var req PrewarmPackageRequest - if err := c.BodyParser(&req); err != nil { + if err := c.Bind().Body(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "invalid JSON in request body", }) diff --git a/pkg/app/handlers_prewarming_test.go b/pkg/app/handlers_prewarming_test.go index 941b2bc..57a7e1e 100644 --- a/pkg/app/handlers_prewarming_test.go +++ b/pkg/app/handlers_prewarming_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/prewarming" "github.com/stretchr/testify/suite" ) diff --git a/pkg/app/handlers_vulnerabilities.go b/pkg/app/handlers_vulnerabilities.go index f1c766e..107d905 100644 --- a/pkg/app/handlers_vulnerabilities.go +++ b/pkg/app/handlers_vulnerabilities.go @@ -3,13 +3,13 @@ package app import ( "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/metadata" "github.com/rs/zerolog/log" ) // handleVulnerabilities handles /api/packages/{registry}/{name}/{version}/vulnerabilities endpoint -func (a *App) handleVulnerabilities(c *fiber.Ctx) error { +func (a *App) handleVulnerabilities(c fiber.Ctx) error { c.Set("Content-Type", "application/json") c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") diff --git a/pkg/app/middleware.go b/pkg/app/middleware.go index 79d11f8..de3ad8d 100644 --- a/pkg/app/middleware.go +++ b/pkg/app/middleware.go @@ -3,7 +3,7 @@ package app import ( "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/rs/zerolog/log" @@ -20,7 +20,7 @@ const ( // extractAPIKey pulls the raw API key from either the Authorization bearer // header or the X-API-Key header. Returns ("", false) when neither is set or // the Authorization header has the wrong shape. -func extractAPIKey(c *fiber.Ctx) (string, bool) { +func extractAPIKey(c fiber.Ctx) (string, bool) { if h := strings.TrimSpace(c.Get("Authorization")); h != "" { // Expect: "Bearer ". Be tolerant of casing on the scheme. parts := strings.SplitN(h, " ", 2) @@ -40,7 +40,7 @@ func extractAPIKey(c *fiber.Ctx) (string, bool) { // writeAuthError sends a structured error response matching the project's // errors envelope (errors.Error JSON shape). Uses the HTTPStatusCode map so // callers don't need to remember which status maps to which code. -func writeAuthError(c *fiber.Ctx, code, message string) error { +func writeAuthError(c fiber.Ctx, code, message string) error { status, ok := errors.HTTPStatusCode[code] if !ok { status = fiber.StatusInternalServerError @@ -51,7 +51,7 @@ func writeAuthError(c *fiber.Ctx, code, message string) error { // requestIDFromCtx returns the request ID set by an upstream middleware or // the X-Request-ID header. Empty string when neither is available — callers // should treat empty as "no correlation id". -func requestIDFromCtx(c *fiber.Ctx) string { +func requestIDFromCtx(c fiber.Ctx) string { if v := c.Locals("request_id"); v != nil { if s, ok := v.(string); ok && s != "" { return s @@ -65,7 +65,7 @@ func requestIDFromCtx(c *fiber.Ctx) string { // resolved key plus a boolean indicating whether a key was present at all // (false when no header). The error is non-nil only when validation failed // (i.e. a key was present but invalid/expired). -func validateAndAttach(c *fiber.Ctx, authMgr *auth.Manager) (key *auth.APIKey, present bool, err error) { +func validateAndAttach(c fiber.Ctx, authMgr *auth.Manager) (key *auth.APIKey, present bool, err error) { rawKey, ok := extractAPIKey(c) if !ok { return nil, false, nil @@ -83,7 +83,7 @@ func validateAndAttach(c *fiber.Ctx, authMgr *auth.Manager) (key *auth.APIKey, p // API key. On success the resolved *auth.APIKey is stored in c.Locals under // LocalAuthKey. func RequireAuth(authMgr *auth.Manager) fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { key, present, err := validateAndAttach(c, authMgr) if !present { log.Debug(). @@ -115,7 +115,7 @@ func RequireAuth(authMgr *auth.Manager) fiber.Handler { // requires the resolved key's role to be present in the supplied list (any-of // semantics). An empty roles list behaves like RequireAuth. func RequireRole(authMgr *auth.Manager, roles ...string) fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { key, present, err := validateAndAttach(c, authMgr) if !present { return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer or X-API-Key header") @@ -146,7 +146,7 @@ func RequireRole(authMgr *auth.Manager, roles ...string) fiber.Handler { // then requires the resolved key to hold at least one of the supplied // permissions. An empty perms list behaves like RequireAuth. func RequirePermission(authMgr *auth.Manager, perms ...string) fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { key, present, err := validateAndAttach(c, authMgr) if !present { return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer or X-API-Key header") @@ -177,7 +177,7 @@ func RequirePermission(authMgr *auth.Manager, perms ...string) fiber.Handler { // changes when the caller is authenticated (e.g. per-key rate limits) but // which still serve anonymous traffic. Locals are populated on success. func OptionalAuth(authMgr *auth.Manager) fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { _, present, err := validateAndAttach(c, authMgr) if present && err != nil { // Key was provided but invalid — log for observability, continue diff --git a/pkg/app/middleware_test.go b/pkg/app/middleware_test.go index 2ebec38..1f03794 100644 --- a/pkg/app/middleware_test.go +++ b/pkg/app/middleware_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/stretchr/testify/require" @@ -23,9 +23,9 @@ func mwTestSetup(t *testing.T, role auth.Role, mw fiber.Handler) (*fiber.App, *a apiKey, raw, err := mgr.GenerateAPIKey("test-"+string(role), role, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", mw) - app.Get("/protected", func(c *fiber.Ctx) error { + app.Get("/protected", func(c fiber.Ctx) error { got, _ := c.Locals(LocalAuthKey).(*auth.APIKey) gotRole, _ := c.Locals(LocalAuthRole).(string) body := fiber.Map{"ok": true, "have_key": got != nil} @@ -48,7 +48,7 @@ func decodeErrorBody(t *testing.T, body io.Reader) errors.Error { func TestRequireAuth_NoHeader_Returns401(t *testing.T) { app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(auth.New())) req := httptest.NewRequest("GET", "/protected", nil) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) body := decodeErrorBody(t, resp.Body) @@ -63,7 +63,7 @@ func TestRequireAuth_BadKey_Returns401(t *testing.T) { app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(mgr)) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer not-a-real-key") - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) body := decodeErrorBody(t, resp.Body) @@ -84,14 +84,14 @@ func TestRequireAuth_BadHeaderShape_Returns401(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { mgr := auth.New() - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) if tc.value != "" { req.Header.Set(tc.header, tc.value) } - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) }) @@ -103,9 +103,9 @@ func TestRequireAuth_ValidBearer_Returns200_AndPopulatesLocals(t *testing.T) { apiKey, raw, err := mgr.GenerateAPIKey("good", auth.RoleReadWrite, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { + app.Get("/protected", func(c fiber.Ctx) error { k, _ := c.Locals(LocalAuthKey).(*auth.APIKey) require.NotNil(t, k) require.Equal(t, apiKey.ID, k.ID) @@ -115,7 +115,7 @@ func TestRequireAuth_ValidBearer_Returns200_AndPopulatesLocals(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) @@ -129,13 +129,13 @@ func TestRequireAuth_ValidXAPIKey_Returns200(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("good", auth.RoleReadOnly, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("X-API-Key", raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) } @@ -146,13 +146,13 @@ func TestRequireAuth_ExpiredKey_Returns401(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("expired", auth.RoleReadOnly, &d) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) } @@ -162,13 +162,13 @@ func TestRequireRole_Match_Returns200(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("admin", auth.RoleAdmin, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireRole(mgr, string(auth.RoleAdmin), string(auth.RoleReadWrite))) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) } @@ -178,13 +178,13 @@ func TestRequireRole_Mismatch_Returns403(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("ro", auth.RoleReadOnly, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireRole(mgr, string(auth.RoleAdmin))) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusForbidden, resp.StatusCode) body := decodeErrorBody(t, resp.Body) @@ -193,12 +193,12 @@ func TestRequireRole_Mismatch_Returns403(t *testing.T) { func TestRequireRole_NoHeader_Returns401(t *testing.T) { mgr := auth.New() - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequireRole(mgr, string(auth.RoleAdmin))) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) } @@ -208,13 +208,13 @@ func TestRequirePermission_Match_Returns200(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("rw", auth.RoleReadWrite, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequirePermission(mgr, string(auth.PermissionWritePackage))) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) } @@ -224,43 +224,43 @@ func TestRequirePermission_Mismatch_Returns403(t *testing.T) { _, raw, err := mgr.GenerateAPIKey("ro", auth.RoleReadOnly, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", RequirePermission(mgr, string(auth.PermissionDeletePackage))) - app.Get("/protected", func(c *fiber.Ctx) error { return c.SendStatus(200) }) + app.Get("/protected", func(c fiber.Ctx) error { return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, fiber.StatusForbidden, resp.StatusCode) } func TestOptionalAuth_NoHeader_Continues(t *testing.T) { mgr := auth.New() - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", OptionalAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { + app.Get("/protected", func(c fiber.Ctx) error { require.Nil(t, c.Locals(LocalAuthKey)) return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) } func TestOptionalAuth_BadKey_ContinuesAnonymously(t *testing.T) { mgr := auth.New() - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", OptionalAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { + app.Get("/protected", func(c fiber.Ctx) error { // Bad key should NOT populate locals. require.Nil(t, c.Locals(LocalAuthKey)) return c.SendStatus(200) }) req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer not-real") - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) } @@ -270,9 +270,9 @@ func TestOptionalAuth_GoodKey_PopulatesLocals(t *testing.T) { apiKey, raw, err := mgr.GenerateAPIKey("good", auth.RoleAdmin, nil) require.NoError(t, err) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) app.Use("/protected", OptionalAuth(mgr)) - app.Get("/protected", func(c *fiber.Ctx) error { + app.Get("/protected", func(c fiber.Ctx) error { k, _ := c.Locals(LocalAuthKey).(*auth.APIKey) require.NotNil(t, k) require.Equal(t, apiKey.ID, k.ID) @@ -281,7 +281,7 @@ func TestOptionalAuth_GoodKey_PopulatesLocals(t *testing.T) { req := httptest.NewRequest("GET", "/protected", nil) req.Header.Set("Authorization", "Bearer "+raw) - resp, err := app.Test(req, 5000) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) }