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)
}