Files
GoMembership/internal/controllers/user_controller.go

375 lines
11 KiB
Go

package controllers
import (
"GoMembership/internal/config"
"GoMembership/internal/constants"
"GoMembership/internal/middlewares"
"GoMembership/internal/models"
"GoMembership/internal/services"
"GoMembership/internal/utils"
"strings"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"GoMembership/pkg/errors"
"GoMembership/pkg/logger"
)
type UserController struct {
Service services.UserServiceInterface
EmailService *services.EmailService
ConsentService services.ConsentServiceInterface
BankAccountService services.BankAccountServiceInterface
MembershipService services.MembershipServiceInterface
LicenceService services.LicenceInterface
}
type RegistrationData struct {
User models.User `json:"user"`
}
func (uc *UserController) UpdateHandler(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err)
var validationErrors []gin.H
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
validationErrors = append(validationErrors, gin.H{
"field": e.Field(),
"key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
logger.Error.Printf("ValidationErrors: %#v", validationErrors)
c.JSON(http.StatusBadRequest, gin.H{"errors": validationErrors})
c.Abort()
return
}
logger.Error.Print("Continuing...")
tokenString, err := c.Cookie("jwt")
if err != nil {
logger.Error.Printf("No Auth token: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.no_auth_token",
}}})
c.Abort()
return
}
_, claims, err := middlewares.ExtractContentFrom(tokenString)
if err != nil {
logger.Error.Printf("Error retrieving token and claims from JWT")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_parsing_error",
}}})
return
}
jwtUserID := uint((*claims)["user_id"].(float64))
userRole := int8((*claims)["role_id"].(float64))
if user.ID == 0 {
logger.Error.Printf("No User.ID in request from user with id: %v, aborting", jwtUserID)
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "id",
"key": "server.validation.no_user_id_provided",
}}})
return
}
if user.ID != jwtUserID && userRole < constants.Roles.Editor {
c.JSON(http.StatusForbidden, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.unauthorized_update",
}}})
return
}
selectedModel, err := uc.MembershipService.GetModelByName(&user.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", user.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
return
}
user.Membership.SubscriptionModel = *selectedModel
// TODO: If it's not an admin, prevent changes to critical fields
// if userRole != constants.Roles.Admin {
// existingUser, err := uc.Service.GetUserByID(jwtUserID)
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving user data"})
// return
// }
// user.Email = existingUser.Email
// user.RoleID = existingUser.RoleID
// }
updatedUser, err := uc.Service.UpdateUser(&user, userRole)
if err != nil {
switch err {
case errors.ErrUserNotFound:
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": user.FirstName + " " + user.LastName,
"key": "server.validation.user_not_found",
}}})
case errors.ErrInvalidUserData:
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.validation.invalid_user_data",
}}})
default:
logger.Error.Printf("Failed to update user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return
}
}
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser})
}
func (uc *UserController) CurrentUserHandler(c *gin.Context) {
userIDInterface, ok := c.Get("user_id")
if !ok || userIDInterface == nil {
logger.Error.Printf("Error getting user_id from header")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.validation.no_user_id_provided",
}}})
return
}
userID, ok := userIDInterface.(uint)
if !ok {
logger.Error.Printf("Error: user_id is not of type uint")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.error.internal_server_error",
}}})
return
}
user, err := uc.Service.GetUserByID(uint(userID))
if err != nil {
logger.Error.Printf("Error retrieving valid user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return
}
c.JSON(http.StatusOK, gin.H{
"user": user.Safe(),
})
}
func (uc *UserController) LogoutHandler(c *gin.Context) {
tokenString, err := c.Cookie("jwt")
if err != nil {
logger.Error.Printf("unable to get token from cookie: %#v", err)
}
middlewares.InvalidateSession(tokenString)
c.SetCookie("jwt", "", -1, "/", "", true, true)
c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"})
}
func (uc *UserController) LoginHandler(c *gin.Context) {
var input struct {
Email string `json:"email"`
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&input); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err.Error())
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.invalid_json",
}}})
return
}
user, err := uc.Service.GetUserByEmail(input.Email)
if err != nil {
logger.Error.Printf("Error during user(%v) retrieval: %v\n", input.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
return
}
ok, err := user.PasswordMatches(input.Password)
if err != nil {
logger.Error.Printf("Error during Password comparison: %v", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return
}
if !ok {
logger.Error.Printf("Wrong Password: %v %v", user.FirstName, user.LastName)
c.JSON(http.StatusNotAcceptable, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
return
}
logger.Error.Printf("jwtsevret: %v", config.Auth.JWTSecret)
token, err := middlewares.GenerateToken(config.Auth.JWTSecret, user, "")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_generation_failed",
}}})
return
}
utils.SetCookie(c, token)
c.JSON(http.StatusOK, gin.H{
"message": "Login successful",
})
}
func (uc *UserController) RegisterUser(c *gin.Context) {
var regData RegistrationData
if err := c.ShouldBindJSON(&regData); err != nil {
logger.Error.Printf("Couldn't decode Userdata: %v", err)
var validationErrors []gin.H
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
validationErrors = append(validationErrors, gin.H{
"field": e.Field(),
"key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
c.JSON(http.StatusBadRequest, gin.H{"error": validationErrors})
return
}
selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", regData.User.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
return
}
regData.User.Membership.SubscriptionModel = *selectedModel
regData.User.RoleID = constants.Roles.Member
// Register User
id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil {
logger.Error.Printf("Couldn't register User(%v): %v", regData.User.Email, err)
if strings.Contains(err.Error(), "UNIQUE constraint failed: users.email") {
c.JSON(http.StatusConflict, gin.H{"errors": []gin.H{{
"field": "email",
"key": "server.validation.email_already_registered",
}}})
} else {
logger.Error.Printf("Failed to register user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
}
return
}
regData.User.ID = id
// Register Consents
var consents = [2]models.Consent{
{
FirstName: regData.User.FirstName,
LastName: regData.User.LastName,
Email: regData.User.Email,
ConsentType: "TermsOfService",
},
{
FirstName: regData.User.FirstName,
LastName: regData.User.LastName,
Email: regData.User.Email,
ConsentType: "Privacy",
},
}
for _, consent := range consents {
_, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil {
logger.Error.Printf("%v, Couldn't register consent: %v", regData.User.Email, err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return
}
}
// Send notifications
if err := uc.EmailService.SendVerificationEmail(&regData.User, &token); err != nil {
logger.Error.Printf("Failed to send email verification email to user(%v): %v", regData.User.Email, err)
// Proceed without returning error since user registration is successful
}
// Notify admin of new user registration
if err := uc.EmailService.SendRegistrationNotification(&regData.User); err != nil {
logger.Error.Printf("Failed to notify admin of new user(%v) registration: %v", regData.User.Email, err)
// Proceed without returning error since user registration is successful
}
c.JSON(http.StatusCreated, gin.H{
"message": "Registration successuful",
"id": regData.User.ID,
})
}
func (uc *UserController) VerifyMailHandler(c *gin.Context) {
token := c.Query("token")
if token == "" {
logger.Error.Println("Missing token to verify mail")
c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Missing token"})
return
}
user, err := uc.Service.VerifyUser(&token)
if err != nil {
logger.Error.Printf("Cannot verify user: %v", err)
c.HTML(http.StatusUnauthorized, "verification_error.html", gin.H{"ErrorMessage": "Emailadresse wurde schon bestätigt. Sollte dies nicht der Fall sein, wende Dich bitte an info@carsharing-hasloh.de."})
return
}
logger.Info.Printf("VerificationMailHandler User: %#v", user.Email)
uc.EmailService.SendWelcomeEmail(user)
c.HTML(http.StatusOK, "verification_success.html", gin.H{"FirstName": user.FirstName})
}