package controllers import ( "crypto/rand" "errors" "fmt" "log" "math/big" "strings" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/app" "github.com/sujit-baniya/fiber-boilerplate/config" "github.com/sujit-baniya/fiber-boilerplate/pkg/auth" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" "github.com/sujit-baniya/fiber-boilerplate/pkg/services" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // Función para renderizar la página de registro func RegisterGet(c *fiber.Ctx) error { data := getFlashData(c) data["title"] = "Register | " if err := c.Render("auth/register", data, "layouts/landing"); err != nil { //nolint:wsl panic(err.Error()) } return nil } // Función para manejar la solicitud de registro func RegisterPost(c *fiber.Ctx) error { register := c.Locals("register").(models.RegisterForm) user, err := register.Signup() if err != nil { return app.Http.Flash.WithError(c, fiber.Map{ "error": true, "message": "Error on register request: " + err.Error(), }).Redirect("/register") } store := app.Http.Session.Get(c) // obtener/crear nueva sesión store.Set("user_id", user.ID) // guardar en el almacenamiento _ = store.Save() go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario) return c.Redirect("/") } // Función para verificar el correo electrónico registrado func VerifyRegisteredEmail(c *fiber.Ctx) error { return c.Redirect("/app") } // Función para reenviar el correo de confirmación func ResendConfirmEmail(c *fiber.Ctx) error { user, _ := auth.User(c) go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario) return c.Redirect("/") } func ReenvioEmail(c *fiber.Ctx) error { usuarioID := c.Params("id") // Usa Params para obtener el parámetro de la ruta user, err := models.GetUserById(usuarioID) if err != nil { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Usuario no encontrado: " + err.Error(), }) } // Reenvío del correo electrónico de forma asíncrona go services.SendConfirmationEmail(user.Email, app.Http.Server.Url, user.NombreUsuario) return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "Email reenviado correctamente", }) } // Función para manejar la solicitud de restablecimiento de contraseña func RequestPasswordResetPost(c *fiber.Ctx) error { // Crear una estructura para mapear los datos entrantes type Request struct { NombreUsuario string `json:"nombre_usuario"` // Mapeo con la clave del JSON } var req Request // Parsear el cuerpo JSON de la solicitud if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Invalid JSON format", }) } // Obtener el usuario del JSON parseado usuario := req.NombreUsuario fmt.Println("Usuario recibido:", usuario) // Intentar obtener el usuario por nombre de usuario user, err := models.GetUserByUsuario(usuario) if err != nil || user == nil { // Devolver respuesta de error en formato JSON return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Usuario inactivo o no existente", }) } // Obtener el email del usuario usuario_email := user.Email // Imprimir el email en la consola log.Println("Sending password reset email to:", usuario_email) // Enviar el correo de restablecimiento de contraseña de forma asíncrona go services.SendPasswordResetEmail(usuario_email, app.Http.Server.Url) // Devolver respuesta de éxito en formato JSON return c.JSON(fiber.Map{ "success": true, "message": "We've sent an email to reset your password to your registered email address", }) } // todo: Generar contrasenas aleatorias /do/generate-password func generatePassword(length int) (string, error) { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&" var password strings.Builder for i := 0; i < length; i++ { randomIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) if err != nil { return "", err } password.WriteByte(charset[randomIndex.Int64()]) } return password.String(), nil } func hashPassword(password string) (string, error) { hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", err } return string(hashed), nil } func updatePassword(nombreUsuario, plainPassword string) error { var user models.RegisterForm result := app.Http.Database.DB.Where("nombre_usuario = ?", nombreUsuario).First(&user) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return errors.New("codigo no pertenece a ningun usuario") } if user.Password != "" { return errors.New("este usuario ya cuenta con una contraseña") } hashedPassword, err := hashPassword(plainPassword) if err != nil { return err } user.Password = hashedPassword if err := app.Http.Database.DB.Save(&user).Error; err != nil { return err } return nil } func GeneratePasswordPost(c *fiber.Ctx) error { // Parsear el cuerpo de la solicitud var body struct { NombreUsuario string `json:"nombre_usuario"` } if err := c.BodyParser(&body); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Error al analizar el cuerpo de la solicitud", }) } if body.NombreUsuario == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "El codigo de usuario es requerido", }) } // Generar la contraseña password, err := generatePassword(12) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "Error generando contraseña", }) } // Actualizar la contraseña en la base de datos if err := updatePassword(body.NombreUsuario, password); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": err.Error(), }) } // Responder con la contraseña generada return c.JSON(fiber.Map{ "success": true, "message": "Contrasena generada exitosamente", "password": password, }) } // Función para renderizar la página de restablecimiento de contraseña func PasswordReset(c *fiber.Ctx) error { token := c.Query("t") if err := c.Render("auth/password-reset", fiber.Map{ "Title": "Password Reset", "Token": token, }, "layouts/landing"); err != nil { panic(err.Error()) } return nil } func PasswordResetPost(c *fiber.Ctx) error { // Recuperar el nombre de usuario y la nueva contraseña del formulario nombreUsuario := c.FormValue("nombre_usuario") password := c.FormValue("password") // Validar que ambos campos estén presentes if nombreUsuario == "" || password == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "message": "Los campos nombre_usuario y password son obligatorios.", "error": true, }) } // Imprimir los datos recibidos (opcional para depuración) fmt.Printf("Datos recibidos: nombre_usuario=%s, password=%s\n", nombreUsuario, password) // Buscar al usuario en la base de datos por nombre de usuario user, err := models.GetUserByUsuario(nombreUsuario) if err != nil || user == nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ "message": "Usuario no encontrado.", "error": true, }) } hash := config.Hash{} // Encripta la nueva contraseña hashedPassword, err := hash.Create(password) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "message": "Error al encriptar la contraseña", "error": true, }) } // Asigna la contraseña encriptada al modelo password = hashedPassword // Actualizar la contraseña del usuario err = models.UpdateUserPassword(user.ID, password) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "message": "Error al actualizar la contraseña.", "error": true, }) } // Redirigir al usuario al formulario de inicio de sesión después del éxito return c.Redirect("/login") } // Función para renderizar la página de solicitud de restablecimiento de contraseña func RequestPasswordReset(c *fiber.Ctx) error { data := getFlashData(c) data["title"] = "Password Reset | " if err := c.Render("auth/request-password-reset", data, "layouts/landing"); err != nil { //nolint:wsl panic(err.Error()) } return nil } func TokenVencido(c *fiber.Ctx) error { // Renderiza la vista con el layout layouts/main if err := c.Render("token_vencido", nil, "layouts/landing"); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "message": "Error rendering template", "error": err.Error(), }) } return nil }