chore(deps): migrate fiber from v2 to v3

The automated update changed only go.mod, so the build failed on the old v2 imports. This commit moves the code to the v3 API.

The commit replaces BodyParser with Bind().Body, Static with the static middleware, and ListenTLS with ListenConfig. It also updates the tests for TestConfig.

Fixes #179
This commit is contained in:
2026-10-04 00:50:37 +01:00
parent 7d75dac29c
commit 02415f5a92
14 changed files with 127 additions and 116 deletions
+5 -5
View File
@@ -11,7 +11,6 @@ require (
github.com/go-gormigrate/gormigrate/v2 v2.1.7 github.com/go-gormigrate/gormigrate/v2 v2.1.7
github.com/go-sql-driver/mysql v1.10.1 github.com/go-sql-driver/mysql v1.10.1
github.com/goccy/go-json v0.11.2 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/gofiber/fiber/v3 v3.5.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/lib/pq v1.12.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/aws-sdk-go-v2/service/sts v1.51.1 // indirect
github.com/aws/smithy-go v1.28.2 // indirect github.com/aws/smithy-go v1.28.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v7 v7.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v7 v7.0.1 // indirect
github.com/cespare/xxhash/v2 v2.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/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect
github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.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-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.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/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-uuid v1.0.4 // indirect github.com/hashicorp/go-uuid v1.0.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -93,7 +92,6 @@ require (
github.com/magiconair/properties v1.18.12 // indirect github.com/magiconair/properties v1.18.12 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.24 // 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/mattn/go-sqlite3 v1.14.52 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.3.3 // 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/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // 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/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0 // indirect
github.com/prometheus/client_model v0.6.3 // indirect github.com/prometheus/client_model v0.6.3 // indirect
github.com/prometheus/common v0.72.0 // indirect github.com/prometheus/common v0.72.0 // indirect
@@ -121,6 +120,7 @@ require (
github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/pflag v1.0.10 // indirect
github.com/stretchr/objx v0.5.3 // indirect github.com/stretchr/objx v0.5.3 // indirect
github.com/subosito/gotenv v1.6.0 // 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/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect github.com/tklauser/numcpus v0.12.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
+15 -7
View File
@@ -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/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 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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 h1:xiTow1n7oNJPzn9QH+U2sT65mO2F12zNNlY4ZzOew70=
github.com/cloudsoda/go-smb2 v0.0.0-20260918041005-0c5d69b69701/go.mod h1:1pQXB0vAlzRlqcY7LYKOOZMw0wKfJPFxTLsJRF2Gswo= 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= 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/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 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= 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 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64=
github.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
github.com/go-gormigrate/gormigrate/v2 v2.1.7 h1:PdT4jVPbRb4R+0Ey2R0yJOdctVf4Whiq1Qi4necaZdg= 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/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 h1:jdZv93Tt4ioR8yW1CoNsvSxrcZlCXAUU1aZXN7gpXUA=
github.com/goccy/go-json v0.11.2/go.mod h1:3NdmfEkZlB7YI5UFw/qdFKq8XN1aiWR0YyRPWZNQltY= 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/v3 v3.5.0 h1:dk7TOUH6DXJGtOLsN2XEG+0ZML7cznzHILTVozbNEK8=
github.com/gofiber/fiber/v2 v2.52.15/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/fiber/v3 v3.5.0/go.mod h1:GOVDTW+gjJvfe0iJyVujbQ1Lnx+JUjFySJRI/9/xX/w= 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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 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-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 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= 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 h1:wVbm2Qnf4OXkqhBTSPuCRZDRnxfbVrrmiCEroVdog8U=
github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= 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/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 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 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/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 h1:XA01Vk/wv9YikCi1V51yRzIHPMT5of9+cMpZoZvDn/M=
github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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/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 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= 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 h1:CaBo/hBFqvlJoLvAQEKdHdf5GD97/MN2ACfyv63+2Ig=
github.com/shirou/gopsutil/v4 v4.26.9/go.mod h1:nKH+8wX2zxr/mDbsR+AAy6Qd2z4ZOQ7wfQaEp+yrYmg= 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= 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/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 h1:8fdv/9y3JMxjQ+ULAcOG8RtgeNu5t9XF9LolSXDuTwM=
github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0/go.mod h1:CFr2LncGYokw+OKjXcr8ARCKG1SaC2UEnGxFBovE86g= 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 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= 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/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 h1:wMS9fnO2QTALozYx5pId2Vi7ZwU/epUkY8i/KPWCHoU=
github.com/valyala/fasthttp v1.74.0/go.mod h1:3ARmLamUcw7ElxVtC8PXaGzQ6VEuvnetlkrwIklQBSE= 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/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 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+7 -5
View File
@@ -15,8 +15,9 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v2/middleware/adaptor" "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/analytics"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/lukaszraczylo/gohoarder/pkg/cache" "github.com/lukaszraczylo/gohoarder/pkg/cache"
@@ -554,10 +555,10 @@ func (a *App) setupServer() error {
frontendDir := "frontend/dist" frontendDir := "frontend/dist"
if _, err := os.Stat(frontendDir); err == nil { if _, err := os.Stat(frontendDir); err == nil {
log.Info().Str("dir", frontendDir).Msg("Serving frontend static files") log.Info().Str("dir", frontendDir).Msg("Serving frontend static files")
a.app.Static("/", frontendDir) a.app.Use("/", static.New(frontendDir))
} else { } else {
log.Warn().Msg("Frontend dist directory not found, frontend won't be served") 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(` return c.Type("html").SendString(`
<html> <html>
<head><title>GoHoarder</title></head> <head><title>GoHoarder</title></head>
@@ -607,7 +608,7 @@ func (a *App) Run() error {
Str("addr", addr). Str("addr", addr).
Str("cert_file", a.config.Server.TLS.CertFile). Str("cert_file", a.config.Server.TLS.CertFile).
Msg("Starting Fiber server with TLS") 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 errChan <- err
} }
return return
@@ -643,6 +644,7 @@ func (a *App) Run() error {
// Graceful shutdown // Graceful shutdown
return a.Shutdown() return a.Shutdown()
} }
// Shutdown gracefully shuts down the application // Shutdown gracefully shuts down the application
func (a *App) Shutdown() error { func (a *App) Shutdown() error {
log.Info().Msg("Starting graceful shutdown") log.Info().Msg("Starting graceful shutdown")
+8 -8
View File
@@ -4,7 +4,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/internal/version" "github.com/lukaszraczylo/gohoarder/internal/version"
"github.com/lukaszraczylo/gohoarder/pkg/metadata" "github.com/lukaszraczylo/gohoarder/pkg/metadata"
"github.com/lukaszraczylo/gohoarder/pkg/websocket" "github.com/lukaszraczylo/gohoarder/pkg/websocket"
@@ -12,7 +12,7 @@ import (
) )
// handlePackages handles /api/packages endpoint // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, DELETE, OPTIONS") 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 // 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() ctx := c.Context()
// Get packages from metadata store // Get packages from metadata store
@@ -201,7 +201,7 @@ func (a *App) handleListPackages(c *fiber.Ctx) error {
} }
// handleDeletePackage deletes a cached package // handleDeletePackage deletes a cached package
func (a *App) handleDeletePackage(c *fiber.Ctx) error { func (a *App) handleDeletePackage(c fiber.Ctx) error {
ctx := c.Context() ctx := c.Context()
// Parse path: /api/packages/{registry}/{name}/{version} // Parse path: /api/packages/{registry}/{name}/{version}
@@ -331,7 +331,7 @@ func (a *App) handleDeletePackage(c *fiber.Ctx) error {
} }
// handleStats handles /api/stats endpoint // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") 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 // handleTimeSeriesStats handles /api/stats/timeseries endpoint
// Returns time-series download statistics for charts // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") 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 // handleConfig handles /api/config endpoint
// Returns runtime configuration for the frontend // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") 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 // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS")
+10 -10
View File
@@ -3,7 +3,7 @@ package app
import ( import (
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/lukaszraczylo/gohoarder/pkg/errors"
"github.com/lukaszraczylo/gohoarder/pkg/metadata" "github.com/lukaszraczylo/gohoarder/pkg/metadata"
@@ -14,7 +14,7 @@ import (
// requireAdmin middleware checks for admin authentication. It accepts the API // requireAdmin middleware checks for admin authentication. It accepts the API
// key via either the Authorization Bearer header or the X-API-Key header // 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. // (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 // Get API key from Authorization or X-API-Key header
apiKey, ok := extractAPIKey(c) apiKey, ok := extractAPIKey(c)
if !ok { if !ok {
@@ -43,7 +43,7 @@ func (a *App) requireAdmin(c *fiber.Ctx) error {
} }
// handleAdminBypasses handles /api/admin/bypasses endpoint // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS") 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 // handleListBypasses lists all CVE bypasses
func (a *App) handleListBypasses(c *fiber.Ctx) error { func (a *App) handleListBypasses(c fiber.Ctx) error {
ctx := c.Context() ctx := c.Context()
// Parse query parameters // Parse query parameters
@@ -112,11 +112,11 @@ type CreateBypassRequest struct {
} }
// handleCreateBypass creates a new CVE bypass // 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() ctx := c.Context()
var req CreateBypassRequest 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"}) 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 // 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() ctx := c.Context()
// Extract ID from parameter // Extract ID from parameter
@@ -217,7 +217,7 @@ type UpdateBypassRequest struct {
} }
// handleUpdateBypass updates a bypass (activate/deactivate or extend expiration) // 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() ctx := c.Context()
// Extract ID from parameter // Extract ID from parameter
@@ -228,7 +228,7 @@ func (a *App) handleUpdateBypass(c *fiber.Ctx) error {
} }
var req UpdateBypassRequest 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"}) 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 // handleDeleteBypass deletes a bypass
func (a *App) handleDeleteBypass(c *fiber.Ctx) error { func (a *App) handleDeleteBypass(c fiber.Ctx) error {
ctx := c.Context() ctx := c.Context()
// Extract ID from parameter // Extract ID from parameter
+8 -8
View File
@@ -3,12 +3,12 @@ package app
import ( import (
"strconv" "strconv"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// handleAnalyticsTopPackages returns the most downloaded packages // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
@@ -29,7 +29,7 @@ func (a *App) handleAnalyticsTopPackages(c *fiber.Ctx) error {
} }
// handleAnalyticsTrendingPackages returns trending packages // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
@@ -50,7 +50,7 @@ func (a *App) handleAnalyticsTrendingPackages(c *fiber.Ctx) error {
} }
// handleAnalyticsTrends returns download trends over time // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
@@ -62,7 +62,7 @@ func (a *App) handleAnalyticsTrends(c *fiber.Ctx) error {
} }
// handleAnalyticsTotalStats returns overall statistics // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
@@ -72,7 +72,7 @@ func (a *App) handleAnalyticsTotalStats(c *fiber.Ctx) error {
} }
// handleAnalyticsRegistryStats returns per-registry statistics // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") 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 // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") 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 // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/analytics" "github.com/lukaszraczylo/gohoarder/pkg/analytics"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+5 -5
View File
@@ -3,7 +3,7 @@ package app
import ( import (
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@@ -16,11 +16,11 @@ type GenerateAPIKeyRequest struct {
} }
// handleGenerateAPIKey generates a new API key // 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") c.Set("Content-Type", "application/json")
var req GenerateAPIKeyRequest 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{ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid JSON in request body", "error": "invalid JSON in request body",
}) })
@@ -82,7 +82,7 @@ func (a *App) handleGenerateAPIKey(c *fiber.Ctx) error {
} }
// handleListAPIKeys lists all API keys // 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") c.Set("Content-Type", "application/json")
keys := a.authManager.ListAPIKeys() keys := a.authManager.ListAPIKeys()
@@ -108,7 +108,7 @@ func (a *App) handleListAPIKeys(c *fiber.Ctx) error {
} }
// handleRevokeAPIKey revokes an API key // 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") c.Set("Content-Type", "application/json")
keyID := c.Params("key_id") keyID := c.Params("key_id")
+10 -9
View File
@@ -5,8 +5,9 @@ import (
"encoding/json" "encoding/json"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -114,7 +115,7 @@ func (s *AuthHandlersTestSuite) TestHandleGenerateAPIKey() {
req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes)) req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/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.Require().NoError(err)
s.Equal(tt.expectedStatus, resp.StatusCode) s.Equal(tt.expectedStatus, resp.StatusCode)
@@ -144,7 +145,7 @@ func (s *AuthHandlersTestSuite) TestHandleListAPIKeys() {
_, _, _ = s.authManager.GenerateAPIKey("test-key-3", auth.RoleAdmin, nil) _, _, _ = s.authManager.GenerateAPIKey("test-key-3", auth.RoleAdmin, nil)
req := httptest.NewRequest("GET", "/api/admin/keys", 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.Require().NoError(err)
s.Equal(200, resp.StatusCode) s.Equal(200, resp.StatusCode)
@@ -189,7 +190,7 @@ func (s *AuthHandlersTestSuite) TestHandleRevokeAPIKey() {
for _, tt := range tests { for _, tt := range tests {
s.Run(tt.name, func() { s.Run(tt.name, func() {
req := httptest.NewRequest("DELETE", "/api/admin/keys/"+tt.keyID, nil) 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.Require().NoError(err)
s.Equal(tt.expectedStatus, resp.StatusCode) 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 := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader([]byte("invalid json")))
req.Header.Set("Content-Type", "application/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.Require().NoError(err)
s.Equal(400, resp.StatusCode) s.Equal(400, resp.StatusCode)
} }
@@ -221,7 +222,7 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() {
req1 := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes)) req1 := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes))
req1.Header.Set("Content-Type", "application/json") 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.Require().NoError(err)
s.Equal(201, resp1.StatusCode) s.Equal(201, resp1.StatusCode)
@@ -235,7 +236,7 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() {
// List keys - should include our new key // List keys - should include our new key
req2 := httptest.NewRequest("GET", "/api/admin/keys", nil) 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.Require().NoError(err)
s.Equal(200, resp2.StatusCode) s.Equal(200, resp2.StatusCode)
@@ -257,13 +258,13 @@ func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() {
// Revoke the key // Revoke the key
req3 := httptest.NewRequest("DELETE", "/api/admin/keys/"+keyID, nil) 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.Require().NoError(err)
s.Equal(200, resp3.StatusCode) s.Equal(200, resp3.StatusCode)
// List keys again - should not include the revoked key // List keys again - should not include the revoked key
req4 := httptest.NewRequest("GET", "/api/admin/keys", nil) 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.Require().NoError(err)
s.Equal(200, resp4.StatusCode) s.Equal(200, resp4.StatusCode)
+5 -5
View File
@@ -1,12 +1,12 @@
package app package app
import ( import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// handlePrewarmingStatus returns the status of the pre-warming worker // 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") c.Set("Content-Type", "application/json")
status := a.prewarmWorker.GetStatus() status := a.prewarmWorker.GetStatus()
@@ -15,7 +15,7 @@ func (a *App) handlePrewarmingStatus(c *fiber.Ctx) error {
} }
// handlePrewarmingTrigger manually triggers a pre-warming cycle // 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") c.Set("Content-Type", "application/json")
ctx := c.Context() ctx := c.Context()
@@ -36,11 +36,11 @@ type PrewarmPackageRequest struct {
} }
// handlePrewarmingPackage pre-warms a specific package // 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") c.Set("Content-Type", "application/json")
var req PrewarmPackageRequest 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{ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid JSON in request body", "error": "invalid JSON in request body",
}) })
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/prewarming" "github.com/lukaszraczylo/gohoarder/pkg/prewarming"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+2 -2
View File
@@ -3,13 +3,13 @@ package app
import ( import (
"strings" "strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/metadata" "github.com/lukaszraczylo/gohoarder/pkg/metadata"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// handleVulnerabilities handles /api/packages/{registry}/{name}/{version}/vulnerabilities endpoint // 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("Content-Type", "application/json")
c.Set("Access-Control-Allow-Origin", "*") c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, OPTIONS") c.Set("Access-Control-Allow-Methods", "GET, OPTIONS")
+9 -9
View File
@@ -3,7 +3,7 @@ package app
import ( import (
"strings" "strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/lukaszraczylo/gohoarder/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@@ -20,7 +20,7 @@ const (
// extractAPIKey pulls the raw API key from either the Authorization bearer // 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 // header or the X-API-Key header. Returns ("", false) when neither is set or
// the Authorization header has the wrong shape. // 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 != "" { if h := strings.TrimSpace(c.Get("Authorization")); h != "" {
// Expect: "Bearer <token>". Be tolerant of casing on the scheme. // Expect: "Bearer <token>". Be tolerant of casing on the scheme.
parts := strings.SplitN(h, " ", 2) 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 // writeAuthError sends a structured error response matching the project's
// errors envelope (errors.Error JSON shape). Uses the HTTPStatusCode map so // errors envelope (errors.Error JSON shape). Uses the HTTPStatusCode map so
// callers don't need to remember which status maps to which code. // 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] status, ok := errors.HTTPStatusCode[code]
if !ok { if !ok {
status = fiber.StatusInternalServerError 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 // requestIDFromCtx returns the request ID set by an upstream middleware or
// the X-Request-ID header. Empty string when neither is available — callers // the X-Request-ID header. Empty string when neither is available — callers
// should treat empty as "no correlation id". // 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 v := c.Locals("request_id"); v != nil {
if s, ok := v.(string); ok && s != "" { if s, ok := v.(string); ok && s != "" {
return 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 // 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 // (false when no header). The error is non-nil only when validation failed
// (i.e. a key was present but invalid/expired). // (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) rawKey, ok := extractAPIKey(c)
if !ok { if !ok {
return nil, false, nil 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 // API key. On success the resolved *auth.APIKey is stored in c.Locals under
// LocalAuthKey. // LocalAuthKey.
func RequireAuth(authMgr *auth.Manager) fiber.Handler { 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) key, present, err := validateAndAttach(c, authMgr)
if !present { if !present {
log.Debug(). 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 // requires the resolved key's role to be present in the supplied list (any-of
// semantics). An empty roles list behaves like RequireAuth. // semantics). An empty roles list behaves like RequireAuth.
func RequireRole(authMgr *auth.Manager, roles ...string) fiber.Handler { 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) key, present, err := validateAndAttach(c, authMgr)
if !present { if !present {
return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer <key> or X-API-Key header") return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer <key> 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 // then requires the resolved key to hold at least one of the supplied
// permissions. An empty perms list behaves like RequireAuth. // permissions. An empty perms list behaves like RequireAuth.
func RequirePermission(authMgr *auth.Manager, perms ...string) fiber.Handler { 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) key, present, err := validateAndAttach(c, authMgr)
if !present { if !present {
return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer <key> or X-API-Key header") return writeAuthError(c, errors.ErrCodeUnauthorized, "missing API key; provide Authorization: Bearer <key> 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 // changes when the caller is authenticated (e.g. per-key rate limits) but
// which still serve anonymous traffic. Locals are populated on success. // which still serve anonymous traffic. Locals are populated on success.
func OptionalAuth(authMgr *auth.Manager) fiber.Handler { func OptionalAuth(authMgr *auth.Manager) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c fiber.Ctx) error {
_, present, err := validateAndAttach(c, authMgr) _, present, err := validateAndAttach(c, authMgr)
if present && err != nil { if present && err != nil {
// Key was provided but invalid — log for observability, continue // Key was provided but invalid — log for observability, continue
+41 -41
View File
@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v3"
"github.com/lukaszraczylo/gohoarder/pkg/auth" "github.com/lukaszraczylo/gohoarder/pkg/auth"
"github.com/lukaszraczylo/gohoarder/pkg/errors" "github.com/lukaszraczylo/gohoarder/pkg/errors"
"github.com/stretchr/testify/require" "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) apiKey, raw, err := mgr.GenerateAPIKey("test-"+string(role), role, nil)
require.NoError(t, err) require.NoError(t, err)
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", mw) 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) got, _ := c.Locals(LocalAuthKey).(*auth.APIKey)
gotRole, _ := c.Locals(LocalAuthRole).(string) gotRole, _ := c.Locals(LocalAuthRole).(string)
body := fiber.Map{"ok": true, "have_key": got != nil} 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) { func TestRequireAuth_NoHeader_Returns401(t *testing.T) {
app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(auth.New())) app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(auth.New()))
req := httptest.NewRequest("GET", "/protected", nil) 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.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
body := decodeErrorBody(t, resp.Body) body := decodeErrorBody(t, resp.Body)
@@ -63,7 +63,7 @@ func TestRequireAuth_BadKey_Returns401(t *testing.T) {
app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(mgr)) app, _, _, _ := mwTestSetup(t, auth.RoleReadOnly, RequireAuth(mgr))
req := httptest.NewRequest("GET", "/protected", nil) req := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer not-a-real-key") 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.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode)
body := decodeErrorBody(t, resp.Body) body := decodeErrorBody(t, resp.Body)
@@ -84,14 +84,14 @@ func TestRequireAuth_BadHeaderShape_Returns401(t *testing.T) {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
mgr := auth.New() mgr := auth.New()
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", RequireAuth(mgr)) 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 := httptest.NewRequest("GET", "/protected", nil)
if tc.value != "" { if tc.value != "" {
req.Header.Set(tc.header, 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.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) 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) apiKey, raw, err := mgr.GenerateAPIKey("good", auth.RoleReadWrite, nil)
require.NoError(t, err) require.NoError(t, err)
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", RequireAuth(mgr)) 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) k, _ := c.Locals(LocalAuthKey).(*auth.APIKey)
require.NotNil(t, k) require.NotNil(t, k)
require.Equal(t, apiKey.ID, k.ID) 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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("good", auth.RoleReadOnly, nil)
require.NoError(t, err) require.NoError(t, err)
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", RequireAuth(mgr)) 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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("X-API-Key", raw) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("expired", auth.RoleReadOnly, &d)
require.NoError(t, err) require.NoError(t, err)
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", RequireAuth(mgr)) 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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("admin", auth.RoleAdmin, nil)
require.NoError(t, err) 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.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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("ro", auth.RoleReadOnly, nil)
require.NoError(t, err) 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.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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, fiber.StatusForbidden, resp.StatusCode) require.Equal(t, fiber.StatusForbidden, resp.StatusCode)
body := decodeErrorBody(t, resp.Body) body := decodeErrorBody(t, resp.Body)
@@ -193,12 +193,12 @@ func TestRequireRole_Mismatch_Returns403(t *testing.T) {
func TestRequireRole_NoHeader_Returns401(t *testing.T) { func TestRequireRole_NoHeader_Returns401(t *testing.T) {
mgr := auth.New() mgr := auth.New()
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", RequireRole(mgr, string(auth.RoleAdmin))) 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 := 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.NoError(t, err)
require.Equal(t, fiber.StatusUnauthorized, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("rw", auth.RoleReadWrite, nil)
require.NoError(t, err) 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.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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) 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) _, raw, err := mgr.GenerateAPIKey("ro", auth.RoleReadOnly, nil)
require.NoError(t, err) 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.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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, fiber.StatusForbidden, resp.StatusCode) require.Equal(t, fiber.StatusForbidden, resp.StatusCode)
} }
func TestOptionalAuth_NoHeader_Continues(t *testing.T) { func TestOptionalAuth_NoHeader_Continues(t *testing.T) {
mgr := auth.New() mgr := auth.New()
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", OptionalAuth(mgr)) 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)) require.Nil(t, c.Locals(LocalAuthKey))
return c.SendStatus(200) return c.SendStatus(200)
}) })
req := httptest.NewRequest("GET", "/protected", nil) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) require.Equal(t, 200, resp.StatusCode)
} }
func TestOptionalAuth_BadKey_ContinuesAnonymously(t *testing.T) { func TestOptionalAuth_BadKey_ContinuesAnonymously(t *testing.T) {
mgr := auth.New() mgr := auth.New()
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", OptionalAuth(mgr)) 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. // Bad key should NOT populate locals.
require.Nil(t, c.Locals(LocalAuthKey)) require.Nil(t, c.Locals(LocalAuthKey))
return c.SendStatus(200) return c.SendStatus(200)
}) })
req := httptest.NewRequest("GET", "/protected", nil) req := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer not-real") 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) 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) apiKey, raw, err := mgr.GenerateAPIKey("good", auth.RoleAdmin, nil)
require.NoError(t, err) require.NoError(t, err)
app := fiber.New(fiber.Config{DisableStartupMessage: true}) app := fiber.New(fiber.Config{})
app.Use("/protected", OptionalAuth(mgr)) 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) k, _ := c.Locals(LocalAuthKey).(*auth.APIKey)
require.NotNil(t, k) require.NotNil(t, k)
require.Equal(t, apiKey.ID, k.ID) 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 := httptest.NewRequest("GET", "/protected", nil)
req.Header.Set("Authorization", "Bearer "+raw) 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.NoError(t, err)
require.Equal(t, 200, resp.StatusCode) require.Equal(t, 200, resp.StatusCode)
} }