-
Notifications
You must be signed in to change notification settings - Fork 26
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
Feat:finish task #7
base: main
Are you sure you want to change the base?
Changes from all commits
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,27 @@ | ||
package admin | ||
|
||
import ( | ||
"github.com/gin-contrib/sessions" | ||
"github.com/gin-gonic/gin" | ||
"net/http" | ||
"sh4ll0t/db" | ||
) | ||
|
||
func Admin(c *gin.Context) { | ||
session := sessions.Default(c) | ||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||
return | ||
} | ||
if session.Get("username") != "admin" { | ||
c.JSON(http.StatusUnauthorized, gin.H{"error": "管理员才可以访问!"}) | ||
return | ||
} | ||
var questions []db.Question | ||
if err := db.DB.Preload("Answers").Find(&questions).Error; err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, questions) | ||
|
||
} | ||
Comment on lines
+25
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance response structure with metadata. The current response only includes the questions data. To improve the API, consider adding metadata to the response. This could include:
Here's a suggested improvement for the response structure: c.JSON(http.StatusOK, gin.H{
"data": questions,
"meta": gin.H{
"total": total,
"page": page,
"pageSize": pageSize,
"totalPages": int(math.Ceil(float64(total) / float64(pageSize))),
},
}) This structure provides more context to the client about the data being returned and allows for easier implementation of pagination on the frontend. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package admin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"gorm.io/gorm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"sh4ll0t/db" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func CheckAnswer(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
session := sessions.Default(c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if session.Get("username") != "admin" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "管理员才可以访问!"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Role-Based Access Control (RBAC) for Admin Verification Currently, the code checks if Apply this diff to improve admin verification: if auth, ok := session.Get("authenticated").(bool); !ok || !auth {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
-if session.Get("username") != "admin" {
+if session.Get("role") != "admin" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "管理员才可以访问!"})
return
} Ensure that the user's role is set during authentication: // After successful authentication
-session.Set("username", user.Username)
+session.Set("role", user.Role) // Assume 'Role' is a field in your user model
session.Save()
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
idStr := c.PostForm("id") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
checkStatusStr := c.PostForm("check") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
checkStatus, err := strconv.Atoi(checkStatusStr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的审核状态"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+25
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate 'checkStatus' Value Before Updating Currently, If checkStatus, err := strconv.Atoi(checkStatusStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的审核状态"})
return
}
+if checkStatus != 0 && checkStatus != 1 {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "审核状态只能是0或1"})
+ return
+} 📝 Committable suggestion
Suggested change
Comment on lines
+23
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate the 'id' Parameter Before Database Query The Apply this diff to validate and convert idStr := c.PostForm("id")
+id, err := strconv.Atoi(idStr)
+if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
+ return
+}
checkStatusStr := c.PostForm("check")
checkStatus, err := strconv.Atoi(checkStatusStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的审核状态"})
return
} Update the database query to use the integer var answer db.Answer
-if err := db.DB.First(&answer, idStr).Error; err != nil {
+if err := db.DB.First(&answer, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "ID 不存在"})
} else { 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var answer db.Answer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err := db.DB.First(&answer, idStr).Error; err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err == gorm.ErrRecordNotFound { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusNotFound, gin.H{"error": "ID 不存在"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid Exposing Internal Error Messages to Users Returning Apply this diff: } else {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ // Log the error details
+ // log.Errorf("Database error: %v", err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
} Ensure you have a logging mechanism in place to capture the error details for debugging purposes.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
answer.CheckStatus = checkStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err := db.DB.Save(&answer).Error; err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "审核状态更新成功"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||||||||||||||||||
package admin | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||
"gorm.io/gorm" | ||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||
"sh4ll0t/db" | ||||||||||||||||||||||||
"strconv" | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
func CheckQuestion(c *gin.Context) { | ||||||||||||||||||||||||
session := sessions.Default(c) | ||||||||||||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if session.Get("username") != "admin" { | ||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "管理员才可以访问!"}) | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
checkStatusStr := c.PostForm("check") | ||||||||||||||||||||||||
checkStatus, err := strconv.Atoi(checkStatusStr) | ||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的审核状态"}) | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+23
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate After converting For example, if valid if checkStatus != 0 && checkStatus != 1 {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的审核状态"})
return
} |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
idStr := c.PostForm("id") | ||||||||||||||||||||||||
var question db.Question | ||||||||||||||||||||||||
if err := db.DB.First(&question, idStr).Error; err != nil { | ||||||||||||||||||||||||
Comment on lines
+30
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parse and validate the Parsing Modify the code as follows: idStr := c.PostForm("id")
+id, err := strconv.Atoi(idStr)
+if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
+ return
+}
var question db.Question
-if err := db.DB.First(&question, idStr).Error; err != nil {
+if err := db.DB.First(&question, id).Error; err != nil { 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
if err == gorm.ErrRecordNotFound { | ||||||||||||||||||||||||
c.JSON(http.StatusNotFound, gin.H{"error": "ID 不存在"}) | ||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid exposing internal error messages to clients Returning internal error details with Apply this diff to return a generic error message: if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "ID 不存在"})
} else {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
} Similarly, update the error handling in line 42: if err := db.DB.Save(&question).Error; err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
return
} Also applies to: 42-42 |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
question.CheckStatus = checkStatus | ||||||||||||||||||||||||
if err := db.DB.Save(&question).Error; err != nil { | ||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "审核状态更新成功"}) | ||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package answer | ||
|
||
import ( | ||
"github.com/gin-contrib/sessions" | ||
"github.com/gin-gonic/gin" | ||
"net/http" | ||
"sh4ll0t/db" | ||
"strconv" | ||
) | ||
|
||
func Answer(c *gin.Context) { | ||
session := sessions.Default(c) | ||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||
return | ||
} | ||
respondent := session.Get("username").(string) | ||
Comment on lines
+11
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider enhancing error handling and authentication check. The function signature and basic authentication check look good. However, consider the following improvements:
Example of using middleware: func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
session := sessions.Default(c)
if auth, ok := session.Get("authenticated").(bool); !ok || !auth {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized", "code": "AUTH_REQUIRED"})
return
}
c.Next()
}
}
// In your router setup:
// r.POST("/answer", AuthMiddleware(), Answer) This approach would simplify your |
||
idStr := c.PostForm("id") | ||
answerText := c.PostForm("answer") | ||
questionID, err := strconv.ParseUint(idStr, 10, 32) | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的问题 ID"}) | ||
return | ||
} | ||
|
||
answer := db.Answer{ | ||
QuestionID: uint(questionID), | ||
AnswerText: answerText, | ||
Respondent: respondent, | ||
} | ||
|
||
if err := db.DB.Create(&answer).Error; err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "回答失败"}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, gin.H{"message": "回答成功"}) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,36 @@ | ||||||||||||||||||||||||||||||||||||
package answer | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||
"sh4ll0t/db" | ||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
func ChangeAnswer(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||
session := sessions.Default(c) | ||||||||||||||||||||||||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
id := c.PostForm("id") | ||||||||||||||||||||||||||||||||||||
newAnswerText := c.PostForm("Answer") | ||||||||||||||||||||||||||||||||||||
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate input parameters to prevent empty or invalid data Currently, the Consider adding validation to ensure that id := c.PostForm("id")
newAnswerText := c.PostForm("Answer")
+if id == "" || newAnswerText == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "ID and Answer cannot be empty"})
+ return
+} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
var answer db.Answer | ||||||||||||||||||||||||||||||||||||
if err := db.DB.First(&answer, id).Error; err != nil { | ||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid exposing internal error messages Returning internal error details ( Update the error response: - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"}) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+19
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return 404 Not Found when the answer does not exist When the specified answer is not found in the database, the current code returns a 500 Internal Server Error. It's more appropriate to return a 404 Not Found status to indicate that the resource doesn't exist. Modify the error handling to check for if err := db.DB.First(&answer, id).Error; err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ c.JSON(http.StatusNotFound, gin.H{"error": "Answer not found"})
+ } else {
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
+ }
return
} Make sure to import the import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"hduhelp_text/db"
"net/http"
+ "errors"
) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
if session.Get("username") != answer.Respondent { | ||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "只有本作者才可以修改!"}) | ||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use proper HTTP status code for authorization failure When the user is authenticated but not authorized to modify the answer, it's appropriate to return Adjust the status code: if session.Get("username") != answer.Respondent {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "只有本作者才可以修改!"})
+ c.JSON(http.StatusForbidden, gin.H{"error": "只有本作者才可以修改!"})
return
} 📝 Committable suggestion
Suggested change
🛠️ Refactor suggestion Perform type assertion when retrieving session data When retrieving Modify the code to include a type assertion: -if session.Get("username") != answer.Respondent {
+username, ok := session.Get("username").(string)
+if !ok || username != answer.Respondent {
c.JSON(http.StatusForbidden, gin.H{"error": "只有本作者才可以修改!"})
return
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
answer.AnswerText = newAnswerText | ||||||||||||||||||||||||||||||||||||
if err := db.DB.Save(&answer).Error; err != nil { | ||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+30
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid exposing internal error messages on save failure Similar to the previous error handling, avoid sending internal error details when saving to the database fails. Update the error response: if err := db.DB.Save(&answer).Error; err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update the answer"})
return
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "修改成功"}) | ||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,31 @@ | ||||||||||||||
package answer | ||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||
"net/http" | ||||||||||||||
"sh4ll0t/db" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
func DeleteAnswer(c *gin.Context) { | ||||||||||||||
session := sessions.Default(c) | ||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||
return | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
id := c.PostForm("id") | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate the 'id' parameter to prevent unexpected behavior The 'id' parameter retrieved from the form data is not validated. If 'id' is empty or invalid, it could lead to unintended errors. Apply this diff to add input validation: id := c.PostForm("id")
+if id == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "缺少参数id"})
+ return
+} This ensures that the 'id' parameter is provided before proceeding. 📝 Committable suggestion
Suggested change
|
||||||||||||||
var answer db.Answer | ||||||||||||||
if err := db.DB.First(&answer, id).Error; err != nil { | ||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||
return | ||||||||||||||
} | ||||||||||||||
if session.Get("username") != answer.Respondent { | ||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "只有本作者才可以删除!"}) | ||||||||||||||
return | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
db.DB.Delete(&db.Answer{}, "id = ?", id) | ||||||||||||||
db.DB.Delete(&answer) | ||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider implementing internationalization for response messages To enhance user experience for a broader audience, consider supporting multiple languages by implementing internationalization for response messages. This will make the application more adaptable and user-friendly in different locales. |
||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package answer | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"net/http" | ||
"sh4ll0t/db" | ||
) | ||
|
||
func SearchAnswer(c *gin.Context) { | ||
answer := c.PostForm("answer") | ||
var answers []db.Answer | ||
var questions []db.Question | ||
err := db.DB.Where("answer_text LIKE ? AND `check` = ?", "%"+answer+"%", 1).Find(&answers).Error | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err}) | ||
return | ||
} | ||
if len(answers) == 0 { | ||
c.JSON(http.StatusNotFound, gin.H{"message": "未查询到相关答案"}) | ||
return | ||
} | ||
err = db.DB.Preload("Answers").Where("id IN ?", getQuestionIDs(answers)).Find(&questions).Error | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, questions) | ||
} | ||
Comment on lines
+9
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve security, error handling, and performance in SearchAnswer function. While the overall structure is good, there are several areas for improvement:
Here's a suggested refactor: func SearchAnswer(c *gin.Context) {
answer := c.PostForm("answer")
var answers []db.Answer
var questions []db.Question
// Use parameterized query to prevent SQL injection
err := db.DB.Where("answer_text LIKE ? AND `check` = ?", "%"+answer+"%", 1).Limit(100).Find(&answers).Error
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
return
}
if len(answers) == 0 {
c.JSON(http.StatusNotFound, gin.H{"message": "未查询到相关答案"})
return
}
err = db.DB.Preload("Answers").Where("id IN ?", getQuestionIDs(answers)).Limit(100).Find(&questions).Error
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
return
}
if len(questions) == 0 {
c.JSON(http.StatusNotFound, gin.H{"message": "未查询到相关问题"})
return
}
c.JSON(http.StatusOK, questions)
} This refactored version addresses the identified issues and improves the overall robustness of the function. |
||
|
||
func getQuestionIDs(answers []db.Answer) []uint { | ||
var ids []uint | ||
for _, answer := range answers { | ||
ids = append(ids, answer.QuestionID) | ||
} | ||
return ids | ||
} | ||
Comment on lines
+31
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize getQuestionIDs function for better performance. The function correctly extracts question IDs, but its efficiency can be improved by pre-allocating the slice. Consider this optimized version: func getQuestionIDs(answers []db.Answer) []uint {
ids := make([]uint, 0, len(answers))
for _, answer := range answers {
ids = append(ids, answer.QuestionID)
}
return ids
} This change pre-allocates the slice with the expected capacity, reducing the number of potential memory allocations and improving performance, especially for larger datasets. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,58 @@ | ||||||||||||||||||
package question | ||||||||||||||||||
|
||||||||||||||||||
import ( | ||||||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||
"net/http" | ||||||||||||||||||
"sh4ll0t/db" | ||||||||||||||||||
"sh4ll0t/utils" | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
func Ask(c *gin.Context) { | ||||||||||||||||||
session := sessions.Default(c) | ||||||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
username, ok := session.Get("username").(string) | ||||||||||||||||||
if !ok || username == "" { | ||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名不存在"}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
questionText := c.PostForm("question") | ||||||||||||||||||
if questionText == "" { | ||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "问题内容不能为空"}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+24
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add input validation and sanitization for While checking if |
||||||||||||||||||
|
||||||||||||||||||
newQuestion := db.Question{ | ||||||||||||||||||
QuestionText: questionText, | ||||||||||||||||||
Questioner: username, | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if err := db.DB.Create(&newQuestion).Error; err != nil { | ||||||||||||||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+35
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid exposing internal error details and use appropriate HTTP status codes Returning Apply this diff to address the issues: if err := db.DB.Create(&newQuestion).Error; err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
return
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
answerText, err := utils.GenerateAIAnswer(questionText) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成答案"}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+40
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider processing AI answer generation asynchronously to improve performance Generating the AI answer synchronously within the request handler can lead to increased response times and potential timeouts, especially if the AI service experiences delays. Consider offloading the AI answer generation to a background worker or message queue, and immediately respond to the user indicating that their question has been received and will be answered shortly. |
||||||||||||||||||
|
||||||||||||||||||
answer := db.Answer{ | ||||||||||||||||||
QuestionID: newQuestion.ID, | ||||||||||||||||||
AnswerText: answerText, | ||||||||||||||||||
Respondent: "ai", | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if err := db.DB.Create(&answer).Error; err != nil { | ||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "答案保存失败"}) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "提问成功"}) | ||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,38 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package question | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-contrib/sessions" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"sh4ll0t/db" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func ChangeQuestion(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
session := sessions.Default(c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if auth, ok := session.Get("authenticated").(bool); !ok || !auth { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+10
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding logging for unauthorized access attempts. The authentication check is implemented correctly. However, to enhance security monitoring, consider adding logging for unauthorized access attempts. Here's a suggested modification: func ChangeQuestion(c *gin.Context) {
session := sessions.Default(c)
if auth, ok := session.Get("authenticated").(bool); !ok || !auth {
+ log.Printf("Unauthorized access attempt to ChangeQuestion")
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
} Don't forget to import the "log" package if it's not already imported. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id := c.PostForm("id") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
questionText := c.PostForm("question") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var existingQuestion db.Question | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err := db.DB.First(&existingQuestion, "id = ?", id).Error; err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if session.Get("username") != existingQuestion.Questioner { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "只有本作者才可以修改!"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+17
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add input validation for question ID and text. While the question retrieval and authorization check are implemented correctly, there's no validation of the input parameters. This could potentially lead to security vulnerabilities. Consider adding input validation: id := c.PostForm("id")
questionText := c.PostForm("question")
+// Validate id
+if id == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Question ID is required"})
+ return
+}
+
+// Validate questionText
+if len(questionText) < 10 || len(questionText) > 500 {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Question text must be between 10 and 500 characters"})
+ return
+}
var existingQuestion db.Question
if err := db.DB.First(&existingQuestion, "id = ?", id).Error; err != nil { Adjust the length constraints for 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
existingQuestion.QuestionText = questionText | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err := db.DB.Save(&existingQuestion).Error; err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+31
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize to avoid unnecessary database writes. The question update process is correct, but it doesn't check if the new question text is different from the existing one. This could lead to unnecessary database writes. Consider adding a check before updating: +if existingQuestion.QuestionText != questionText {
existingQuestion.QuestionText = questionText
if err := db.DB.Save(&existingQuestion).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
+} else {
+ c.JSON(http.StatusOK, gin.H{"message": "No changes made"})
+ return
+} This will avoid unnecessary database operations if the question text hasn't changed. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "修改成功"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+37
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider returning updated question data with success message. While the success response is appropriate, it might be more informative to return the updated question data along with the success message. Consider modifying the response: -c.JSON(http.StatusOK, gin.H{"message": "修改成功"})
+c.JSON(http.StatusOK, gin.H{
+ "message": "修改成功",
+ "question": gin.H{
+ "id": existingQuestion.ID,
+ "questionText": existingQuestion.QuestionText,
+ "questioner": existingQuestion.Questioner,
+ // Add other relevant fields
+ },
+}) This provides more context to the client about the updated question.
|
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
Enhance authentication and authorization checks.
While the current implementation is functional, consider the following improvements:
Here's a suggested refactoring:
This refactoring improves code organization and allows for easier expansion of the admin check logic in the future.