-
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 #14
base: main
Are you sure you want to change the base?
feat:finish task #14
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 @@ | ||
package answer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package create | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"goexample/Account_System/forum/initial" | ||
"net/http" | ||
) | ||
|
||
func Create(c *gin.Context) { | ||
if c.Request.Method != "POST" { | ||
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"error": "Method not allowed"}) | ||
return | ||
} | ||
//Bind方法解析表单数据到结构体 | ||
var newQuestion initial.Question | ||
if err := c.Bind(&newQuestion.QuestionInfo); err != nil { | ||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) | ||
return | ||
} | ||
|
||
newQuestion.QuestionInfo.Content = c.PostForm("question") | ||
if newQuestion.QuestionInfo.Content == "" { | ||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Question is required"}) | ||
return | ||
} | ||
|
||
newQuestion.QuestionInfo.UserName = c.PostForm("username") | ||
|
||
Comment on lines
+14
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. Streamline data binding and add validation. The current implementation mixes automatic binding with manual form data extraction, which is redundant. Additionally, there's no validation for the username field. Consider the following improvements:
Here's an example of how you could refactor this: type QuestionForm struct {
Content string `form:"question" binding:"required"`
Username string `form:"username" binding:"required"`
}
func Create(c *gin.Context) {
var form QuestionForm
if err := c.ShouldBind(&form); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newQuestion := initial.Question{
QuestionInfo: initial.QuestionInfo{
Content: form.Content,
UserName: form.Username,
},
}
// Proceed with database insertion...
} This approach simplifies the code, ensures all required fields are present, and validates the input in a single step. |
||
result := initial.Dbq.Create(&newQuestion) | ||
if result.Error != nil { | ||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": result.Error.Error()}) | ||
return | ||
} | ||
Comment on lines
+29
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. 🛠️ Refactor suggestion Enhance error handling and add logging. While error handling is present, directly returning the database error to the client might expose sensitive information or be confusing to end-users. Consider the following improvements:
Here's an example of how you could refactor this: import (
"log"
"github.com/google/uuid"
)
// In your Create function
result := initial.Dbq.Create(&newQuestion)
if result.Error != nil {
errorID := uuid.New().String()
log.Printf("Error creating question (ID: %s): %v", errorID, result.Error)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"error": "An unexpected error occurred while creating the question.",
"errorID": errorID,
})
return
} This approach provides better security, improves user experience, and facilitates easier debugging. |
||
|
||
c.JSON(http.StatusOK, gin.H{"message": "Your Question successfully created"}) | ||
} | ||
Comment on lines
+35
to
+36
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 the success response with more information. While the current success response is clear, it could be more informative by including details about the created question. Consider returning additional information such as the question ID, timestamp, or a summary of the created content. This can be helpful for client-side operations or user feedback. Example: c.JSON(http.StatusOK, gin.H{
"message": "Your question was successfully created",
"questionID": newQuestion.ID, // Assuming your Question struct has an ID field
"createdAt": time.Now(),
"summary": truncate(newQuestion.QuestionInfo.Content, 100), // A helper function to limit the content length
})
// Helper function (place this outside of your handler)
func truncate(s string, max int) string {
if len(s) <= max {
return s
}
return s[:max] + "..."
} This provides more context to the client about the newly created question, which can be useful for immediate display or further actions. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package distribute | ||
|
||
import ( | ||
"fmt" | ||
"github.com/gin-gonic/gin" | ||
"goexample/Account_System/forum/initial" | ||
"goexample/Account_System/forum/readall" | ||
) | ||
Comment on lines
+1
to
+8
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 removing unused import and improving error handling The Apply this diff to improve the code: package distribute
import (
- "fmt"
+ "errors"
"github.com/gin-gonic/gin"
"goexample/Account_System/forum/initial"
"goexample/Account_System/forum/readall"
) Then update the error creation in the - return nil, fmt.Errorf("user not found")
+ return nil, errors.New("user not found")
|
||
|
||
/*func Tosql() *gorm.DB { | ||
db, err := gorm.Open("mysql", "root:123456789@tcp(127.0.0.1:3306)/dbquestion?charset=utf8&parseTime=True&loc=Local") | ||
if err != nil { | ||
panic(err) | ||
} | ||
return db | ||
}*/ | ||
Comment on lines
+10
to
+16
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. Remove commented-out code and avoid hardcoded credentials The If this function is needed elsewhere:
If it's not needed, simply remove the commented-out code to improve readability and maintainability. |
||
|
||
func Finduser(Username string) (*initial.QuestionInfo, error) { | ||
db := initial.Dbq | ||
var user initial.QuestionInfo | ||
err := db.Where("username=?", Username).First(&user).Error | ||
if err != nil { | ||
return nil, err | ||
} | ||
if user.UserName != "" { | ||
return &user, nil | ||
} | ||
return nil, fmt.Errorf("user not found") | ||
} | ||
Comment on lines
+18
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. 🛠️ Refactor suggestion Improve error handling and remove redundant check in The function can be simplified and improved:
Consider refactoring the function as follows: func Finduser(Username string) (*initial.QuestionInfo, error) {
db := initial.Dbq
var user initial.QuestionInfo
err := db.Where("username = ?", Username).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
}
return nil, fmt.Errorf("database error: %w", err)
}
return &user, nil
} This version:
Note: Make sure to import the |
||
|
||
func FindQuestion(Username string) *initial.Question { | ||
db := initial.Dbq | ||
var question initial.Question | ||
db.Where("username=?", Username).First(&question) | ||
return &question | ||
} | ||
Comment on lines
+31
to
+36
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 error handling to The current implementation of Consider refactoring the function to handle potential errors and return func FindQuestion(Username string) (*initial.Question, error) {
db := initial.Dbq
var question initial.Question
err := db.Where("username = ?", Username).First(&question).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // No question found, but not an error
}
return nil, fmt.Errorf("database error: %w", err)
}
return &question, nil
} This version:
Update the function signature and handle the error in the calling code accordingly. |
||
|
||
func Distribute(c *gin.Context) { | ||
username := c.Param("username") | ||
if user, _ := Finduser(username); user != nil { | ||
readall.GetAllQuestions() | ||
|
||
c.JSON(200, gin.H{ | ||
"user": "found", | ||
"username": username, | ||
"question": readall.GetAllQuestions(), | ||
}) | ||
|
||
c.JSON(200, gin.H{ | ||
"question": FindQuestion(username), | ||
}) | ||
|
||
return | ||
} | ||
|
||
c.JSON(200, gin.H{"message": "User not found:" + username}) | ||
|
||
} | ||
Comment on lines
+38
to
+58
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 The current implementation of the
Consider refactoring the function as follows: func Distribute(c *gin.Context) {
username := c.Param("username")
user, err := Finduser(username)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found: " + username})
return
}
questions := readall.GetAllQuestions()
userQuestion, err := FindQuestion(username)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch user's question"})
return
}
c.JSON(http.StatusOK, gin.H{
"user": user,
"allQuestions": questions,
"userQuestion": userQuestion,
})
} This refactored version:
Make sure to update the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package initial | ||
|
||
import ( | ||
_ "github.com/go-sql-driver/mysql" | ||
"github.com/jinzhu/gorm" | ||
) | ||
|
||
var Dbq *gorm.DB | ||
|
||
type Question struct { | ||
gorm.Model | ||
QuestionInfo QuestionInfo | ||
AnswerInfo AnswerInfo | ||
} | ||
|
||
type QuestionInfo struct { | ||
Content string | ||
UserName string | ||
} | ||
|
||
type AnswerInfo struct { | ||
Content string | ||
UserName string | ||
} | ||
Comment on lines
+10
to
+24
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 refining struct relationships and adding field tags. The current struct definitions can be improved:
Example improvement: type Question struct {
gorm.Model
Content string `gorm:"type:text" json:"content"`
UserName string `gorm:"size:255" json:"user_name"`
Answers []Answer `gorm:"foreignKey:QuestionID" json:"answers"`
}
type Answer struct {
gorm.Model
QuestionID uint `gorm:"index" json:"question_id"`
Content string `gorm:"type:text" json:"content"`
UserName string `gorm:"size:255" json:"user_name"`
} This structure more clearly represents a one-to-many relationship between questions and answers. |
||
|
||
func Initial() error { | ||
var err error | ||
Dbq, err = gorm.Open("mysql", "root:123456789@tcp(127.0.0.1:3306)/dbquestion?charset=utf8&parseTime=True&loc=Local") | ||
if err != nil { | ||
return err | ||
} | ||
Dbq.AutoMigrate(&Question{}) | ||
return nil | ||
} | ||
Comment on lines
+26
to
+34
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 Improve initialization function and database connection handling. Several improvements can be made to the
Example improvement: import (
"context"
"fmt"
"os"
"time"
)
func Initialize(ctx context.Context) error {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_NAME"))
var err error
db, err = gorm.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
// Set connection pool settings
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
db.DB().SetConnMaxLifetime(time.Hour)
// Auto-migrate all models
err = db.AutoMigrate(&Question{}, &Answer{})
if err != nil {
return fmt.Errorf("failed to auto-migrate: %w", err)
}
return nil
}
// Don't forget to add a function to close the database connection
func Close() {
if db != nil {
db.Close()
}
} This approach improves security, flexibility, and resource management. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,103 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package login | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/jinzhu/gorm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_ "github.com/jinzhu/gorm/dialects/mysql" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type User struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
gorm.Model | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Username string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Password string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+11
to
+14
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. Store passwords securely using hashing Storing passwords in plain text is a significant security risk. It's essential to hash passwords using a secure algorithm like bcrypt before storing them in the database. Consider updating your code to hash passwords before storing them: +import "golang.org/x/crypto/bcrypt"
type User struct {
gorm.Model
Username string
- Password string
+ Password string `json:"-"`
} Implement methods for setting and checking passwords: func (u *User) SetPassword(password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.Password = string(hashedPassword)
return nil
}
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
} In your registration handler, set the password using |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var db *gorm.DB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Initialize the database connection The Consider initializing |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/*func InitDB() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var err error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
db, err = gorm.Open("mysql", "user:password@tcp(127.0.0.1:24306)/main_db?charset=utf8&parseTime=True&loc=Local") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
panic(err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
db.AutoMigrate(&User{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/*u0 := User{Username: "Admin", Password: "hdu123"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result := db.Create(&u0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if result.Error != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fmt.Println(result.Error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fmt.Printf("User created with ID: %v\n", u0.ID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 检查是否匹配 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func findUser(Username string) *User { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var user User | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
db.Where("username=?", Username).First(&user) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if user.Username != "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return &user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+36
to
+43
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. Modify Currently, Update -func findUser(Username string) *User {
+func findUser(Username string) (*User, error) {
var user User
- db.Where("username=?", Username).First(&user)
- if user.Username != "" {
- return &user
- }
- return nil
+ result := db.Where("username = ?", Username).First(&user)
+ if result.Error != nil {
+ if result.Error == gorm.ErrRecordNotFound {
+ return nil, nil
+ }
+ return nil, result.Error
+ }
+ return &user, nil
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func authenticate(Username, Password string) bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user := findUser(Username) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return user != nil && user.Password == Password | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+46
to
+47
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. Handle potential nil pointer dereference in If Modify the func authenticate(Username, Password string) bool {
- user := findUser(Username)
- return user != nil && user.Password == Password
+ user, err := findUser(Username)
+ if err != nil {
+ // Handle error (e.g., log it)
+ return false
+ }
+ if user == nil {
+ return false
+ }
+ return user.CheckPassword(Password)
} Ensure that
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func LoginHandler(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if c.Request.Method != "POST" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"error": "Method not allowed"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
username := c.PostForm("Username") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
password := c.PostForm("Password") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if username == "" || password == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Username or password is empty"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if authenticate(username, password) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//http.Redirect(w, r, "/forum/:username", http.StatusFound) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_, _ = fmt.Println("Welcome to forum!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//c.Redirect(http.StatusFound, "/forum/"+username) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+50
to
+68
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 appropriate responses to the client in In Modify the code to send a response to the client upon successful login: if authenticate(username, password) {
- _, _ = fmt.Println("Welcome to forum!")
+ c.JSON(http.StatusOK, gin.H{"message": "Welcome to the forum!", "username": username})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/*func sayHello(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
b, err := ioutil.ReadFile("./Hello.txt") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//成功 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fmt.Fprintln(w, string(b)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func Registerhandler(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if c.Request.Method != "POST" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"error": "Method not allowed"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
username := c.PostForm("Username") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
password := c.PostForm("Password") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if username == "" || password == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Username or password is empty"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+85
to
+90
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 input validation in Ensure that the username and password meet certain criteria, such as minimum length and allowed characters, to improve security and user experience. Consider adding validation logic like: if len(username) < 5 || len(password) < 8 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Username or password is too short"})
return
} |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if findUser(username) != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Username already exists"}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
newuser := User{Username: username, Password: password} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result := db.Create(&newuser) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if result.Error != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusOK, gin.H{"message": "User registered successfully", "username": newuser.Username}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+96
to
+102
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. Implement password hashing in registration Passwords should be hashed before storage to enhance security. Update the registration handler to hash the password using newuser := User{}
- newuser.Username = username
- newuser.Password = password
+ newuser.Username = username
+ err := newuser.SetPassword(password)
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Error hashing password"})
+ return
+ }
result := db.Create(&newuser)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package readall | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"goexample/Account_System/forum/initial" | ||
) | ||
|
||
/*func ReadAll() { | ||
//initial.Init() | ||
|
||
|
||
|
||
ReadAll() func(c *gin.Context) { | ||
id := c.Param("id") | ||
|
||
var questions []initial.Question | ||
if err := initial.Dbq.Find(&questions).Error;err!=nil{ | ||
c.JSON(500,gin.H{"error":err.Error()}) | ||
return | ||
} | ||
c.JSON(200,questions) | ||
}*/ | ||
|
||
func GetAllQuestions() gin.HandlerFunc { | ||
/*db, err := gorm.Open("mysql", "root:123456789@tcp(127.0.0.1:3306)/dbquestion?charset=utf8&parseTime=True&loc=Local") | ||
if err != nil { | ||
panic(err) | ||
}*/ | ||
return func(c *gin.Context) { | ||
var questions []initial.Question | ||
if err := initial.Dbq.Find(&questions).Error; err != nil { | ||
c.JSON(500, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
c.JSON(200, questions) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package AI | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
_ "github.com/go-sql-driver/mysql" | ||
"goexample/Forum/InitDB" | ||
"goexample/Forum/Models" | ||
"net/http" | ||
) | ||
|
||
// 假设的AI API响应结构 | ||
type AIResponse struct { | ||
Content string `json:"answer"` | ||
} | ||
|
||
var aiAPIURL = "https://yiyan.baidu.com/" // 替换为你的AI API URL | ||
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 making the AI API URL configurable. While the Here's a suggested improvement: import "os"
var aiAPIURL = os.Getenv("AI_API_URL")
func init() {
if aiAPIURL == "" {
aiAPIURL = "https://yiyan.baidu.com/" // fallback to default URL
}
} This allows the URL to be set via an environment variable, with a fallback to the default value if not set. |
||
|
||
func Aiimport(c *gin.Context) { | ||
id := c.Param("id") | ||
result := InitDB.Db.Where("id=?", id).Find(&Models.Question{}) | ||
if result.Error != nil { | ||
if result.RecordNotFound() { | ||
c.JSON(http.StatusNotFound, gin.H{"error": "no questions found with this question_id"}) | ||
} else { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"}) | ||
} | ||
return | ||
} | ||
|
||
} | ||
Comment on lines
+18
to
+30
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. Function While the error handling for database queries is good, there are several issues with this function:
Here's a suggested outline for completing the function: func HandleAIQuestion(c *gin.Context) {
id := c.Param("id")
var question Models.Question
result := InitDB.Db.Where("id = ?", id).First(&question)
if result.Error != nil {
if result.RecordNotFound() {
c.JSON(http.StatusNotFound, gin.H{"error": "Question not found"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
}
return
}
// TODO: Send question to AI API and get response
// aiResponse, err := sendToAIAPI(question.Content)
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{"error": "AI API error"})
// return
// }
// c.JSON(http.StatusOK, AIResponse{Content: aiResponse})
} This outline provides a structure for completing the function, including interacting with the AI API and sending a proper response. Would you like assistance in implementing the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package InitDB | ||
|
||
import ( | ||
_ "github.com/go-sql-driver/mysql" | ||
"github.com/jinzhu/gorm" | ||
"goexample/Forum/Models" | ||
"log" | ||
) | ||
Comment on lines
+3
to
+8
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. Review the import path for the Models package. The import path |
||
|
||
var Db *gorm.DB | ||
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 Reconsider global variable usage and naming. While using a global variable for the database connection is common, it can make testing and maintaining the code more difficult. Consider passing the database connection as a parameter to functions that need it. If you decide to keep it global, rename |
||
|
||
func InitDB() error { | ||
var err error | ||
Db, err = gorm.Open("mysql", "root:123456789@tcp(127.0.0.1:3306)/dbquestion?charset=utf8&parseTime=True&loc=Local") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
Db.AutoMigrate(&Models.Question{}, &Models.UserLogin{}) | ||
return nil | ||
} | ||
Comment on lines
+12
to
+20
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 Improve database initialization and error handling. There are several areas for improvement in the
Here's a suggested refactoring: func InitDB() error {
var err error
dsn := os.Getenv("DATABASE_URL") // Use environment variable
Db, err = gorm.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
defer func() {
if err != nil {
Db.Close()
}
}()
err = Db.AutoMigrate(&Models.Question{}, &Models.UserLogin{})
if err != nil {
return fmt.Errorf("failed to auto-migrate: %w", err)
}
return nil
} Also, consider adding a func Close() {
if Db != nil {
Db.Close()
}
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package Login | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"goexample/Forum/InitDB" | ||
"goexample/Forum/Models" | ||
"goexample/Forum/Token" | ||
"net/http" | ||
) | ||
|
||
func LoginHandler(c *gin.Context) { | ||
if c.Request.Method != "POST" { | ||
c.JSON(http.StatusMethodNotAllowed, gin.H{"error": "Method not allowed"}) | ||
return | ||
} | ||
Comment on lines
+12
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 using Gin's built-in method routing. Instead of manually checking the HTTP method, consider using Gin's built-in routing methods like // In your route setup:
router.POST("/login", LoginHandler)
// Then in LoginHandler, remove the method check:
func LoginHandler(c *gin.Context) {
// Start with binding the request
var login Models.UserLogin
if err := c.ShouldBindJSON(&login); err != nil {
// ... (rest of the function)
}
} |
||
|
||
var login Models.UserLogin | ||
if err := c.ShouldBindJSON(&login); err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
var user Models.UserLogin | ||
result := InitDB.Db.Where("username = ? AND password = ?", login.Username, login.Password).First(&user) | ||
if result.Error != nil { | ||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) | ||
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. 💡 Codebase verification Security Issue: Passwords Are Stored in Plain Text Without Hashing
🔗 Analysis chainSecurity concern: Potential SQL injection vulnerability. The current database query method might be vulnerable to SQL injection attacks. Consider using parameterized queries or an ORM method that handles escaping. Here's a safer alternative using GORM's Where clause with separate arguments: result := InitDB.Db.Where("username = ? AND password = ?", login.Username, login.Password).First(&user) Additionally, storing plain text passwords is a security risk. Implement password hashing before storage and comparison. Let's check if password hashing is implemented elsewhere: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for password hashing implementation
rg --type go 'func.*(hash|encrypt).*password' -g '!vendor'
Length of output: 60 Script: #!/bin/bash
# Search for common hashing libraries and their usage in the codebase
rg --type go '(bcrypt|sha256|argon2|scrypt)' -g '!vendor'
# Search for any function or method that handles passwords more broadly
rg --type go 'password' -g '!vendor'
Length of output: 1315 |
||
|
||
token, err := Token.GenerateToken(user.ID, user.Username) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate token"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{"status": "success", "token": token}) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,34 @@ | ||||||||||||||||||||||||||||||||||||||||||
package Middleware | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||||||||||||||||
"github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||
"goexample/Forum/Token" | ||||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||||
"strings" | ||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+3
to
+9
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 using a proper logging mechanism instead of fmt.Println While the imports are generally appropriate for the middleware's functionality, using You could use a logging library like
import (
...
+ "github.com/sirupsen/logrus"
)
- fmt.Println("User ID from token:", userIDStr)
+ logrus.WithField("userId", userIDStr).Info("User authenticated") This change will provide more structured and configurable logging, which is beneficial for monitoring and debugging in production environments.
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
func AuthMiddleware() gin.HandlerFunc { | ||||||||||||||||||||||||||||||||||||||||||
return func(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||
token := c.GetHeader("Authorization") | ||||||||||||||||||||||||||||||||||||||||||
if token == "" { | ||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization token is missing"}) | ||||||||||||||||||||||||||||||||||||||||||
c.Abort() | ||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
token = strings.Split(token, " ")[1] | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+13
to
+19
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 token extraction robustness The current implementation of token extraction could be more robust. It assumes the token is always in the format "Bearer ", which may not always be the case. Consider the following improvements:
Here's a suggested implementation: - token := c.GetHeader("Authorization")
- if token == "" {
+ authHeader := c.GetHeader("Authorization")
+ if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization token is missing"})
c.Abort()
return
}
- token = strings.Split(token, " ")[1]
+ parts := strings.SplitN(authHeader, " ", 2)
+ if len(parts) != 2 || parts[0] != "Bearer" {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"})
+ c.Abort()
+ return
+ }
+ token := parts[1] This change ensures that the "Bearer " prefix is present and correctly handles the token extraction. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
claims, err := Token.ParseToken(token) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"}) | ||||||||||||||||||||||||||||||||||||||||||
c.Abort() | ||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+21
to
+26
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 error handling in token parsing The current implementation doesn't distinguish between different types of token errors (e.g., expired token, invalid signature, etc.). This could be improved to provide more informative error messages. Consider modifying the claims, err := Token.ParseToken(token)
if err != nil {
switch err.(type) {
case *jwt.ValidationError:
vErr := err.(*jwt.ValidationError)
switch vErr.Errors {
case jwt.ValidationErrorExpired:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token has expired"})
case jwt.ValidationErrorSignatureInvalid:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"})
default:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
}
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process token"})
}
c.Abort()
return
} This change would provide more specific error messages, helping in debugging and improving the user experience. |
||||||||||||||||||||||||||||||||||||||||||
userIDStr := claims.Id | ||||||||||||||||||||||||||||||||||||||||||
fmt.Println("User ID from token:", userIDStr) | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// 设置用户 ID 到上下文中,以便后续中间件或处理器使用 | ||||||||||||||||||||||||||||||||||||||||||
c.Set("userId", userIDStr) | ||||||||||||||||||||||||||||||||||||||||||
c.Next() | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+27
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. 🛠️ Refactor suggestion Improve user ID handling and context setting The current implementation sets the user ID in the context as a string. It might be beneficial to parse it to the appropriate type (e.g., int or uint) before setting it in the context. Consider parsing the user ID to the appropriate type: - userIDStr := claims.Id
- fmt.Println("User ID from token:", userIDStr)
- c.Set("userId", userIDStr)
+ userID, err := strconv.ParseUint(claims.Id, 10, 64)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID in token"})
+ c.Abort()
+ return
+ }
+ logrus.WithField("userId", userID).Info("User authenticated")
+ c.Set("userId", userID) This change ensures that the user ID is of the correct type when used in subsequent handlers, reducing the chance of type-related errors. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} |
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
Use Gin's routing for HTTP method restriction.
While checking the HTTP method is a good practice, Gin provides built-in method-specific routing that can handle this more elegantly.
Consider modifying your route registration to use
router.POST("/your-endpoint", create.Create)
instead of a generic handler. This way, Gin will automatically handle method restrictions, and you can remove the manual check in your handler:This approach leverages Gin's features and makes your code more idiomatic.