diff --git a/.gitignore b/.gitignore index b50e444..ee4c55d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,8 @@ dist/ build/ # Compiled binaries -/server server.exe +main.exe # IDE files .vscode/ @@ -108,3 +108,4 @@ local/ dev/ service_coverage .gitignore +docs/ diff --git a/cmd/server/main.go b/cmd/server/main.go index 8abc2fa..2503cca 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,3 +1,12 @@ +// @title CarrotSkin API +// @version 1.0 +// @description Minecraft皮肤站后端API +// @host localhost:8080 +// @BasePath /api/v1 +// @securityDefinitions.apikey BearerAuth +// @in header +// @name Authorization + package main import ( @@ -22,6 +31,8 @@ import ( "github.com/gin-gonic/gin" "go.uber.org/zap" + + _ "carrotskin/docs" // Swagger docs ) func main() { diff --git a/docker-compose.yml b/docker-compose.yml index 843629b..3795054 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -108,7 +108,7 @@ services: retries: 5 start_period: 5s - # ==================== RustFS 对象存储 (可选) ==================== + # ==================== RustFS 对象存储==================== rustfs: image: ghcr.io/rustfs/rustfs:latest container_name: carrotskin-rustfs diff --git a/go.mod b/go.mod index 41ad466..40d2389 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,9 @@ require ( github.com/minio/minio-go/v7 v7.0.97 github.com/redis/go-redis/v9 v9.17.2 github.com/spf13/viper v1.21.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.1 + github.com/swaggo/swag v1.16.6 github.com/wenlng/go-captcha-assets v1.0.7 github.com/wenlng/go-captcha/v2 v2.0.4 go.uber.org/zap v1.27.1 @@ -23,10 +26,21 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/go-ini/ini v1.67.0 // 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/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 + github.com/go-openapi/swag/loading v0.25.4 // indirect + 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-sql-driver/mysql v1.9.3 // indirect github.com/goccy/go-yaml v1.19.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect @@ -36,7 +50,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.57.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/tinylib/msgp v1.6.1 // indirect go.uber.org/mock v0.6.0 // indirect golang.org/x/image v0.33.0 // indirect diff --git a/go.sum b/go.sum index 2c03017..06a89ac 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -12,8 +14,6 @@ github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2N github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= 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/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk= -github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= github.com/chai2010/webp v1.4.0 h1:6DA2pkkRUPnbOHvvsmGI3He1hBKf/bkRlniAiSGuEko= github.com/chai2010/webp v1.4.0/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= @@ -31,12 +31,41 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= 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-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-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +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= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -51,8 +80,6 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 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/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= @@ -105,8 +132,6 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= -github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= -github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= 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= @@ -124,12 +149,8 @@ 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= 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/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= @@ -162,8 +183,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= -github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= +github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= +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.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -177,8 +202,6 @@ github.com/wenlng/go-captcha/v2 v2.0.4/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQv github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -204,6 +227,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -230,6 +254,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/internal/container/container.go b/internal/container/container.go index b3f0ef8..cecd6a7 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -35,16 +35,17 @@ type Container struct { YggdrasilRepo repository.YggdrasilRepository // Service层 - UserService service.UserService - ProfileService service.ProfileService - TextureService service.TextureService - TokenService service.TokenService - YggdrasilService service.YggdrasilService - VerificationService service.VerificationService - UploadService service.UploadService - SecurityService service.SecurityService - CaptchaService service.CaptchaService - SignatureService *service.SignatureService + UserService service.UserService + ProfileService service.ProfileService + TextureService service.TextureService + TokenService service.TokenService + YggdrasilService service.YggdrasilService + VerificationService service.VerificationService + UploadService service.UploadService + SecurityService service.SecurityService + CaptchaService service.CaptchaService + SignatureService *service.SignatureService + TextureRenderService service.TextureRenderService } // NewContainer 创建依赖容器 @@ -89,6 +90,7 @@ func NewContainer( c.UserService = service.NewUserService(c.UserRepo, c.ConfigRepo, jwtService, redisClient, cacheManager, logger) c.ProfileService = service.NewProfileService(c.ProfileRepo, c.UserRepo, cacheManager, logger) c.TextureService = service.NewTextureService(c.TextureRepo, c.UserRepo, storageClient, cacheManager, logger) + c.TextureRenderService = service.NewTextureRenderService(c.TextureRepo, storageClient, cacheManager, logger) // 获取Yggdrasil私钥并创建JWT服务(TokenService需要) // 注意:这里仍然需要预先初始化,因为TokenService在创建时需要YggdrasilJWT diff --git a/internal/handler/helpers.go b/internal/handler/helpers.go index 202a50b..e52c4ac 100644 --- a/internal/handler/helpers.go +++ b/internal/handler/helpers.go @@ -70,7 +70,6 @@ func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo { Name: profile.Name, SkinID: profile.SkinID, CapeID: profile.CapeID, - IsActive: profile.IsActive, LastUsedAt: profile.LastUsedAt, CreatedAt: profile.CreatedAt, UpdatedAt: profile.UpdatedAt, @@ -173,24 +172,24 @@ func RespondWithError(c *gin.Context, err error) { } // 使用errors.Is检查预定义错误 - if errors.Is(err, errors.ErrUserNotFound) || - errors.Is(err, errors.ErrProfileNotFound) || - errors.Is(err, errors.ErrTextureNotFound) || - errors.Is(err, errors.ErrNotFound) { + if errors.Is(err, errors.ErrUserNotFound) || + errors.Is(err, errors.ErrProfileNotFound) || + errors.Is(err, errors.ErrTextureNotFound) || + errors.Is(err, errors.ErrNotFound) { RespondNotFound(c, err.Error()) return } - if errors.Is(err, errors.ErrProfileNoPermission) || - errors.Is(err, errors.ErrTextureNoPermission) || - errors.Is(err, errors.ErrForbidden) { + if errors.Is(err, errors.ErrProfileNoPermission) || + errors.Is(err, errors.ErrTextureNoPermission) || + errors.Is(err, errors.ErrForbidden) { RespondForbidden(c, err.Error()) return } - if errors.Is(err, errors.ErrUnauthorized) || - errors.Is(err, errors.ErrInvalidToken) || - errors.Is(err, errors.ErrTokenExpired) { + if errors.Is(err, errors.ErrUnauthorized) || + errors.Is(err, errors.ErrInvalidToken) || + errors.Is(err, errors.ErrTokenExpired) { RespondUnauthorized(c, err.Error()) return } diff --git a/internal/handler/profile_handler.go b/internal/handler/profile_handler.go index 345f20b..243db48 100644 --- a/internal/handler/profile_handler.go +++ b/internal/handler/profile_handler.go @@ -207,39 +207,3 @@ func (h *ProfileHandler) Delete(c *gin.Context) { RespondSuccess(c, gin.H{"message": "删除成功"}) } - -// SetActive 设置活跃档案 -// @Summary 设置活跃档案 -// @Description 将指定档案设置为活跃状态 -// @Tags profile -// @Accept json -// @Produce json -// @Security BearerAuth -// @Param uuid path string true "档案UUID" -// @Success 200 {object} model.Response "设置成功" -// @Failure 403 {object} model.ErrorResponse "无权操作" -// @Router /api/v1/profile/{uuid}/activate [post] -func (h *ProfileHandler) SetActive(c *gin.Context) { - userID, ok := GetUserIDFromContext(c) - if !ok { - return - } - - uuid := c.Param("uuid") - if uuid == "" { - RespondBadRequest(c, "UUID不能为空", nil) - return - } - - if err := h.container.ProfileService.SetActive(c.Request.Context(), uuid, userID); err != nil { - h.logger.Error("设置活跃档案失败", - zap.String("uuid", uuid), - zap.Int64("user_id", userID), - zap.Error(err), - ) - RespondWithError(c, err) - return - } - - RespondSuccess(c, gin.H{"message": "设置成功"}) -} diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 5806e86..209e383 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -7,6 +7,8 @@ import ( "carrotskin/pkg/auth" "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) // Handlers 集中管理所有Handler @@ -38,6 +40,9 @@ func RegisterRoutesWithDI(router *gin.Engine, c *container.Container) { // 健康检查路由 router.GET("/health", HealthCheck) + // Swagger文档路由 + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + // 创建Handler实例 h := NewHandlers(c) @@ -147,7 +152,6 @@ func registerProfileRoutesWithDI(v1 *gin.RouterGroup, h *ProfileHandler, jwtServ profileAuth.GET("/", h.List) profileAuth.PUT("/:uuid", h.Update) profileAuth.DELETE("/:uuid", h.Delete) - profileAuth.POST("/:uuid/activate", h.SetActive) } } } diff --git a/internal/model/profile.go b/internal/model/profile.go index 4f64158..e087d70 100644 --- a/internal/model/profile.go +++ b/internal/model/profile.go @@ -7,12 +7,11 @@ import ( // Profile Minecraft 档案模型 type Profile struct { UUID string `gorm:"column:uuid;type:varchar(36);primaryKey" json:"uuid"` - UserID int64 `gorm:"column:user_id;not null;index:idx_profiles_user_created,priority:1;index:idx_profiles_user_active,priority:1" json:"user_id"` + UserID int64 `gorm:"column:user_id;not null;index:idx_profiles_user_created,priority:1" json:"user_id"` Name string `gorm:"column:name;type:varchar(16);not null;uniqueIndex:idx_profiles_name" json:"name"` // Minecraft 角色名 SkinID *int64 `gorm:"column:skin_id;type:bigint;index:idx_profiles_skin_id" json:"skin_id,omitempty"` CapeID *int64 `gorm:"column:cape_id;type:bigint;index:idx_profiles_cape_id" json:"cape_id,omitempty"` RSAPrivateKey string `gorm:"column:rsa_private_key;type:text;not null" json:"-"` // RSA 私钥不返回给前端 - IsActive bool `gorm:"column:is_active;not null;default:true;index:idx_profiles_user_active,priority:2" json:"is_active"` LastUsedAt *time.Time `gorm:"column:last_used_at;type:timestamp;index:idx_profiles_last_used,sort:desc" json:"last_used_at,omitempty"` CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;index:idx_profiles_user_created,priority:2,sort:desc" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP" json:"updated_at"` @@ -33,7 +32,6 @@ type ProfileResponse struct { UUID string `json:"uuid"` Name string `json:"name"` Textures ProfileTexturesData `json:"textures"` - IsActive bool `json:"is_active"` LastUsedAt *time.Time `json:"last_used_at,omitempty"` CreatedAt time.Time `json:"created_at"` } diff --git a/internal/repository/interfaces.go b/internal/repository/interfaces.go index 1faa028..f630c42 100644 --- a/internal/repository/interfaces.go +++ b/internal/repository/interfaces.go @@ -35,7 +35,6 @@ type ProfileRepository interface { Delete(ctx context.Context, uuid string) error BatchDelete(ctx context.Context, uuids []string) (int64, error) // 批量删除 CountByUserID(ctx context.Context, userID int64) (int64, error) - SetActive(ctx context.Context, uuid string, userID int64) error UpdateLastUsedAt(ctx context.Context, uuid string) error GetByNames(ctx context.Context, names []string) ([]*model.Profile, error) GetKeyPair(ctx context.Context, profileId string) (*model.KeyPair, error) diff --git a/internal/repository/profile_repository.go b/internal/repository/profile_repository.go index 6f9cded..623046e 100644 --- a/internal/repository/profile_repository.go +++ b/internal/repository/profile_repository.go @@ -109,20 +109,6 @@ func (r *profileRepository) CountByUserID(ctx context.Context, userID int64) (in return count, err } -func (r *profileRepository) SetActive(ctx context.Context, uuid string, userID int64) error { - return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - if err := tx.Model(&model.Profile{}). - Where("user_id = ?", userID). - Update("is_active", false).Error; err != nil { - return err - } - - return tx.Model(&model.Profile{}). - Where("uuid = ? AND user_id = ?", uuid, userID). - Update("is_active", true).Error - }) -} - func (r *profileRepository) UpdateLastUsedAt(ctx context.Context, uuid string) error { return r.db.WithContext(ctx).Model(&model.Profile{}). Where("uuid = ?", uuid). diff --git a/internal/repository/profile_repository_test.go b/internal/repository/profile_repository_test.go index 4566c98..1e29cf2 100644 --- a/internal/repository/profile_repository_test.go +++ b/internal/repository/profile_repository_test.go @@ -42,41 +42,6 @@ func TestProfileRepository_QueryConditions(t *testing.T) { } } -// TestProfileRepository_SetActiveLogic 测试设置活跃档案的逻辑 -func TestProfileRepository_SetActiveLogic(t *testing.T) { - tests := []struct { - name string - uuid string - userID int64 - otherProfiles int - wantAllInactive bool - }{ - { - name: "设置一个档案为活跃,其他应该变为非活跃", - uuid: "profile-1", - userID: 1, - otherProfiles: 2, - wantAllInactive: true, - }, - { - name: "只有一个档案时", - uuid: "profile-1", - userID: 1, - otherProfiles: 0, - wantAllInactive: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // 验证逻辑:设置一个档案为活跃时,应该先将所有档案设为非活跃 - if !tt.wantAllInactive { - t.Error("Setting active profile should first set all profiles to inactive") - } - }) - } -} - // TestProfileRepository_CountLogic 测试统计逻辑 func TestProfileRepository_CountLogic(t *testing.T) { tests := []struct { @@ -109,30 +74,30 @@ func TestProfileRepository_CountLogic(t *testing.T) { // TestProfileRepository_UpdateFieldsLogic 测试更新字段逻辑 func TestProfileRepository_UpdateFieldsLogic(t *testing.T) { tests := []struct { - name string - uuid string - updates map[string]interface{} + name string + uuid string + updates map[string]interface{} wantValid bool }{ { name: "有效的更新", uuid: "123e4567-e89b-12d3-a456-426614174000", updates: map[string]interface{}{ - "name": "NewName", + "name": "NewName", "skin_id": int64(1), }, wantValid: true, }, { - name: "UUID为空", - uuid: "", - updates: map[string]interface{}{"name": "NewName"}, + name: "UUID为空", + uuid: "", + updates: map[string]interface{}{"name": "NewName"}, wantValid: false, }, { - name: "更新字段为空", - uuid: "123e4567-e89b-12d3-a456-426614174000", - updates: map[string]interface{}{}, + name: "更新字段为空", + uuid: "123e4567-e89b-12d3-a456-426614174000", + updates: map[string]interface{}{}, wantValid: true, // 空更新也是有效的,只是不会更新任何字段 }, } @@ -150,24 +115,24 @@ func TestProfileRepository_UpdateFieldsLogic(t *testing.T) { // TestProfileRepository_FindOneProfileLogic 测试查找单个档案的逻辑 func TestProfileRepository_FindOneProfileLogic(t *testing.T) { tests := []struct { - name string + name string profileCount int - wantError bool + wantError bool }{ { - name: "有档案时返回第一个", + name: "有档案时返回第一个", profileCount: 1, - wantError: false, + wantError: false, }, { - name: "多个档案时返回第一个", + name: "多个档案时返回第一个", profileCount: 3, - wantError: false, + wantError: false, }, { - name: "没有档案时应该错误", + name: "没有档案时应该错误", profileCount: 0, - wantError: true, + wantError: true, }, } @@ -181,4 +146,3 @@ func TestProfileRepository_FindOneProfileLogic(t *testing.T) { }) } } - diff --git a/internal/service/interfaces.go b/internal/service/interfaces.go index 2a03a9d..1e97f7d 100644 --- a/internal/service/interfaces.go +++ b/internal/service/interfaces.go @@ -45,7 +45,6 @@ type ProfileService interface { Delete(ctx context.Context, uuid string, userID int64) error // 档案状态 - SetActive(ctx context.Context, uuid string, userID int64) error CheckLimit(ctx context.Context, userID int64, maxProfiles int) error // 批量查询 diff --git a/internal/service/mocks_test.go b/internal/service/mocks_test.go index aa890d1..1c9fc30 100644 --- a/internal/service/mocks_test.go +++ b/internal/service/mocks_test.go @@ -214,10 +214,6 @@ func (m *MockProfileRepository) CountByUserID(ctx context.Context, userID int64) return int64(len(m.userProfiles[userID])), nil } -func (m *MockProfileRepository) SetActive(ctx context.Context, uuid string, userID int64) error { - return nil -} - func (m *MockProfileRepository) UpdateLastUsedAt(ctx context.Context, uuid string) error { return nil } @@ -808,10 +804,6 @@ func (m *MockProfileService) Delete(uuid string, userID int64) error { return nil } -func (m *MockProfileService) SetActive(uuid string, userID int64) error { - return nil -} - func (m *MockProfileService) CheckLimit(userID int64, maxProfiles int) error { count := 0 for _, profile := range m.profiles { diff --git a/internal/service/profile_service.go b/internal/service/profile_service.go index eda9a53..b42577f 100644 --- a/internal/service/profile_service.go +++ b/internal/service/profile_service.go @@ -77,18 +77,12 @@ func (s *profileService) Create(ctx context.Context, userID int64, name string) UserID: userID, Name: name, RSAPrivateKey: privateKey, - IsActive: true, } if err := s.profileRepo.Create(ctx, profile); err != nil { return nil, fmt.Errorf("创建档案失败: %w", err) } - // 设置活跃状态 - if err := s.profileRepo.SetActive(ctx, profileUUID, userID); err != nil { - return nil, fmt.Errorf("设置活跃状态失败: %w", err) - } - // 清除用户的 profile 列表缓存 s.cacheInv.OnCreate(ctx, s.cacheKeys.ProfileList(userID)) @@ -220,34 +214,6 @@ func (s *profileService) Delete(ctx context.Context, uuid string, userID int64) return nil } -func (s *profileService) SetActive(ctx context.Context, uuid string, userID int64) error { - // 获取档案并验证权限 - profile, err := s.profileRepo.FindByUUID(ctx, uuid) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return ErrProfileNotFound - } - return fmt.Errorf("查询档案失败: %w", err) - } - - if profile.UserID != userID { - return ErrProfileNoPermission - } - - if err := s.profileRepo.SetActive(ctx, uuid, userID); err != nil { - return fmt.Errorf("设置活跃状态失败: %w", err) - } - - if err := s.profileRepo.UpdateLastUsedAt(ctx, uuid); err != nil { - return fmt.Errorf("更新使用时间失败: %w", err) - } - - // 清除该用户所有 profile 的缓存(因为活跃状态改变了) - s.cacheInv.BatchInvalidate(ctx, s.cacheKeys.ProfilePattern(userID)) - - return nil -} - func (s *profileService) CheckLimit(ctx context.Context, userID int64, maxProfiles int) error { count, err := s.profileRepo.CountByUserID(ctx, userID) if err != nil { diff --git a/internal/service/profile_service_test.go b/internal/service/profile_service_test.go index 960f090..5d92895 100644 --- a/internal/service/profile_service_test.go +++ b/internal/service/profile_service_test.go @@ -80,15 +80,6 @@ func TestProfileService_StatusValidation(t *testing.T) { } } -// TestProfileService_IsActiveDefault 测试Profile默认活跃状态 -func TestProfileService_IsActiveDefault(t *testing.T) { - // 新创建的档案默认为活跃状态 - isActive := true - if !isActive { - t.Error("新创建的Profile应该默认为活跃状态") - } -} - // TestUpdateProfile_PermissionCheck 测试更新Profile的权限检查逻辑 func TestUpdateProfile_PermissionCheck(t *testing.T) { tests := []struct { @@ -191,38 +182,6 @@ func TestDeleteProfile_PermissionCheck(t *testing.T) { } } -// TestSetActiveProfile_PermissionCheck 测试设置活跃Profile的权限检查 -func TestSetActiveProfile_PermissionCheck(t *testing.T) { - tests := []struct { - name string - profileUserID int64 - requestUserID int64 - wantErr bool - }{ - { - name: "用户ID匹配,允许设置", - profileUserID: 1, - requestUserID: 1, - wantErr: false, - }, - { - name: "用户ID不匹配,拒绝设置", - profileUserID: 1, - requestUserID: 2, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - hasError := tt.profileUserID != tt.requestUserID - if hasError != tt.wantErr { - t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr) - } - }) - } -} - // TestCheckProfileLimit_Logic 测试Profile数量限制检查逻辑 func TestCheckProfileLimit_Logic(t *testing.T) { tests := []struct { @@ -642,8 +601,8 @@ func TestProfileServiceImpl_GetByUserID(t *testing.T) { } } -// TestProfileServiceImpl_Update_And_SetActive 测试 Update 与 SetActive -func TestProfileServiceImpl_Update_And_SetActive(t *testing.T) { +// TestProfileServiceImpl_Update 测试 Update +func TestProfileServiceImpl_Update(t *testing.T) { profileRepo := NewMockProfileRepository() userRepo := NewMockUserRepository() logger := zap.NewNop() @@ -686,16 +645,6 @@ func TestProfileServiceImpl_Update_And_SetActive(t *testing.T) { if _, err := svc.Update(ctx, "u1", 1, stringPtr("Duplicate"), nil, nil); err == nil { t.Fatalf("Update 在名称重复时应返回错误") } - - // SetActive 正常 - if err := svc.SetActive(ctx, "u1", 1); err != nil { - t.Fatalf("SetActive 正常情况失败: %v", err) - } - - // SetActive 无权限 - if err := svc.SetActive(ctx, "u1", 2); err == nil { - t.Fatalf("SetActive 在无权限时应返回错误") - } } // TestProfileServiceImpl_CheckLimit_And_GetByNames 测试 CheckLimit / GetByNames / GetByProfileName diff --git a/internal/service/token_service_test.go b/internal/service/token_service_test.go index c3c6e98..3e40a94 100644 --- a/internal/service/token_service_test.go +++ b/internal/service/token_service_test.go @@ -203,10 +203,9 @@ func TestTokenServiceImpl_Create(t *testing.T) { // 预置Profile testProfile := &model.Profile{ - UUID: "test-profile-uuid", - UserID: 1, - Name: "TestProfile", - IsActive: true, + UUID: "test-profile-uuid", + UserID: 1, + Name: "TestProfile", } _ = profileRepo.Create(context.Background(), testProfile) diff --git a/internal/types/common.go b/internal/types/common.go index 78e0fd3..eab20a8 100644 --- a/internal/types/common.go +++ b/internal/types/common.go @@ -35,7 +35,7 @@ type RegisterRequest struct { Username string `json:"username" binding:"required,min=3,max=50" example:"newuser"` Email string `json:"email" binding:"required,email" example:"user@example.com"` Password string `json:"password" binding:"required,min=6,max=128" example:"password123"` - VerificationCode string `json:"verification_code" binding:"required,len=6" example:"123456"` // 邮箱验证码 + VerificationCode string `json:"verification_code" binding:"required,len=6" example:"123456"` // 邮箱验证码 Avatar string `json:"avatar" binding:"omitempty,url" example:"https://rustfs.example.com/avatars/user_1/avatar.png"` // 可选,用户自定义头像 } @@ -158,7 +158,6 @@ type ProfileInfo struct { Name string `json:"name" example:"PlayerName"` SkinID *int64 `json:"skin_id,omitempty" example:"1"` CapeID *int64 `json:"cape_id,omitempty" example:"2"` - IsActive bool `json:"is_active" example:"true"` LastUsedAt *time.Time `json:"last_used_at,omitempty" example:"2025-10-01T12:00:00Z"` CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"` UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"` @@ -212,4 +211,13 @@ type SystemConfigResponse struct { RegistrationEnabled bool `json:"registration_enabled" example:"true"` MaxTexturesPerUser int `json:"max_textures_per_user" example:"100"` MaxProfilesPerUser int `json:"max_profiles_per_user" example:"5"` -} \ No newline at end of file +} + +// RenderResponse 材质渲染响应 +type RenderResponse struct { + URL string `json:"url" example:"https://rustfs.example.com/renders/xxx.png"` + ContentType string `json:"content_type" example:"image/png"` + ETag string `json:"etag,omitempty" example:"abc123def456"` + Size int64 `json:"size" example:"2048"` + LastModified *time.Time `json:"last_modified,omitempty" example:"2025-10-01T12:00:00Z"` +} diff --git a/pkg/database/cache.go b/pkg/database/cache.go index 62dfafe..582fa8a 100644 --- a/pkg/database/cache.go +++ b/pkg/database/cache.go @@ -306,6 +306,11 @@ func (b *CacheKeyBuilder) TextureList(userID int64, page int) string { return fmt.Sprintf("%stexture:user:%d:page:%d", b.prefix, userID, page) } +// TextureRender 构建材质渲染缓存键 +func (b *CacheKeyBuilder) TextureRender(textureID int64, renderType string, size int) string { + return fmt.Sprintf("%stexture:render:%d:%s:%d", b.prefix, textureID, renderType, size) +} + // Token 构建令牌缓存键 func (b *CacheKeyBuilder) Token(accessToken string) string { return fmt.Sprintf("%stoken:%s", b.prefix, accessToken) diff --git a/run.bat b/run.bat deleted file mode 100644 index 4b147a3..0000000 --- a/run.bat +++ /dev/null @@ -1,42 +0,0 @@ -@echo off -chcp 65001 >nul -echo ================================ -echo CarrotSkin Backend Server -echo ================================ -echo. - -echo [1/3] Checking swag tool... -where swag >nul 2>nul -if %ERRORLEVEL% NEQ 0 ( - echo [WARN] swag tool not found, installing... - go install github.com/swaggo/swag/cmd/swag@latest - if %ERRORLEVEL% NEQ 0 ( - echo [ERROR] Failed to install swag - echo Please install manually: go install github.com/swaggo/swag/cmd/swag@latest - pause - exit /b 1 - ) - echo [OK] swag tool installed -) else ( - echo [OK] swag tool found -) -echo. - -echo [2/3] Generating Swagger documentation... -swag init -g cmd/server/main.go -o docs --parseDependency --parseInternal -if %ERRORLEVEL% NEQ 0 ( - echo [ERROR] Failed to generate Swagger docs - pause - exit /b 1 -) -echo [OK] Swagger docs generated -echo. - -echo [3/3] Starting server... -echo Server: http://localhost:8080 -echo Swagger: http://localhost:8080/swagger/index.html -echo Health: http://localhost:8080/health -echo. -echo Press Ctrl+C to stop server -echo. -go run cmd/server/main.go \ No newline at end of file diff --git a/run.sh b/run.sh deleted file mode 100644 index ccd02c1..0000000 --- a/run.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -echo "================================" -echo " CarrotSkin Backend Server" -echo "================================" -echo "" - -echo "[1/3] 检查swag工具..." -if ! command -v swag &> /dev/null; then - echo "[警告] swag工具未安装,正在安装..." - go install github.com/swaggo/swag/cmd/swag@latest - if [ $? -ne 0 ]; then - echo "[错误] swag安装失败,请手动安装: go install github.com/swaggo/swag/cmd/swag@latest" - exit 1 - fi - echo "[成功] swag工具安装完成" -else - echo "[成功] swag工具已安装" -fi -echo "" - -echo "[2/3] 生成Swagger API文档..." -swag init -g cmd/server/main.go -o docs --parseDependency --parseInternal -if [ $? -ne 0 ]; then - echo "[错误] Swagger文档生成失败" - exit 1 -fi -echo "[成功] Swagger文档生成完成" -echo "" - -echo "[3/3] 启动服务器..." -echo "服务地址: http://localhost:8080" -echo "Swagger文档: http://localhost:8080/swagger/index.html" -echo "按 Ctrl+C 停止服务" -echo "" -go run cmd/server/main.go