Skip to content
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 #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

feat: finish task #6

wants to merge 3 commits into from

Conversation

AAAxianyu
Copy link

@AAAxianyu AAAxianyu commented Oct 2, 2024

feat: finish task

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive API documentation for user, question, and answer management functionalities.
    • Added a new interactive HTML webpage with dynamic image functionality.
    • Implemented a web application with user registration, login, and question/answer management using the Gin framework and GORM.
    • Added search functionality for questions and answers.
  • Bug Fixes

    • Included content filtering to remove forbidden words from user inputs.
  • Documentation

    • New Go module file created to manage project dependencies.

Copy link

coderabbitai bot commented Oct 2, 2024

Walkthrough

A comprehensive API documentation has been introduced for the AAAxianyu platform, detailing functionalities related to user, question, answer, best answer management, and keyword search. Additionally, a new Go module has been created for the project at github.com/AAAxianyu/website, specifying Go version 1.23.0 and listing various indirect dependencies. An HTML file has also been added, providing an interactive webpage with dynamic image changes through JavaScript. Furthermore, a main.go file implements a web application using the Gin framework and GORM for database interactions.

Changes

File Path Change Summary
.github/AAAxianyu/API 文档 New API documentation created detailing user, question, answer, best answer management, and keyword search functionalities.
.github/AAAxianyu/go.mod New Go module file created with module name and Go version specified; multiple indirect dependencies added.
.github/AAAxianyu/hello.txt New HTML file created featuring an interactive webpage with dynamic image functionality.
.github/AAAxianyu/main.go New Go file implementing a web application with user and question management using Gin and GORM.

Possibly related PRs

  • feat: finish task #1: The introduction of a new Go module in both the main PR and the retrieved PR indicates a similar focus on setting up Go modules for dependency management, suggesting a related context in project structure.

Poem

In a world of code, a rabbit hops,
With modules and functions, the excitement never stops!
A webpage to greet, with colors so bright,
Dynamic images dance, what a joyful sight!
With Gin and GORM, our data flows free,
Oh, what a wonder this code can be! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 249892d and 316dc72.

📒 Files selected for processing (1)
  • .github/AAAxianyu/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/AAAxianyu/main.go

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Outside diff range and nitpick comments (2)
.github/AAAxianyu/hello.txt (1)

1-12: Implement web development best practices

While your code creates a functional interactive webpage, there are several areas where it could be improved to align with modern web development best practices:

  1. Separation of Concerns: Separate HTML, CSS, and JavaScript into different files.
  2. Error Handling: Add fallbacks for image loading errors.
  3. Coding Style: Adopt a consistent coding style (e.g., indentation, naming conventions).
  4. Accessibility: Ensure all interactive elements are keyboard accessible.
  5. Performance: Optimize image sizes and use appropriate formats.

Consider restructuring your project as follows:

project/
├── index.html
├── styles/
│   └── main.css
├── scripts/
│   └── main.js
└── images/
    ├── welcome.jpg
    └── surprise.gif

This structure separates concerns and makes your project more maintainable and scalable.

Would you like assistance in implementing these best practices or creating a more robust structure for your project?

.github/AAAxianyu/main.go (1)

67-70: Handle Errors Returned by Functions

In the sayHello function, errors returned by ioutil.ReadFile and fmt.Fprint are being ignored. Ignoring errors can lead to unexpected behavior and makes debugging difficult. It's important to handle these errors appropriately to ensure the application can respond to failures gracefully.

