-
Notifications
You must be signed in to change notification settings - Fork 23
我是帅哥 #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
我是帅哥 #16
Changes from 2 commits
0b467b5
a7db5a5
04df898
3d059f4
94a5fa1
36a1606
c4ce710
509d1fe
0e085a9
b878dbb
c063724
1aa7f2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 我做了更新 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| module QA | ||
|
|
||
| go 1.23.2 | ||
|
|
||
| require ( | ||
| filippo.io/edwards25519 v1.1.0 // indirect | ||
| github.com/bytedance/sonic v1.12.3 // indirect | ||
| github.com/bytedance/sonic/loader v0.2.0 // indirect | ||
| github.com/cloudwego/base64x v0.1.4 // indirect | ||
| github.com/cloudwego/iasm v0.2.0 // indirect | ||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | ||
| github.com/gabriel-vasile/mimetype v1.4.5 // indirect | ||
| github.com/gin-contrib/cors v1.7.2 // indirect | ||
| github.com/gin-contrib/sse v0.1.0 // indirect | ||
| github.com/gin-gonic/gin v1.10.0 // indirect | ||
| github.com/go-playground/locales v0.14.1 // indirect | ||
| github.com/go-playground/universal-translator v0.18.1 // indirect | ||
| github.com/go-playground/validator/v10 v10.22.1 // indirect | ||
| github.com/go-sql-driver/mysql v1.8.1 // indirect | ||
| github.com/goccy/go-json v0.10.3 // indirect | ||
| github.com/jinzhu/inflection v1.0.0 // indirect | ||
| github.com/jinzhu/now v1.1.5 // indirect | ||
| github.com/json-iterator/go v1.1.12 // indirect | ||
| github.com/klauspost/cpuid/v2 v2.2.8 // indirect | ||
| github.com/leodido/go-urn v1.4.0 // indirect | ||
| github.com/mattn/go-isatty v0.0.20 // indirect | ||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
| github.com/modern-go/reflect2 v1.0.2 // indirect | ||
| github.com/pelletier/go-toml/v2 v2.2.3 // indirect | ||
| github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||
| github.com/ugorji/go/codec v1.2.12 // indirect | ||
| golang.org/x/arch v0.10.0 // indirect | ||
| golang.org/x/crypto v0.27.0 // indirect | ||
| golang.org/x/net v0.29.0 // indirect | ||
| golang.org/x/sys v0.25.0 // indirect | ||
| golang.org/x/text v0.18.0 // indirect | ||
| google.golang.org/protobuf v1.34.2 // indirect | ||
| gopkg.in/yaml.v3 v3.0.1 // indirect | ||
| gorm.io/driver/mysql v1.5.7 // indirect | ||
| gorm.io/gorm v1.25.12 // indirect | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | ||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||
| github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= | ||
| github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= | ||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||
| github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= | ||
| github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||
| github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= | ||
| github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||
| github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= | ||
| github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||
| github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= | ||
| github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= | ||
| github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= | ||
| github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= | ||
| github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= | ||
| github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= | ||
| github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | ||
| github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | ||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||
| github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= | ||
| github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||
| github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | ||
| github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= | ||
| github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= | ||
| 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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||
| github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= | ||
| github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= | ||
| github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= | ||
| github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | ||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||
| github.com/klauspost/cpuid/v2 v2.0.9/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/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | ||
| github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||
| github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= | ||
| github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= | ||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
| github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= | ||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||
| golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= | ||
| golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||
| golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | ||
| golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | ||
| golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= | ||
| golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | ||
| 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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= | ||
| golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
| golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= | ||
| golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||
| google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||
| google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= | ||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
| gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= | ||
| gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= | ||
| gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= | ||
| gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= | ||
| gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= | ||
| nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= | ||
| rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,217 @@ | ||||||||||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||
| "crypto/rand" | ||||||||||||||||||||||||||||||||||||||||||
| "crypto/sha256" | ||||||||||||||||||||||||||||||||||||||||||
| "encoding/hex" | ||||||||||||||||||||||||||||||||||||||||||
| "github.com/gin-contrib/cors" | ||||||||||||||||||||||||||||||||||||||||||
| "github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||
| "gorm.io/driver/mysql" | ||||||||||||||||||||||||||||||||||||||||||
| "gorm.io/gorm" | ||||||||||||||||||||||||||||||||||||||||||
| "gorm.io/gorm/logger" | ||||||||||||||||||||||||||||||||||||||||||
| "net/http" | ||||||||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| type User struct { | ||||||||||||||||||||||||||||||||||||||||||
| Name string | ||||||||||||||||||||||||||||||||||||||||||
| ID string | ||||||||||||||||||||||||||||||||||||||||||
| Password string | ||||||||||||||||||||||||||||||||||||||||||
| Salt string // 用于存储盐 | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| type Question struct { | ||||||||||||||||||||||||||||||||||||||||||
| QuestionID int `json:"QuestionID" gorm:"primaryKey;AUTO_INCREMENT"` | ||||||||||||||||||||||||||||||||||||||||||
| ID string `json:"ID"` | ||||||||||||||||||||||||||||||||||||||||||
| Title string `json:"title"` | ||||||||||||||||||||||||||||||||||||||||||
| Content string `json:"content"` | ||||||||||||||||||||||||||||||||||||||||||
| Answers []Answer `json:"answers" gorm:"foreignKey:QuestionID"` | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| type Answer struct { | ||||||||||||||||||||||||||||||||||||||||||
| ID string `json:"ID"` | ||||||||||||||||||||||||||||||||||||||||||
| QuestionID int `json:"QuestionID"` | ||||||||||||||||||||||||||||||||||||||||||
| Content string `json:"content"` | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| type AIResponse struct { | ||||||||||||||||||||||||||||||||||||||||||
| Response string `json:"response"` | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // 生成随机盐 | ||||||||||||||||||||||||||||||||||||||||||
| func GenerateSalt() string { | ||||||||||||||||||||||||||||||||||||||||||
| bytes := make([]byte, 16) | ||||||||||||||||||||||||||||||||||||||||||
| if _, err := rand.Read(bytes); err != nil { | ||||||||||||||||||||||||||||||||||||||||||
| panic("Error generating salt") | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| return hex.EncodeToString(bytes) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // 哈希加盐 | ||||||||||||||||||||||||||||||||||||||||||
| func HashPassword(password, salt string) string { | ||||||||||||||||||||||||||||||||||||||||||
| saltedPassword := password + salt | ||||||||||||||||||||||||||||||||||||||||||
| hash := sha256.New() | ||||||||||||||||||||||||||||||||||||||||||
| hash.Write([]byte(saltedPassword)) | ||||||||||||||||||||||||||||||||||||||||||
| return hex.EncodeToString(hash.Sum(nil)) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| func HashPassword(password, salt string) string { | |
| saltedPassword := password + salt | |
| hash := sha256.New() | |
| hash.Write([]byte(saltedPassword)) | |
| return hex.EncodeToString(hash.Sum(nil)) | |
| } | |
| import ( | |
| "golang.org/x/crypto/bcrypt" | |
| // ... (other imports) | |
| ) | |
| func HashPassword(password string) (string, error) { | |
| hashedBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | |
| return string(hashedBytes), err | |
| } | |
| func CheckPasswordHash(password, hash string) bool { | |
| err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) | |
| return err == nil | |
| } |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate That Student IDs Are Numeric
Currently, the code checks only the length of the Student ID but doesn't verify if it contains only digits. This can lead to invalid IDs being accepted.
Apply this diff to validate the Student ID format:
+import (
+ ...
+ "regexp"
+)
func yes(c *gin.Context, u User) bool {
const (
...
)
if len(u.ID) != StudentIdLen {
c.JSON(http.StatusBadRequest, gin.H{"error": "请使用你本人的学号"})
return false
}
+ if !regexp.MustCompile(`^\d+$`).MatchString(u.ID) {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "学号只能包含数字"})
+ return false
+ }This ensures that only valid numeric Student IDs are accepted.
Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Errors Returned by c.BindJSON
Ignoring errors from c.BindJSON can lead to runtime exceptions if the JSON payload is malformed. It's important to handle these errors to ensure the robustness of your application.
Apply this diff to handle the error:
- c.BindJSON(&question)
+ if err := c.BindJSON(&question); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }Repeat similar changes for the following lines:
- Line 102 in
postAnswerfunction. - Line 176 in the
/hdu.wiki/registerroute. - Line 197 in the
/hdu.wiki/loginroute.
This ensures that the application responds appropriately to invalid input.
Also applies to: 102-102, 176-176, 197-197
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Associate Questions with the Authenticated User
In the postQuestion function, the question.ID field (which represents the user ID) should be set to the ID of the authenticated user. This ensures that each question is correctly associated with the user who posted it. Currently, this association is missing.
Apply this diff to set the question.ID to the authenticated user's ID:
func postQuestion(c *gin.Context) {
var question Question
c.BindJSON(&question)
+ userID, exists := c.Get("userID")
+ if !exists {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
+ return
+ }
+ question.ID = userID.(string)
if len(question.Title) == 0 || len(question.Title) > 10 {
c.JSON(http.StatusBadRequest, gin.H{
"message": "标题请设置在1-10字内",
})
return
}
if len(question.Content) < 10 || len(question.Content) > 100 {
c.JSON(http.StatusBadRequest, gin.H{"error": "问题内容需要控制在10-100字内"})
return
}
db2.Create(&question)
c.JSON(http.StatusCreated, question)
}Also, ensure that the route is protected with JWTMiddleware to enforce authentication:
-server.POST("/hdu.wiki/question", postQuestion)
+server.POST("/hdu.wiki/question", JWTMiddleware(), postQuestion)Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Associate Answers with the Authenticated User
In the postAnswer function, the answer.ID field (which represents the user ID) should be set to the ID of the authenticated user. This ensures that each answer is correctly associated with the user who posted it. Currently, this association is missing.
Apply this diff to set the answer.ID to the authenticated user's ID:
func postAnswer(c *gin.Context) {
var answer Answer
c.BindJSON(&answer)
+ userID, exists := c.Get("userID")
+ if !exists {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
+ return
+ }
+ answer.ID = userID.(string)
var existingquestion Question
res := db2.Where("question_id= ?", answer.QuestionID).First(&existingquestion)
if res.RowsAffected == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"message": "未找到该问题",
})
return
}
if len(answer.Content) == 0 || len(answer.Content) > 1000 {
c.JSON(http.StatusBadRequest, gin.H{
"message": "回答请设置在1-1000字内",
})
return
}
db2.Create(&answer)
c.JSON(http.StatusCreated, answer)
}Also, update the route to include JWTMiddleware:
-server.POST("/hdu.wiki/answers", postAnswer)
+server.POST("/hdu.wiki/answers", JWTMiddleware(), postAnswer)Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Implement Pagination in listQuestions to Improve Performance
Fetching all questions and preloading their answers without pagination can lead to performance issues as the data grows.
Consider adding pagination parameters to limit the number of records returned per request:
func listQuestions(c *gin.Context) {
var questions []Question
+ page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
+ pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
+ offset := (page - 1) * pageSize
- db2.Preload("Answers").Find(&questions)
+ db2.Preload("Answers").Limit(pageSize).Offset(offset).Find(&questions)
c.JSON(http.StatusOK, questions)
}Ensure to handle errors when parsing query parameters.
Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Errors When Parsing IDs from Route Parameters
Ignoring errors when converting route parameters to integers with strconv.Atoi can lead to unexpected behavior if the parameter is not a valid integer.
Apply this diff to handle the error:
- id, _ := strconv.Atoi(c.Param("id"))
+ idStr := c.Param("id")
+ id, err := strconv.Atoi(idStr)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
+ return
+ }Implement this change in both the deleteQuestion and deleteAnswer functions to ensure robust error handling.
Also applies to: 135-135
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restrict Question Deletion to the Owner
In the deleteQuestion function, any authenticated user can delete any question. To enhance security, only the user who posted the question should be allowed to delete it. Currently, this check is not implemented.
Apply this diff to verify question ownership before deletion:
func deleteQuestion(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
+ userID, exists := c.Get("userID")
+ if !exists {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
+ return
+ }
+ var question Question
+ result := db2.Where("question_id = ?", id).First(&question)
+ if result.RowsAffected == 0 {
+ c.JSON(http.StatusNotFound, gin.H{"message": "这个问题不存在!"})
+ return
+ }
+ if question.ID != userID.(string) {
+ c.JSON(http.StatusForbidden, gin.H{"error": "You are not authorized to delete this question"})
+ return
+ }
db2.Delete(&question)
c.JSON(http.StatusOK, gin.H{"message": "这个问题已被删除"})
}Also, ensure the route uses JWTMiddleware:
-server.DELETE("/hdu.wiki/questions/:id", deleteQuestion)
+server.DELETE("/hdu.wiki/questions/:id", JWTMiddleware(), deleteQuestion)Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restrict Answer Deletion to the Owner
In the deleteAnswer function, any authenticated user can delete any answer. To ensure security, only the user who posted the answer should be allowed to delete it. This ownership check is currently missing.
Apply this diff to verify answer ownership before deletion:
func deleteAnswer(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
+ userID, exists := c.Get("userID")
+ if !exists {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
+ return
+ }
+ var answer Answer
+ result := db2.Where("id = ?", id).First(&answer)
+ if result.RowsAffected == 0 {
+ c.JSON(http.StatusNotFound, gin.H{"error": "Answer not found"})
+ return
+ }
+ if answer.ID != userID.(string) {
+ c.JSON(http.StatusForbidden, gin.H{"error": "You are not authorized to delete this answer"})
+ return
+ }
db2.Delete(&answer)
c.JSON(http.StatusOK, gin.H{"message": "Answer deleted"})
}Update the route to include JWTMiddleware:
-server.DELETE("/hdu.wiki/answers/:id", deleteAnswer)
+server.DELETE("/hdu.wiki/answers/:id", JWTMiddleware(), deleteAnswer)Committable suggestion was skipped due to low confidence.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Empty Search Queries Appropriately
In the searchQuestions function, if the query parameter is empty, the function returns all questions. This might not be the intended behavior and could lead to performance issues as the data grows.
Consider returning an error when the search query is empty:
func searchQuestions(c *gin.Context) {
query := c.Query("query")
+ if strings.TrimSpace(query) == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Search query cannot be empty"})
+ return
+ }
var results []Question
db2.Where("title LIKE ? OR content LIKE ?", "%"+query+"%", "%"+query+"%").Find(&results)
c.JSON(http.StatusOK, results)
}This ensures that the client provides a valid search term.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func searchQuestions(c *gin.Context) { | |
| query := c.Query("query") | |
| var results []Question | |
| db2.Where("title LIKE ? OR content LIKE ?", "%"+query+"%", "%"+query+"%").Find(&results) | |
| c.JSON(http.StatusOK, results) | |
| } | |
| func searchQuestions(c *gin.Context) { | |
| query := c.Query("query") | |
| if strings.TrimSpace(query) == "" { | |
| c.JSON(http.StatusBadRequest, gin.H{"error": "Search query cannot be empty"}) | |
| return | |
| } | |
| var results []Question | |
| db2.Where("title LIKE ? OR content LIKE ?", "%"+query+"%", "%"+query+"%").Find(&results) | |
| c.JSON(http.StatusOK, results) | |
| } |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid Hardcoding Database Credentials
Hardcoding sensitive information like database usernames and passwords is a security risk. It's recommended to use environment variables or configuration files to manage sensitive data. This approach enhances security and makes it easier to manage different environments (development, testing, production).
Apply this diff to refactor the code:
-import (
+import (
...
+ "os"
)
...
- dsn1 := "root:123456@tcp(127.0.0.1:3306)/mydatabase?charset=utf8mb4&parseTime=True&loc=Local"
+ dsn1 := os.Getenv("DB_DSN1")
db1, err := gorm.Open(mysql.Open(dsn1), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
...
- dsn2 := "root:123456@tcp(127.0.0.1:3306)/mydatabase?charset=utf8mb4&parseTime=True&loc=Local"
+ dsn2 := os.Getenv("DB_DSN2")
db2, err = gorm.Open(mysql.Open(dsn2), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})Ensure that you set the environment variables DB_DSN1 and DB_DSN2 appropriately in your deployment environment.
Also applies to: 163-164
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Combine Database Connections to Optimize Resource Usage
Both db1 and db2 are connecting to the same database using identical DSNs. Maintaining two separate connections is unnecessary and could lead to increased resource usage. Consider using a single database connection for both user management and question-answer management.
Apply this diff to use a single database connection:
-var db1 *gorm.DB
-var db2 *gorm.DB
+var db *gorm.DB
var err error
func main() {
- dsn1 := os.Getenv("DB_DSN1")
- db1, err := gorm.Open(mysql.Open(dsn1), &gorm.Config{
+ dsn := os.Getenv("DB_DSN")
+ db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
panic("failed to connect database")
}
- db1.AutoMigrate(&User{})
-
- dsn2 := os.Getenv("DB_DSN2")
- db2, err = gorm.Open(mysql.Open(dsn2), &gorm.Config{
- Logger: logger.Default.LogMode(logger.Info),
- })
- if err != nil {
- panic("failed to connect database")
- }
- db2.AutoMigrate(&Question{}, &Answer{})
+ db.AutoMigrate(&User{}, &Question{}, &Answer{})Update all references from db1 and db2 to db throughout your code:
- res := db1.Where("id = ?", u.ID).First(&existingUser)
+ res := db.Where("id = ?", u.ID).First(&existingUser)
- db2.Create(&question)
+ db.Create(&question)This change simplifies your code and reduces overhead.
Also applies to: 163-169
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement Authentication and Authorization for Protected Routes
All users can currently access protected routes like posting and deleting questions or answers without any authentication. This poses a significant security risk.
[security]
Consider implementing authentication mechanisms (e.g., JWT tokens or session-based authentication) and middleware to protect these routes. Only authenticated users should be able to perform actions like posting or deleting content.
Also applies to: 84-99, 100-119, 125-133, 134-142
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalid Go version specified
The Go version specified (1.23.2) is not a valid Go version. As of October 2024, the latest stable version is 1.21.x.
Please update the Go version to a valid and stable version. For example:
This will ensure compatibility with the Go toolchain and avoid potential issues.
📝 Committable suggestion