diff --git a/README.md b/README.md index ee2e521..cd635cc 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ curl http://localhost:3903/status -H "Authorization: Bearer your-token" ```yaml logging: level: "debug" + format: "text" # or "json" ``` ## License diff --git a/backend/go.mod b/backend/go.mod index e3a2942..5d6ad62 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,15 +3,16 @@ module Noooste/garage-ui go 1.25.3 require ( - github.com/Noooste/azuretls-client v1.12.10 + github.com/Noooste/azuretls-client v1.12.11 github.com/Noooste/swagger v1.2.0 github.com/coreos/go-oidc/v3 v3.17.0 github.com/gofiber/fiber/v3 v3.0.0-rc.3 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/minio/minio-go/v7 v7.0.97 github.com/rs/zerolog v1.34.0 github.com/spf13/viper v1.21.0 github.com/swaggo/swag v1.16.6 - golang.org/x/oauth2 v0.33.0 + golang.org/x/oauth2 v0.34.0 ) require ( @@ -23,7 +24,7 @@ require ( github.com/Noooste/websocket v1.0.3 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/bdandy/go-errors v1.2.2 // indirect - github.com/cloudflare/circl v1.6.1 // indirect + github.com/cloudflare/circl v1.6.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -31,9 +32,9 @@ require ( github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect - github.com/go-openapi/jsonpointer v0.22.3 // indirect - github.com/go-openapi/jsonreference v0.21.3 // indirect - github.com/go-openapi/spec v0.22.1 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/spec v0.22.3 // indirect github.com/go-openapi/swag/conv v0.25.4 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect github.com/go-openapi/swag/jsonutils v0.25.4 // indirect @@ -41,12 +42,9 @@ require ( github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect github.com/gofiber/schema v1.6.0 // indirect - github.com/gofiber/utils/v2 v2.0.0-rc.3 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/gofiber/utils/v2 v2.0.0-rc.5 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.18.2 // indirect @@ -70,15 +68,13 @@ require ( github.com/tinylib/msgp v1.6.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.68.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.39.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/tools v0.40.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 8cff3c6..796c2b8 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,17 +1,15 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/Noooste/azuretls-client v1.12.9 h1:w6XVao95irzge8k+yF5ZovIKf7UBe0GVE3sTRKs/4wE= -github.com/Noooste/azuretls-client v1.12.9/go.mod h1:GHqLaS+vjBk+D3fMA0d+gNgDS2ahtic+6c+7JyMfrdU= -github.com/Noooste/azuretls-client v1.12.10 h1:wD+hSokcB1DCFSSLpj05bMGUCBV0wbPB/Z4uPFNEpoI= -github.com/Noooste/azuretls-client v1.12.10/go.mod h1:nVPwYA6UgHrOinlpvj6t/zDTBV+UfT3t8vmab7WYxF0= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Noooste/azuretls-client v1.12.11 h1:8IvtfPf+K6wOqiRROL/APGkxQCO/+jyjH0S39rnItfQ= +github.com/Noooste/azuretls-client v1.12.11/go.mod h1:lvXW8wpaOwrwtDrSt8nv/Dd8NAbCMVNRoU4sFrAaxYs= github.com/Noooste/fhttp v1.0.15 h1:sYRWOKgr1x4L+wA6REMJCs4Z/lFOSJmuQHSIXMXCcPs= github.com/Noooste/fhttp v1.0.15/go.mod h1:YZtq+i2M11Y22UiOR6gjNSLMNLiPhURh6M44oFVQ1TE= github.com/Noooste/go-socks4 v0.0.2 h1:DwHCYiCEAdjfNrQOFIid7qgKCll7ubhGS1ji5O8FYng= github.com/Noooste/go-socks4 v0.0.2/go.mod h1:+oOgtOFRsU8FoK7NBOhHSjiH5pveY8LgYNF5XcqVgjE= github.com/Noooste/swagger v1.2.0 h1:zGHin8k2V9mXDB1gxXOdKe4V8zhw79ycsw+/L2hH/pk= github.com/Noooste/swagger v1.2.0/go.mod h1:5N+iUZlFA43k2Paf42EZ+SFndBG1niSA1FAnwiNP1PM= -github.com/Noooste/uquic-go v1.0.1 h1:12ARejbnh0R5FLGoHhz4p+qT/8BKkRNTPsJlkX4/pg8= -github.com/Noooste/uquic-go v1.0.1/go.mod h1:6AUIck22N0wQ5+CY/5pLmW3IzdITlf7ABtjbRsaZq2A= github.com/Noooste/uquic-go v1.0.3 h1:VP8npQmU4lkVLm9Ug5Q18SJ8ExFDfUZIzd13YjYaLHE= github.com/Noooste/uquic-go v1.0.3/go.mod h1:MxkrvgpNcbIOSQxqglC3e/798O/6zuL3mBhlFN+04w4= github.com/Noooste/utls v1.3.20 h1:QzBNGGJ184bNMLodOzvM9YWc4vZ36QodIjqFQOHoZ88= @@ -22,8 +20,8 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwTo github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q= github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ= +github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -33,8 +31,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -49,14 +45,14 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8= -github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo= -github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= -github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= -github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k= -github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= +github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= @@ -78,23 +74,18 @@ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxE github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v3 v3.0.0-rc.3 h1:h0KXuRHbivSslIpoHD1R/XjUsjcGwt+2vK0avFiYonA= github.com/gofiber/fiber/v3 v3.0.0-rc.3/go.mod h1:LNBPuS/rGoUFlOyy03fXsWAeWfdGoT1QytwjRVNSVWo= github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY= github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s= -github.com/gofiber/utils/v2 v2.0.0-rc.2 h1:NvJTf7yMafTq16lUOJv70nr+HIOLNQcvGme/X+ftbW8= -github.com/gofiber/utils/v2 v2.0.0-rc.2/go.mod h1:gXins5o7up+BQFiubmO8aUJc/+Mhd7EKXIiAK5GBomI= -github.com/gofiber/utils/v2 v2.0.0-rc.3 h1:gOL5jAEGUT2UbQkTkgMJctYt4rYewnTIt0Y7YaDATDc= -github.com/gofiber/utils/v2 v2.0.0-rc.3/go.mod h1:gXins5o7up+BQFiubmO8aUJc/+Mhd7EKXIiAK5GBomI= +github.com/gofiber/utils/v2 v2.0.0-rc.5 h1:zosaA+j2jm9yhjuxGkFGWxILH8iL0iCoVYT6U/Qgej8= +github.com/gofiber/utils/v2 v2.0.0-rc.5/go.mod h1:8PuWXERC3IoTmoD2Fp/X7amJntq928Fa2yTHI5Orj2M= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -105,22 +96,15 @@ github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+H github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -134,14 +118,12 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk= -github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ= github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= @@ -153,8 +135,8 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo= github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= @@ -179,8 +161,6 @@ github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= -github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= -github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -191,49 +171,47 @@ 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/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/internal/auth/auth.go b/backend/internal/auth/auth.go index 5642417..20d227a 100644 --- a/backend/internal/auth/auth.go +++ b/backend/internal/auth/auth.go @@ -13,8 +13,8 @@ import ( "golang.org/x/oauth2" ) -// AuthService handles authentication operations -type AuthService struct { +// Service handles authentication operations +type Service struct { authConfig *config.AuthConfig serverConfig *config.ServerConfig oidcProvider *oidc.Provider @@ -32,13 +32,13 @@ type UserInfo struct { } // NewAuthService creates a new authentication service -func NewAuthService(authCfg *config.AuthConfig, serverCfg *config.ServerConfig) (*AuthService, error) { +func NewAuthService(authCfg *config.AuthConfig, serverCfg *config.ServerConfig) (*Service, error) { jwtService, err := NewJWTServiceWithKey(authCfg.JWTPrivKey) if err != nil { return nil, fmt.Errorf("failed to initialize JWT service: %w", err) } - service := &AuthService{ + service := &Service{ authConfig: authCfg, serverConfig: serverCfg, jwtService: jwtService, @@ -55,7 +55,7 @@ func NewAuthService(authCfg *config.AuthConfig, serverCfg *config.ServerConfig) } // initOIDC initializes the OIDC provider and configuration -func (a *AuthService) initOIDC() error { +func (a *Service) initOIDC() error { ctx := context.Background() // Create OIDC provider @@ -91,7 +91,7 @@ func (a *AuthService) initOIDC() error { } // ValidateBasicAuth validates basic authentication credentials -func (a *AuthService) ValidateBasicAuth(username, password string) bool { +func (a *Service) ValidateBasicAuth(username, password string) bool { // Use constant-time comparison to prevent timing attacks usernameMatch := subtle.ConstantTimeCompare( []byte(username), @@ -136,7 +136,7 @@ func ParseBasicAuth(authHeader string) (username, password string, ok bool) { } // GetAuthorizationURL returns the OIDC authorization URL for login -func (a *AuthService) GetAuthorizationURL(state string) (string, error) { +func (a *Service) GetAuthorizationURL(state string) (string, error) { if a.oauth2Config == nil { return "", fmt.Errorf("OIDC not initialized") } @@ -145,7 +145,7 @@ func (a *AuthService) GetAuthorizationURL(state string) (string, error) { } // ExchangeCode exchanges an authorization code for tokens -func (a *AuthService) ExchangeCode(ctx context.Context, code string) (*oauth2.Token, error) { +func (a *Service) ExchangeCode(ctx context.Context, code string) (*oauth2.Token, error) { if a.oauth2Config == nil { return nil, fmt.Errorf("OIDC not initialized") } @@ -159,7 +159,7 @@ func (a *AuthService) ExchangeCode(ctx context.Context, code string) (*oauth2.To } // VerifyIDToken verifies an OIDC ID token and extracts user info -func (a *AuthService) VerifyIDToken(ctx context.Context, rawIDToken string) (*UserInfo, error) { +func (a *Service) VerifyIDToken(ctx context.Context, rawIDToken string) (*UserInfo, error) { if a.oidcVerifier == nil { return nil, fmt.Errorf("OIDC not initialized") } @@ -192,7 +192,7 @@ func (a *AuthService) VerifyIDToken(ctx context.Context, rawIDToken string) (*Us } // GetUserInfo retrieves user information from the OIDC provider -func (a *AuthService) GetUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error) { +func (a *Service) GetUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error) { if a.oidcProvider == nil { return nil, fmt.Errorf("OIDC not initialized") } @@ -228,7 +228,7 @@ func (a *AuthService) GetUserInfo(ctx context.Context, token *oauth2.Token) (*Us } // IsAdmin checks if the user has admin role -func (a *AuthService) IsAdmin(userInfo *UserInfo) bool { +func (a *Service) IsAdmin(userInfo *UserInfo) bool { if a.authConfig.OIDC.AdminRole == "" { return false } @@ -316,22 +316,22 @@ func extractStringArray(value interface{}) []string { } // GenerateStateToken generates a secure CSRF state token -func (a *AuthService) GenerateStateToken() (string, error) { +func (a *Service) GenerateStateToken() (string, error) { return a.jwtService.GenerateStateToken() } // ValidateAndConsumeState validates and consumes a CSRF state token -func (a *AuthService) ValidateAndConsumeState(token string) bool { +func (a *Service) ValidateAndConsumeState(token string) bool { return a.jwtService.ValidateAndConsumeState(token) } // GenerateSessionToken generates a JWT session token for the user -func (a *AuthService) GenerateSessionToken(userInfo *UserInfo) (string, error) { +func (a *Service) GenerateSessionToken(userInfo *UserInfo) (string, error) { return a.jwtService.GenerateToken(userInfo, a.authConfig.OIDC.SessionMaxAge) } // ValidateSessionToken validates a JWT session token and returns user info -func (a *AuthService) ValidateSessionToken(tokenString string) (*UserInfo, error) { +func (a *Service) ValidateSessionToken(tokenString string) (*UserInfo, error) { claims, err := a.jwtService.ValidateToken(tokenString) if err != nil { return nil, err diff --git a/backend/internal/handlers/auth.go b/backend/internal/handlers/auth.go index af744dc..d5fe994 100644 --- a/backend/internal/handlers/auth.go +++ b/backend/internal/handlers/auth.go @@ -11,11 +11,11 @@ import ( // AuthHandler handles authentication-related requests type AuthHandler struct { cfg *config.Config - authService *auth.AuthService + authService *auth.Service } // NewAuthHandler creates a new auth handler -func NewAuthHandler(cfg *config.Config, authService *auth.AuthService) *AuthHandler { +func NewAuthHandler(cfg *config.Config, authService *auth.Service) *AuthHandler { return &AuthHandler{ cfg: cfg, authService: authService, @@ -23,12 +23,13 @@ func NewAuthHandler(cfg *config.Config, authService *auth.AuthService) *AuthHand } // GetAuthConfig returns the current authentication configuration -// @Summary Get authentication configuration -// @Description Returns the current auth configuration (admin and/or OIDC) -// @Tags auth -// @Produce json -// @Success 200 {object} object{admin=object,oidc=object} "Auth config" -// @Router /auth/config [get] +// +// @Summary Get authentication configuration +// @Description Returns the current auth configuration (admin and/or OIDC) +// @Tags auth +// @Produce json +// @Success 200 {object} object{admin=object,oidc=object} "Auth config" +// @Router /auth/config [get] func (h *AuthHandler) GetAuthConfig(c fiber.Ctx) error { response := fiber.Map{ "admin": fiber.Map{ @@ -58,16 +59,17 @@ type LoginBasicRequest struct { } // LoginAdmin handles admin authentication login -// @Summary Admin auth login -// @Description Authenticate with admin username and password, returns JWT token -// @Tags auth -// @Accept json -// @Produce json -// @Param credentials body LoginBasicRequest true "Login credentials" -// @Success 200 {object} object{success=bool,token=string,user=object} "Login successful" -// @Failure 400 {object} models.APIResponse "Invalid request" -// @Failure 401 {object} models.APIResponse "Invalid credentials" -// @Router /auth/login [post] +// +// @Summary Admin auth login +// @Description Authenticate with admin username and password, returns JWT token +// @Tags auth +// @Accept json +// @Produce json +// @Param credentials body LoginBasicRequest true "Login credentials" +// @Success 200 {object} object{success=bool,token=string,user=object} "Login successful" +// @Failure 400 {object} models.APIResponse "Invalid request" +// @Failure 401 {object} models.APIResponse "Invalid credentials" +// @Router /auth/login [post] func (h *AuthHandler) LoginAdmin(c fiber.Ctx) error { // Parse request body var req LoginBasicRequest @@ -107,14 +109,15 @@ func (h *AuthHandler) LoginAdmin(c fiber.Ctx) error { } // GetMe returns the current authenticated user's information -// @Summary Get current user -// @Description Returns information about the currently authenticated user -// @Tags auth -// @Produce json -// @Security ApiKeyAuth -// @Success 200 {object} object{success=bool,user=object} "User information" -// @Failure 401 {object} models.APIResponse "Not authenticated" -// @Router /auth/me [get] +// +// @Summary Get current user +// @Description Returns information about the currently authenticated user +// @Tags auth +// @Produce json +// @Security ApiKeyAuth +// @Success 200 {object} object{success=bool,user=object} "User information" +// @Failure 401 {object} models.APIResponse "Not authenticated" +// @Router /auth/me [get] func (h *AuthHandler) GetMe(c fiber.Ctx) error { // Try to get user info from OIDC context userInfoInterface := c.Locals("userInfo") diff --git a/backend/internal/middleware/auth.go b/backend/internal/middleware/auth.go index 3256074..989d917 100644 --- a/backend/internal/middleware/auth.go +++ b/backend/internal/middleware/auth.go @@ -9,7 +9,7 @@ import ( ) // AuthMiddleware supports admin and OIDC authentication -func AuthMiddleware(cfg *config.AuthConfig, authService *auth.AuthService) fiber.Handler { +func AuthMiddleware(cfg *config.AuthConfig, authService *auth.Service) fiber.Handler { return func(c fiber.Ctx) error { // If no auth is enabled, allow all requests if !cfg.Admin.Enabled && !cfg.OIDC.Enabled { @@ -61,41 +61,3 @@ func AuthMiddleware(cfg *config.AuthConfig, authService *auth.AuthService) fiber ) } } - -func RequireAuth(cfg *config.AuthConfig) fiber.Handler { - return func(c fiber.Ctx) error { - if !cfg.Admin.Enabled && !cfg.OIDC.Enabled { - return c.Status(fiber.StatusForbidden).JSON( - models.ErrorResponse(models.ErrCodeForbidden, "Authentication is required but not configured"), - ) - } - return c.Next() - } -} - -func RequireAdmin(authService *auth.AuthService) fiber.Handler { - return func(c fiber.Ctx) error { - userInfoInterface := c.Locals("userInfo") - if userInfoInterface == nil { - return c.Status(fiber.StatusForbidden).JSON( - models.ErrorResponse(models.ErrCodeForbidden, "Admin access required"), - ) - } - - userInfo, ok := userInfoInterface.(*auth.UserInfo) - if !ok { - return c.Status(fiber.StatusForbidden).JSON( - models.ErrorResponse(models.ErrCodeForbidden, "Admin access required"), - ) - } - - // Check if user has admin role - if !authService.IsAdmin(userInfo) { - return c.Status(fiber.StatusForbidden).JSON( - models.ErrorResponse(models.ErrCodeForbidden, "Admin role required"), - ) - } - - return c.Next() - } -} diff --git a/backend/internal/models/garage_admin.go b/backend/internal/models/admin.go similarity index 93% rename from backend/internal/models/garage_admin.go rename to backend/internal/models/admin.go index 7ca63fd..9724052 100644 --- a/backend/internal/models/garage_admin.go +++ b/backend/internal/models/admin.go @@ -2,10 +2,6 @@ package models import "time" -// ==================================== -// Access Key Models -// ==================================== - // GarageKeyInfo represents detailed information about a Garage access key type GarageKeyInfo struct { AccessKeyID string `json:"accessKeyId"` @@ -72,10 +68,6 @@ type ListKeysResponseItem struct { Expiration *time.Time `json:"expiration,omitempty"` } -// ==================================== -// Bucket Models (Admin API) -// ==================================== - // GarageBucketInfo represents detailed information about a bucket from Admin API type GarageBucketInfo struct { ID string `json:"id"` @@ -153,10 +145,6 @@ type BucketLocalAlias struct { Alias string `json:"alias"` } -// ==================================== -// Bucket Alias Models -// ==================================== - // AddBucketAliasRequest represents the request to add a bucket alias type AddBucketAliasRequest struct { BucketID string `json:"bucketId"` @@ -173,10 +161,6 @@ type RemoveBucketAliasRequest struct { AccessKeyID *string `json:"accessKeyId,omitempty"` } -// ==================================== -// Permission Models -// ==================================== - // BucketKeyPermRequest represents a request to change bucket-key permissions type BucketKeyPermRequest struct { BucketID string `json:"bucketId"` @@ -184,10 +168,6 @@ type BucketKeyPermRequest struct { Permissions BucketKeyPermission `json:"permissions"` } -// ==================================== -// Cluster Models -// ==================================== - // ClusterHealth represents the health status of the cluster type ClusterHealth struct { Status string `json:"status"` @@ -211,10 +191,6 @@ type ClusterStatistics struct { Freeform string `json:"freeform"` } -// ==================================== -// Node Models -// ==================================== - // NodeInfo represents information about a cluster node type NodeInfo struct { ID string `json:"id"` diff --git a/backend/internal/routes/routes.go b/backend/internal/routes/routes.go index 946858b..68910e2 100644 --- a/backend/internal/routes/routes.go +++ b/backend/internal/routes/routes.go @@ -5,7 +5,7 @@ import ( "Noooste/garage-ui/internal/config" "Noooste/garage-ui/internal/handlers" "Noooste/garage-ui/internal/middleware" - "fmt" + "Noooste/garage-ui/pkg/logger" "net/url" "os" "path/filepath" @@ -22,7 +22,7 @@ import ( func SetupRoutes( app *fiber.App, cfg *config.Config, - authService *auth.AuthService, + authService *auth.Service, healthHandler *handlers.HealthHandler, bucketHandler *handlers.BucketHandler, objectHandler *handlers.ObjectHandler, @@ -292,7 +292,7 @@ func SetupRoutes( strings.HasPrefix(path, "/auth") || strings.HasPrefix(path, "/health") || strings.HasPrefix(path, "/docs") { - fmt.Println("API or health check route, skipping SPA fallback:", path) + logger.Debug().Str("path", path).Msg("API or health check route, skipping SPA fallback") return c.Next() } diff --git a/backend/internal/services/garage_admin.go b/backend/internal/services/admin.go similarity index 94% rename from backend/internal/services/garage_admin.go rename to backend/internal/services/admin.go index 6fda374..c9cd46d 100644 --- a/backend/internal/services/garage_admin.go +++ b/backend/internal/services/admin.go @@ -21,9 +21,12 @@ type GarageAdminService struct { } // NewGarageAdminService creates a new Garage Admin API service -func NewGarageAdminService(cfg *config.GarageConfig) *GarageAdminService { +func NewGarageAdminService(cfg *config.GarageConfig, logLevel string) *GarageAdminService { session := azuretls.NewSession() - session.Log() + + if logLevel == "debug" { + session.Log() + } return &GarageAdminService{ baseURL: cfg.AdminEndpoint, @@ -76,10 +79,6 @@ func decodeResponse(resp *azuretls.Response, target interface{}) error { return nil } -// ==================================== -// Access Key Operations -// ==================================== - // ListKeys returns all access keys in the cluster func (s *GarageAdminService) ListKeys(ctx context.Context) ([]models.ListKeysResponseItem, error) { resp, err := s.doRequest(ctx, http.MethodGet, "/v2/ListKeys", nil) @@ -178,10 +177,6 @@ func (s *GarageAdminService) ImportKey(ctx context.Context, req models.ImportKey return &result, nil } -// ==================================== -// Bucket Operations (Admin API) -// ==================================== - // ListBuckets returns all buckets in the cluster func (s *GarageAdminService) ListBuckets(ctx context.Context) ([]models.ListBucketsResponseItem, error) { resp, err := s.doRequest(ctx, http.MethodGet, "/v2/ListBuckets", nil) @@ -279,10 +274,6 @@ func (s *GarageAdminService) DeleteBucket(ctx context.Context, bucketID string) return nil } -// ==================================== -// Bucket Alias Operations -// ==================================== - // AddBucketAlias adds an alias to a bucket func (s *GarageAdminService) AddBucketAlias(ctx context.Context, req models.AddBucketAliasRequest) (*models.GarageBucketInfo, error) { resp, err := s.doRequest(ctx, http.MethodPost, "/v2/AddBucketAlias", req) @@ -313,10 +304,6 @@ func (s *GarageAdminService) RemoveBucketAlias(ctx context.Context, req models.R return &result, nil } -// ==================================== -// Permission Operations -// ==================================== - // AllowBucketKey grants permissions for a key on a bucket func (s *GarageAdminService) AllowBucketKey(ctx context.Context, req models.BucketKeyPermRequest) (*models.GarageBucketInfo, error) { resp, err := s.doRequest(ctx, http.MethodPost, "/v2/AllowBucketKey", req) @@ -347,10 +334,6 @@ func (s *GarageAdminService) DenyBucketKey(ctx context.Context, req models.Bucke return &result, nil } -// ==================================== -// Cluster Operations -// ==================================== - // GetClusterHealth returns the health status of the cluster func (s *GarageAdminService) GetClusterHealth(ctx context.Context) (*models.ClusterHealth, error) { resp, err := s.doRequest(ctx, http.MethodGet, "/v2/GetClusterHealth", nil) @@ -396,10 +379,6 @@ func (s *GarageAdminService) GetClusterStatistics(ctx context.Context) (*models. return &result, nil } -// ==================================== -// Node Operations -// ==================================== - // GetNodeInfo returns information about a specific node func (s *GarageAdminService) GetNodeInfo(ctx context.Context, nodeID string) (*models.MultiNodeResponse, error) { path := fmt.Sprintf("/v2/GetNodeInfo?node=%s", nodeID) @@ -434,10 +413,6 @@ func (s *GarageAdminService) GetNodeStatistics(ctx context.Context, nodeID strin return &result, nil } -// ==================================== -// Monitoring Operations -// ==================================== - // HealthCheck checks if the Admin API is reachable func (s *GarageAdminService) HealthCheck(ctx context.Context) error { resp, err := s.doRequest(ctx, http.MethodGet, "/health", nil) diff --git a/backend/main.go b/backend/main.go index 01a1c8f..126325a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "log" "os" "os/signal" "syscall" @@ -13,9 +12,9 @@ import ( "Noooste/garage-ui/internal/handlers" "Noooste/garage-ui/internal/routes" "Noooste/garage-ui/internal/services" + "Noooste/garage-ui/pkg/logger" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/recover" ) @@ -62,20 +61,31 @@ func main() { configPath := flag.String("config", "config.yaml", "Path to configuration file") flag.Parse() - // Load configuration - log.Printf("Loading configuration from: %s", *configPath) + // Load configuration first (before initializing logger) cfg, err := config.Load(*configPath) if err != nil { - log.Fatalf("Failed to load configuration: %v", err) + // If config fails to load, use default logger to report the error + logger.Get().Fatal().Err(err).Str("config_path", *configPath).Msg("Failed to load configuration") } - log.Printf("Starting Garage UI Backend v%s in %s mode", version, cfg.Server.Environment) + // Initialize logger with configuration from config file + logger.Init(logger.Config{ + Level: cfg.Logging.Level, + Format: cfg.Logging.Format, + }) + + // Now log with the properly configured logger + logger.Info(). + Str("config_path", *configPath). + Str("version", version). + Str("environment", cfg.Server.Environment). + Msg("Starting Garage UI Backend") // Initialize services - log.Println("Initializing Garage Admin service...") - adminService := services.NewGarageAdminService(&cfg.Garage) + logger.Info().Msg("Initializing Garage Admin service") + adminService := services.NewGarageAdminService(&cfg.Garage, cfg.Logging.Level) - log.Println("Initializing S3 service...") + logger.Info().Msg("Initializing S3 service") s3Service := services.NewS3Service(&cfg.Garage, adminService) // Determine enabled auth methods for logging @@ -89,10 +99,10 @@ func main() { if len(authMethods) == 0 { authMethods = append(authMethods, "none") } - log.Printf("Initializing authentication service (enabled: %v)...", authMethods) + logger.Info().Strs("enabled_methods", authMethods).Msg("Initializing authentication service") authService, err := auth.NewAuthService(&cfg.Auth, &cfg.Server) if err != nil { - log.Fatalf("Failed to initialize auth service: %v", err) + logger.Fatal().Err(err).Msg("Failed to initialize auth service") } // Initialize handlers @@ -121,9 +131,12 @@ func main() { writeBufferSize = 4096 // 4KB default } - log.Printf("Server limits - Max body: %d bytes (%.2fMB), Max header: %d bytes (%.2fKB)", - maxBodySize, float64(maxBodySize)/(1024*1024), - maxHeaderSize, float64(maxHeaderSize)/1024) + logger.Info(). + Int64("max_body_bytes", maxBodySize). + Float64("max_body_mb", float64(maxBodySize)/(1024*1024)). + Int("max_header_bytes", maxHeaderSize). + Float64("max_header_kb", float64(maxHeaderSize)/1024). + Msg("Server request limits configured") // Create Fiber app with configuration app := fiber.New(fiber.Config{ @@ -136,12 +149,9 @@ func main() { // Apply global middleware app.Use(recover.New()) // Panic recovery - app.Use(logger.New(logger.Config{ - Format: "[${time}] ${status} - ${method} ${path} (${latency})\n", - })) // Setup routes - log.Println("Setting up routes...") + logger.Info().Msg("Setting up routes") routes.SetupRoutes( app, cfg, @@ -157,12 +167,14 @@ func main() { // Start server in a goroutine go func() { addr := cfg.GetAddress() - log.Printf("Server listening on %s", addr) - log.Printf("Health check available at: http://%s/health", addr) - log.Printf("API documentation: http://%s/api/v1/", addr) + logger.Info(). + Str("address", addr). + Str("health_endpoint", fmt.Sprintf("http://%s/health", addr)). + Str("api_docs", fmt.Sprintf("http://%s/api/v1/", addr)). + Msg("Server starting") if err := app.Listen(addr); err != nil { - log.Fatalf("Failed to start server: %v", err) + logger.Fatal().Err(err).Msg("Failed to start server") } }() @@ -171,12 +183,12 @@ func main() { signal.Notify(quit, os.Interrupt, syscall.SIGTERM) <-quit - log.Println("Shutting down server...") + logger.Info().Msg("Shutting down server") if err := app.Shutdown(); err != nil { - log.Fatalf("Server shutdown failed: %v", err) + logger.Fatal().Err(err).Msg("Server shutdown failed") } - log.Println("Server stopped gracefully") + logger.Info().Msg("Server stopped gracefully") } // customErrorHandler handles errors globally @@ -190,7 +202,12 @@ func customErrorHandler(c fiber.Ctx, err error) error { } // Log the error - log.Printf("Error: %v", err) + logger.Error(). + Err(err). + Int("status_code", code). + Str("method", c.Method()). + Str("path", c.Path()). + Msg("Request error") // Return JSON error response return c.Status(code).JSON(fiber.Map{ diff --git a/backend/pkg/logger/logger.go b/backend/pkg/logger/logger.go index 7d22748..b4724f9 100644 --- a/backend/pkg/logger/logger.go +++ b/backend/pkg/logger/logger.go @@ -21,17 +21,16 @@ var ( // Config holds logger configuration type Config struct { - Level string // debug, info, warn, error - Pretty bool // Enable console pretty printing - TimeFormat string // Time format (default: time.RFC3339) + Level string // debug, info, warn, error + Format string // json, text } // Init initializes the global logger with the given configuration func Init(cfg Config) { var output io.Writer = os.Stdout - // Set up pretty console output if enabled - if cfg.Pretty { + // Set up console output for text format + if cfg.Format == "text" { output = zerolog.ConsoleWriter{ Out: os.Stdout, TimeFormat: time.RFC3339, @@ -70,7 +69,7 @@ func Get() *Logger { // Initialize with defaults if not initialized Init(Config{ Level: "info", - Pretty: true, + Format: "text", }) } return globalLogger diff --git a/backend/pkg/utils/cache.go b/backend/pkg/utils/cache.go index 5413ea0..368982d 100644 --- a/backend/pkg/utils/cache.go +++ b/backend/pkg/utils/cache.go @@ -91,5 +91,4 @@ func (c *Cache) cleanupExpired() { } } -// Global cache instance var GlobalCache = NewCache() diff --git a/config.yaml.example b/config.yaml.example index b8bb940..b0cbda1 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -103,7 +103,8 @@ cors: allow_credentials: false max_age: 3600 -# Logging +# Logging Configuration +# The application uses zerolog for structured logging logging: - level: "info" # debug, info, warn, error - format: "json" # json, text + level: "info" # Options: debug, info, warn, error + format: "text" or "json" diff --git a/docker-compose.yml b/docker-compose.yml index 4079fbc..5bf37a5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,7 +41,3 @@ services: -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIH0ZHqIV7MEyVsxNYc5TA/a0qaBxgq3ntlFS3w1F03MS -----END PRIVATE KEY----- - - # Logging - GARAGE_UI_LOGGING_LEVEL: "info" - GARAGE_UI_LOGGING_FORMAT: "json" diff --git a/frontend/src/hooks/useApi.ts b/frontend/src/hooks/useApi.ts index d987ac9..af4fc46 100644 --- a/frontend/src/hooks/useApi.ts +++ b/frontend/src/hooks/useApi.ts @@ -3,9 +3,6 @@ import { bucketsApi, objectsApi, accessApi, garageApi, analyticsApi } from '@/li import { queryKeys } from '@/lib/query-client'; import { toast } from 'sonner'; -// =========================== -// Bucket Hooks -// =========================== export function useBuckets() { return useQuery({ @@ -66,9 +63,6 @@ export function useGrantBucketPermission() { }); } -// =========================== -// Object Hooks -// =========================== export function useObjects(bucket: string, prefix?: string, enabled = true) { return useQuery({ @@ -138,9 +132,6 @@ export function useDeleteMultipleObjects() { }); } -// =========================== -// Access Key Hooks -// =========================== export function useAccessKeys() { return useQuery({ @@ -197,9 +188,6 @@ export function useUpdateAccessKey() { }); } -// =========================== -// Cluster Hooks -// =========================== export function useClusterHealth() { return useQuery({ @@ -225,9 +213,6 @@ export function useClusterStatistics() { }); } -// =========================== -// Dashboard Hooks -// =========================== export function useDashboardMetrics() { return useQuery({