Apply this diff to handle the errors:

 func sayHello(w http.ResponseWriter, r *http.Request) {
-    b, _ := ioutil.ReadFile("./hello.txt")
-    _, _ = fmt.Fprint(w, string(b))
+    b, err := ioutil.ReadFile("./hello.txt")
+    if err != nil {
+        http.Error(w, "Failed to read file", http.StatusInternalServerError)
+        return
+    }
+    if _, err := fmt.Fprint(w, string(b)); err != nil {
+        http.Error(w, "Failed to write response", http.StatusInternalServerError)
+    }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2102ded and 59f96f8.

⛔ Files ignored due to path filters (1)
  • .github/AAAxianyu/go.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • .github/AAAxianyu/go.mod (1 hunks)
  • .github/AAAxianyu/hello.txt (1 hunks)
  • .github/AAAxianyu/main.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .github/AAAxianyu/go.mod

Comment on lines +1 to +12
<h1 style='color:orange'>Hello!Welcome to hdu!</h1>
<h2>Do you have any questions?</h2>
<img id='i1' src='https://p1.ssl.qhimg.com/t01892c550190fcd062.jpg'>
<h3>作者实在太懒了 所以请自己输入路由叭!!!</h3>
<h4>悄咪咪告诉你 点击按钮有惊喜~~</h4>
<button id='b1'>点我</button>
<script>
document.getElementById('b1').onclick=function(){
document.getElementById('i1').src='https://imgs.qiubiaoqing.com/qiubiaoqing/imgs/607d46d2ad735lWT.gif'
}

</script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve HTML structure and file extension

The current file has several structural issues:

  1. The file extension is .txt, which is incorrect for HTML content.
  2. The HTML lacks proper structure (missing <!DOCTYPE>, , , and tags).

These issues can lead to rendering problems and inconsistencies across browsers.

Please make the following changes:

  1. Rename the file to have a .html extension.
  2. Add the necessary HTML structure. Here's an example of how the beginning of your file should look:
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome to HDU</title>
</head>
<body>
    <!-- Your existing content goes here -->
</body>
</html>

Comment on lines +1 to +5
<h1 style='color:orange'>Hello!Welcome to hdu!</h1>
<h2>Do you have any questions?</h2>
<img id='i1' src='https://p1.ssl.qhimg.com/t01892c550190fcd062.jpg'>
<h3>作者实在太懒了 所以请自己输入路由叭!!!</h3>
<h4>悄咪咪告诉你 点击按钮有惊喜~~</h4>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance HTML content, styling, and accessibility

There are several areas for improvement in the HTML content:

  1. Inline styling is used, which is generally discouraged.
  2. Chinese text is used without proper language declaration.
  3. The image lacks alt text, which is important for accessibility.

Please consider the following improvements:

  1. Move styles to a separate CSS file or a <style> tag in the .
  2. Add a lang attribute to the tag (e.g., <html lang="zh">).
  3. Add an alt attribute to the image.

Here's an example of how you could improve the HTML:

<h1>Hello! Welcome to HDU!</h1>
<h2>Do you have any questions?</h2>
<img id="i1" src="https://p1.ssl.qhimg.com/t01892c550190fcd062.jpg" alt="Welcome image">
<h3>作者实在太懒了 所以请自己输入路由叭!!!</h3>
<h4>悄咪咪告诉你 点击按钮有惊喜~~</h4>

And add this to your or a separate CSS file:

<style>
    h1 { color: orange; }
</style>

Comment on lines +6 to +11
<button id='b1'>点我</button>
<script>
document.getElementById('b1').onclick=function(){
document.getElementById('i1').src='https://imgs.qiubiaoqing.com/qiubiaoqing/imgs/607d46d2ad735lWT.gif'
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve JavaScript code and event handling

The current implementation uses an inline event handler in the script tag, which is not considered best practice in modern web development.

Consider the following improvements:

  1. Move the JavaScript to a separate file for better organization.
  2. Use addEventListener instead of inline event handlers.
  3. Consider using const for variables that won't be reassigned.

Here's an improved version of your JavaScript:

document.addEventListener('DOMContentLoaded', () => {
    const button = document.getElementById('b1');
    const image = document.getElementById('i1');
    
    button.addEventListener('click', () => {
        image.src = 'https://imgs.qiubiaoqing.com/qiubiaoqing/imgs/607d46d2ad735lWT.gif';
    });
});

This code should be placed in a separate .js file and linked in your HTML like this:

<script src="your-script-file.js"></script>

Comment on lines 27 to 31
type User struct {
gorm.Model
Username string `gorm:"uniqueIndex;not null" json:"username"`
Password string `gorm:"not null" json:"password"`
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent and Redundant ID Field Declarations

In the User, Question, and Answer structs, there is an inconsistency in how the ID field is declared. The User struct embeds gorm.Model, which already includes an ID field of type uint. However, the Question and Answer structs both embed gorm.Model and also declare an ID field explicitly.

This redundancy can lead to conflicts and unexpected behavior in ORM operations. It's recommended to rely solely on gorm.Model for the ID field or, if a custom ID is necessary, avoid embedding gorm.Model and define required fields explicitly.

Apply this diff to remove the redundant ID fields:

 type Question struct {
     gorm.Model
-    ID         int    `gorm:"primarykey" json:"id"`
     Content    string `gorm:"not null" json:"content"`
     BestAnswer int    `gorm:"default:null" json:"best_answer"` // 最佳答案的ID
 }

 type Answer struct {
     gorm.Model
-    ID         int    `gorm:"primarykey" json:"id"`
     Content    string `gorm:"not null" json:"content"`
     QuestionID int    `gorm:"not null" json:"question_id"`
     Votes      int    `gorm:"default:0" json:"votes"` // 被选择的次数
 }

Also applies to: 34-39, 42-48


⚠️ Potential issue

Store Passwords Securely Using Hashing

Currently, passwords are stored in plaintext in the database and compared directly during login. This poses a significant security risk, as it exposes user passwords to anyone who can access the database. If the database is compromised, user accounts on other platforms could also be at risk if users reuse passwords.

It's recommended to hash passwords using a secure hashing algorithm like bcrypt before storing them. During login, compare the hashed password with the stored hash to authenticate the user.

Apply this diff to incorporate password hashing:

+import (
+    "golang.org/x/crypto/bcrypt"
+    // other imports
+)

 // User struct
 type User struct {
     gorm.Model
     Username string `gorm:"uniqueIndex;not null" json:"username"`
-    Password string `gorm:"not null" json:"password"`
+    Password string `gorm:"not null"` // Exclude from JSON to avoid exposure
 }

 // User registration
 r.POST("/register", func(c *gin.Context) {
     var newUser User
     if err := c.ShouldBindJSON(&newUser); err != nil {
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
     }
+    // Hash the password
+    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newUser.Password), bcrypt.DefaultCost)
+    if err != nil {
+        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
+        return
+    }
+    newUser.Password = string(hashedPassword)
     result := db.Create(&newUser)
     if result.Error != nil {
         c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
         return
     }
     c.JSON(http.StatusOK, gin.H{"message": "User registered successfully!"})
 })

 // User login
 r.POST("/login", func(c *gin.Context) {
     var loginUser User
     if err := c.ShouldBindJSON(&loginUser); err != nil {
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
     }
     var user User
-    result := db.Where("username = ? AND password = ?", loginUser.Username, loginUser.Password).First(&user)
+    result := db.Where("username = ?", loginUser.Username).First(&user)
     if result.Error != nil || result.RowsAffected == 0 {
         c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
         return
     }
+    // Compare the hashed password
+    if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginUser.Password)); err != nil {
+        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
+        return
+    }
     c.JSON(http.StatusOK, gin.H{"message": "Login successful!"})
 })

