package api import ( "errors" "fmt" "net/http" "strings" "git.artlef.fr/PersonalLibraryManager/internal/appcontext" "git.artlef.fr/PersonalLibraryManager/internal/i18nresource" "git.artlef.fr/PersonalLibraryManager/internal/jwtauth" "git.artlef.fr/PersonalLibraryManager/internal/model" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) func GetMyBooksHanderl(ac appcontext.AppContext) { var userbooks []model.UserBook user, err := getAuthenticatedUser(ac) if err != nil { manageDefaultError(ac.C, err) return } ac.Db.Preload("Book").Where("user_id = ?", user.ID).Find(&userbooks) booksDto := make([]bookUserGet, 0) for _, userbook := range userbooks { booksDto = append(booksDto, fromUserBookDb(&userbook)) } ac.C.JSON(http.StatusOK, booksDto) } func GetSearchBooksHandler(ac appcontext.AppContext) { searchterm := ac.C.Param("searchterm") var booksDb []model.Book ac.Db.Where("LOWER(title) LIKE ?", "%"+strings.ToLower(searchterm)+"%").Find(&booksDb) books := make([]bookSearchGet, 0) for _, b := range booksDb { books = append(books, fromBookDb(&b)) } ac.C.JSON(http.StatusOK, books) } func PostBookHandler(ac appcontext.AppContext) { var book bookPostCreate err := ac.C.ShouldBindJSON(&book) if err != nil { manageBindingError(ac, err) return } user, fetchUserErr := getAuthenticatedUser(ac) if fetchUserErr != nil { manageDefaultError(ac.C, err) return } bookDb := book.toBook(&user) err = ac.Db.Model(&model.Book{}).Save(&bookDb).Error if err != nil { manageDefaultError(ac.C, err) return } ac.C.String(200, "Success") } func PostSignupHandler(ac appcontext.AppContext) { var user userSignup err := ac.C.ShouldBindJSON(&user) if err != nil { manageBindingError(ac, err) return } userDb, err := user.toUser() if err != nil { manageDefaultError(ac.C, err) return } err = ac.Db.Model(&model.User{}).Save(&userDb).Error if err != nil { manageDefaultError(ac.C, err) return } ac.C.String(200, "Success") } func PostLoginHandler(ac appcontext.AppContext) { var user userLogin err := ac.C.ShouldBindJSON(&user) if err != nil { manageBindingError(ac, err) return } if !isUserAndPasswordOk(ac.Db, user.Username, user.Password) { ac.C.JSON(http.StatusInternalServerError, gin.H{"error": i18nresource.GetTranslatedMessage(ac, "InvalidCredentials")}) return } var jwtToken string jwtToken, err = jwtauth.GenerateJwtToken(user.Username) if err != nil { ac.C.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Errorf("Error when generating JWT token: %w", err)}) return } ac.C.JSON(200, gin.H{"message": i18nresource.GetTranslatedMessage(ac, "AuthenticationSuccess"), "token": jwtToken}) } func isUserAndPasswordOk(db *gorm.DB, username string, password string) bool { var user model.User db.Where("name = ?", username).First(&user) err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) return err == nil } func getAuthenticatedUser(ac appcontext.AppContext) (model.User, error) { var user model.User username, userIsInContext := ac.C.Get("user") if !userIsInContext { return user, errors.New("User not found in context") } res := ac.Db.Where("name = ?", username).First(&user) return user, res.Error } func manageBindingError(ac appcontext.AppContext, err error) { var ve validator.ValidationErrors if errors.As(err, &ve) { ac.C.JSON(http.StatusBadRequest, getValidationErrors(ac, &ve)) } else { manageDefaultError(ac.C, err) } } func manageDefaultError(c *gin.Context, err error) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) }