Also applies to: 98-110, 113-126

Comment on lines 72 to 92
func main() {
db, err := initDB()
if err != nil {
panic("Failed to connect database")
}
http.HandleFunc("/hello", sayHello)
err = http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("http serve failed,err:%v\n", err)
return
}
defer func() {
sqlDB, err := db.DB()
if err != nil {
log.Println("Failed to get SQL DB handle:", err)
}
if err := sqlDB.Close(); err != nil {
log.Println("Failed to close database connection:", err)
}
}()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Conflict in Starting Multiple HTTP Servers on the Same Port

The application attempts to start an HTTP server using the standard library with http.ListenAndServe(":8080", nil) (line 78~) and later starts a Gin server with r.Run(":8080") (line 326~), both on port 8080. This will cause a runtime error because only one server can listen on a given port at a time.

Additionally, since http.ListenAndServe is blocking, any code after it (including the Gin server setup) will not execute until that server stops. This means your Gin routes will never be set up, and the application won't function as intended.

To resolve this, consider serving the /hello endpoint using the Gin router instead of starting a separate HTTP server. Here's how you can modify the code:

 func main() {
     db, err := initDB()
     if err != nil {
         panic("Failed to connect database")
     }
-    http.HandleFunc("/hello", sayHello)
-    err = http.ListenAndServe(":8080", nil)
-    if err != nil {
-        fmt.Println("http serve failed,err:%v\n", err)
-        return
-    }
 
     defer func() {
         sqlDB, err := db.DB()
         if err != nil {
             log.Println("Failed to get SQL DB handle:", err)
         }
         if err := sqlDB.Close(); err != nil {
             log.Println("Failed to close database connection:", err)
         }
     }()
 
     migrateDB(db)
 
-    r := gin.Default()
+    r := gin.Default()
+
+    // Serve the /hello endpoint using Gin
+    r.GET("/hello", func(c *gin.Context) {
+        b, err := ioutil.ReadFile("./hello.txt")
+        if err != nil {
+            c.String(http.StatusInternalServerError, "Failed to read file")
+            return
+        }
+        c.String(http.StatusOK, string(b))
+    })
 
     // ... [All other route handlers remain the same] ...
 
     if err := r.Run(":8080"); err != nil {
         log.Fatalf("Failed to start server: %v", err)
     }
 }

This way, all routes are handled by the Gin router, and only one server is started on port 8080.

Also applies to: 95-329

Comment on lines +17 to +24
func initDB() (*gorm.DB, error) {
dsn := "root:LTY060224@tcp(127.0.0.1:23306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
return db, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid Hardcoding Sensitive Information in Source Code

Hardcoding database credentials, such as usernames, passwords, and connection details, directly into the source code is a significant security risk. It exposes sensitive information and can lead to security breaches if the codebase is compromised. It's recommended to store such credentials securely, for example, using environment variables or a configuration file that is not committed to version control.

Apply this diff to securely manage your database credentials:

+import (
+    "os"
+    // other imports
+)
 func initDB() (*gorm.DB, error) {
-    dsn := "root:LTY060224@tcp(127.0.0.1:23306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
+    dsn := os.Getenv("DATABASE_DSN")
     db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
     if err != nil {
         return nil, err
     }
     return db, nil
 }

Make sure to set the DATABASE_DSN environment variable in your deployment environment with the appropriate database connection string.

📝 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.

Suggested change
func initDB() (*gorm.DB, error) {
dsn := "root:LTY060224@tcp(127.0.0.1:23306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
return db, nil
}
import (
"os"
// other imports
)
func initDB() (*gorm.DB, error) {
dsn := os.Getenv("DATABASE_DSN")
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
return db, nil
}

Comment on lines 55 to 65
func filterContent(content string) string {
// 定义需要过滤的违规词列表
forbiddenWords := []string{"垃圾", "最"}

// 使用正则表达式替换违规词
for _, word := range forbiddenWords {
re := regexp.MustCompile(`\b` + regexp.QuoteMeta(word) + `\b`)
content = re.ReplaceAllString(content, "")
}
return strings.TrimSpace(content) // 去除首尾空白
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Regex Word Boundaries May Not Work with Chinese Characters

The filterContent function uses \b (word boundary) in the regular expression to match forbidden words. However, \b is designed for ASCII word boundaries and may not work correctly with Chinese characters, as Chinese text doesn't use spaces to separate words.

This may cause the forbidden words not to be matched and filtered as intended. Consider removing \b from the pattern or using Unicode-aware regular expressions to accurately match and replace forbidden Chinese words.

Apply this diff to adjust the regex pattern:

 func filterContent(content string) string {
     // Define the list of forbidden words
     forbiddenWords := []string{"垃圾", "最"}

     // Use regular expressions to replace forbidden words
     for _, word := range forbiddenWords {
-        re := regexp.MustCompile(`\b` + regexp.QuoteMeta(word) + `\b`)
+        re := regexp.MustCompile(regexp.QuoteMeta(word))
         content = re.ReplaceAllString(content, "")
     }
     return strings.TrimSpace(content) // Remove leading and trailing whitespace
 }
📝 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.

Suggested change
func filterContent(content string) string {
// 定义需要过滤的违规词列表
forbiddenWords := []string{"垃圾", "最"}
// 使用正则表达式替换违规词
for _, word := range forbiddenWords {
re := regexp.MustCompile(`\b` + regexp.QuoteMeta(word) + `\b`)
content = re.ReplaceAllString(content, "")
}
return strings.TrimSpace(content) // 去除首尾空白
}
func filterContent(content string) string {
// 定义需要过滤的违规词列表
forbiddenWords := []string{"垃圾", "最"}
// 使用正则表达式替换违规词
for _, word := range forbiddenWords {
re := regexp.MustCompile(regexp.QuoteMeta(word))
content = re.ReplaceAllString(content, "")
}
return strings.TrimSpace(content) // 去除首尾空白
}

Comment on lines +195 to +210
r.POST("/api/question/:id/answer", func(c *gin.Context) {
id := c.Param("id")
var newAnswer Answer
if err := c.ShouldBindJSON(&newAnswer); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newAnswer.Content = filterContent(newAnswer.Content) // 过滤内容
newAnswer.QuestionID, _ = strconv.Atoi(id)
result := db.Create(&newAnswer)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
c.JSON(http.StatusOK, newAnswer)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle Error from strconv.Atoi

In the route handler for creating an answer, the error returned by strconv.Atoi(id) is being ignored. Ignoring errors can lead to incorrect data being processed and potential runtime issues if id is not a valid integer.

Update the code to handle the error appropriately:

 newAnswer.Content = filterContent(newAnswer.Content) // Filter content
-    newAnswer.QuestionID, _ = strconv.Atoi(id)
+    questionID, err := strconv.Atoi(id)
+    if err != nil {
+        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid question ID"})
+        return
+    }
+    newAnswer.QuestionID = questionID
     result := db.Create(&newAnswer)
     if result.Error != nil {
         c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
         return
     }
     c.JSON(http.StatusOK, newAnswer)

This ensures that if the id parameter is not a valid integer, the client is informed, and the server doesn't attempt to process an invalid ID.

📝 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.

Suggested change
r.POST("/api/question/:id/answer", func(c *gin.Context) {
id := c.Param("id")
var newAnswer Answer
if err := c.ShouldBindJSON(&newAnswer); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newAnswer.Content = filterContent(newAnswer.Content) // 过滤内容
newAnswer.QuestionID, _ = strconv.Atoi(id)
result := db.Create(&newAnswer)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
c.JSON(http.StatusOK, newAnswer)
})
r.POST("/api/question/:id/answer", func(c *gin.Context) {
id := c.Param("id")
var newAnswer Answer
if err := c.ShouldBindJSON(&newAnswer); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newAnswer.Content = filterContent(newAnswer.Content) // Filter content
questionID, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid question ID"})
return
}
newAnswer.QuestionID = questionID
result := db.Create(&newAnswer)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
c.JSON(http.StatusOK, newAnswer)
})

Comment on lines +267 to +289
r.PUT("/api/question/:id/best-answer", func(c *gin.Context) {
id := c.Param("id")
bestAnswerID := c.PostForm("best_answer_id")
if bestAnswerID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing best_answer_id parameter"})
return
}

var question Question
result := db.Where("id = ?", id).First(&question)
if result.Error != nil || result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "Question not found"})
return
}

// 更新问题的最佳答案
db.Model(&question).Update("best_answer", bestAnswerID)

// 更新答案的投票计数
db.Model(&Answer{}).Where("id = ?", bestAnswerID).Update("votes", gorm.Expr("votes + 1"))

c.JSON(http.StatusOK, gin.H{"message": "Best answer selected successfully!"})
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use Appropriate Data Binding for PUT Request

In the route handler for selecting the best answer (lines 267-289~), the code attempts to retrieve best_answer_id using c.PostForm("best_answer_id") in a PUT request. However, c.PostForm is designed to work with POST requests and form data. In a PUT request, data is typically sent in the request body, and using c.PostForm may not retrieve the data as intended.

Consider using c.ShouldBindJSON to properly bind the request data from the JSON body. Here's how you can modify the code:

-    bestAnswerID := c.PostForm("best_answer_id")
-    if bestAnswerID == "" {
-        c.JSON(http.StatusBadRequest, gin.H{"error": "Missing best_answer_id parameter"})
-        return
-    }
+    var requestBody struct {
+        BestAnswerID int `json:"best_answer_id" binding:"required"`
+    }
+    if err := c.ShouldBindJSON(&requestBody); err != nil {
+        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+        return
+    }
+    bestAnswerID := requestBody.BestAnswerID

     var question Question
     result := db.Where("id = ?", id).First(&question)
     if result.Error != nil || result.RowsAffected == 0 {
         c.JSON(http.StatusNotFound, gin.H{"error": "Question not found"})
         return
     }

     // Update the best answer for the question
-    db.Model(&question).Update("best_answer", bestAnswerID)
+    db.Model(&question).Update("best_answer", bestAnswerID)

     // Increment the vote count for the answer
     db.Model(&Answer{}).Where("id = ?", bestAnswerID).Update("votes", gorm.Expr("votes + 1"))

     c.JSON(http.StatusOK, gin.H{"message": "Best answer selected successfully!"})

Ensure that clients send the best_answer_id in the JSON body when making the PUT request.

📝 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.

Suggested change
r.PUT("/api/question/:id/best-answer", func(c *gin.Context) {
id := c.Param("id")
bestAnswerID := c.PostForm("best_answer_id")
if bestAnswerID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing best_answer_id parameter"})
return
}
var question Question
result := db.Where("id = ?", id).First(&question)
if result.Error != nil || result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "Question not found"})
return
}
// 更新问题的最佳答案
db.Model(&question).Update("best_answer", bestAnswerID)
// 更新答案的投票计数
db.Model(&Answer{}).Where("id = ?", bestAnswerID).Update("votes", gorm.Expr("votes + 1"))
c.JSON(http.StatusOK, gin.H{"message": "Best answer selected successfully!"})
})
r.PUT("/api/question/:id/best-answer", func(c *gin.Context) {
id := c.Param("id")
var requestBody struct {
BestAnswerID int `json:"best_answer_id" binding:"required"`
}
if err := c.ShouldBindJSON(&requestBody); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
bestAnswerID := requestBody.BestAnswerID
var question Question
result := db.Where("id = ?", id).First(&question)
if result.Error != nil || result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "Question not found"})
return
}
// Update the best answer for the question
db.Model(&question).Update("best_answer", bestAnswerID)
// Increment the vote count for the answer
db.Model(&Answer{}).Where("id = ?", bestAnswerID).Update("votes", gorm.Expr("votes + 1"))
c.JSON(http.StatusOK, gin.H{"message": "Best answer selected successfully!"})
})

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
.github/AAAxianyu/API 文档 (1)

329-329: Maintain a professional tone in the summary

In line 329, the phrase "(也许算?)" ("maybe counts?") introduces an informal tone to the documentation. Consider revising it to maintain a professional and consistent style throughout the document.

Apply this diff to adjust the wording:

-                        (7)一个简单的前端欢迎页面(也许算?)
+                        (7)一个简单的前端欢迎页面
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 59f96f8 and 249892d.

📒 Files selected for processing (1)
  • .github/AAAxianyu/API 文档 (1 hunks)


总结:这段代码实现了(1)用户的注册和登陆
(2)问题的创建,搜索,更新,删除
(3)答案的创建,搜错,更新,删除
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the typo in "搜错" to "搜索"

In line 325, the term "搜错" (search error) should be corrected to "搜索" (search) to accurately describe the functionality related to answers.

Apply this diff to fix the typo:

-                        (3)答案的创建,搜错,更新,删除
+                        (3)答案的创建,搜索,更新,删除
📝 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.

Suggested change
(3)答案的创建,搜错,更新,删除
(3)答案的创建,搜索,更新,删除

"id": 1,
"content": "Life's purpose is subjective.",
"question_id": 1,
"votes": 0}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix JSON formatting in the response example

In line 232, the closing brace } is on the same line as the last property, which may cause confusion and inconsistency with other examples. It should be placed on a new line for better readability and consistent formatting.

Apply this diff to fix the formatting:

-        "votes": 0}
+        "votes": 0
+    }
📝 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.

Suggested change
"votes": 0}
"votes": 0
}

Copy link
Collaborator

@whxxxxxxxxxx whxxxxxxxxxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最好分模块去写;User和问答割裂了,可以考虑联系起来,不然不知道问题和回答是谁的;考虑了违禁词过滤很不错,有没有更优的过滤策略;可以进一步分析场景的需求,完善功能

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants