mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 08:58:48 +00:00
terminado sign up
This commit is contained in:
@ -4,12 +4,13 @@ import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -32,44 +33,67 @@ public class EmailService {
|
||||
Map<String, Object> variables = Map.of(
|
||||
"fullName", fullName,
|
||||
"resetUrl", resetUrl);
|
||||
sendEmail(to, subject, "imprimelibros/email/reset-password", variables);
|
||||
sendEmail(to, subject, "imprimelibros/email/reset-password", variables, locale);
|
||||
}
|
||||
|
||||
public void sendVerificationEmail(String to, String fullName, String verifyUrl, Locale locale) {
|
||||
String subject = messageSource.getMessage("email.verify.title", null, locale);
|
||||
Map<String, Object> variables = Map.of(
|
||||
"fullName", fullName,
|
||||
"verifyUrl", verifyUrl);
|
||||
sendEmail(to, subject, "imprimelibros/email/verify", variables);
|
||||
"verifyUrl", verifyUrl,
|
||||
"minutes", 60);
|
||||
sendEmail(to, subject, "imprimelibros/email/verify", variables, locale);
|
||||
}
|
||||
|
||||
|
||||
// ->>>>>>>> PRIVATE METHODS <<<<<<<<<<<-
|
||||
|
||||
/******************
|
||||
* Envía un email usando una plantilla Thymeleaf.
|
||||
*
|
||||
* @param to
|
||||
* @param subject
|
||||
* @param template
|
||||
* @param variables
|
||||
**********************************************/
|
||||
|
||||
private void sendEmail(String to, String subject, String template, Map<String, Object> variables) {
|
||||
private void sendEmail(String to, String subject, String template, Map<String, Object> variables, Locale locale) {
|
||||
try {
|
||||
Context context = new Context();
|
||||
context.setVariables(variables);
|
||||
String html = templateEngine.process(template, context);
|
||||
Context ctx = new Context(locale);
|
||||
ctx.setVariables(variables);
|
||||
ctx.setVariable("subject", subject);
|
||||
ctx.setVariable("companyName", "ImprimeLibros");
|
||||
ctx.setVariable("year", java.time.Year.now().getValue());
|
||||
|
||||
MimeMessage message = mailSender.createMimeMessage();
|
||||
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
|
||||
helper.setFrom("no-reply@imprimelibros.com");
|
||||
helper.setTo(to);
|
||||
helper.setSubject(subject);
|
||||
helper.setText(html, true);
|
||||
// Incrusta el CSS (no uses <link> en emails)
|
||||
var cssRes = new ClassPathResource("static/assets/css/email.css");
|
||||
String emailCss = org.springframework.util.StreamUtils.copyToString(cssRes.getInputStream(),
|
||||
java.nio.charset.StandardCharsets.UTF_8);
|
||||
ctx.setVariable("emailCss", emailCss);
|
||||
|
||||
mailSender.send(message);
|
||||
} catch (MessagingException e) {
|
||||
e.printStackTrace();
|
||||
ctx.setVariable("template", template);
|
||||
String html = templateEngine.process("imprimelibros/email/layout", ctx);
|
||||
|
||||
MimeMessage msg = mailSender.createMimeMessage();
|
||||
|
||||
MimeMessageHelper h = new MimeMessageHelper(
|
||||
msg,
|
||||
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
|
||||
"UTF-8");
|
||||
|
||||
h.setFrom("no-reply@imprimelibros.com");
|
||||
h.setTo(to);
|
||||
h.setSubject(subject);
|
||||
|
||||
h.setText(html, true);
|
||||
|
||||
// 3) ahora el inline, con content-type explícito
|
||||
ClassPathResource logoRes = new ClassPathResource("static/assets/images/logo-light.png");
|
||||
h.addInline("logo", logoRes, "image/png");
|
||||
|
||||
mailSender.send(msg);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error enviando email", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
import com.imprimelibros.erp.login.dto.SignupForm;
|
||||
|
||||
@ -19,9 +20,11 @@ import jakarta.validation.Valid;
|
||||
public class LoginController {
|
||||
|
||||
private final SignupService signupService;
|
||||
private final MessageSource messageSource;
|
||||
|
||||
public LoginController(SignupService signupService) {
|
||||
public LoginController(SignupService signupService, MessageSource messageSource) {
|
||||
this.signupService = signupService;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
@ -47,12 +50,13 @@ public class LoginController {
|
||||
if (br.hasErrors()) {
|
||||
ra.addFlashAttribute("org.springframework.validation.BindingResult.signupForm", br);
|
||||
ra.addFlashAttribute("signupForm", form);
|
||||
ra.addFlashAttribute("signup_error", "Revisa el formulario");
|
||||
ra.addFlashAttribute("signup_error",
|
||||
messageSource.getMessage("login.signup.error.review", null, locale));
|
||||
return "redirect:/signup";
|
||||
}
|
||||
try {
|
||||
signupService.register(form, locale);
|
||||
ra.addFlashAttribute("info", "Te hemos enviado un email para confirmar tu correo.");
|
||||
ra.addFlashAttribute("info", messageSource.getMessage("login.signup.success", null, locale));
|
||||
return "redirect:/login";
|
||||
} catch (IllegalArgumentException ex) {
|
||||
ra.addFlashAttribute("signup_error", ex.getMessage());
|
||||
@ -62,12 +66,12 @@ public class LoginController {
|
||||
}
|
||||
|
||||
@GetMapping("/verify")
|
||||
public String verify(@RequestParam("token") String token, RedirectAttributes ra) {
|
||||
public String verify(@RequestParam("token") String token, RedirectAttributes ra, Locale locale) {
|
||||
boolean ok = signupService.verify(token);
|
||||
if (ok) {
|
||||
ra.addFlashAttribute("info", "¡Cuenta verificada! Ya puedes iniciar sesión.");
|
||||
ra.addFlashAttribute("info", messageSource.getMessage("login.signup.success.verified", null, locale));
|
||||
} else {
|
||||
ra.addFlashAttribute("danger", "Enlace inválido o caducado. Solicita uno nuevo.");
|
||||
ra.addFlashAttribute("danger", messageSource.getMessage("login.signup.error.token.invalid", null, locale));
|
||||
}
|
||||
return "redirect:/login";
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import java.util.Map;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
import com.imprimelibros.erp.common.email.EmailService;
|
||||
import com.imprimelibros.erp.login.dto.SignupForm;
|
||||
@ -26,6 +27,7 @@ public class SignupService {
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final VerificationTokenRepository tokenRepository;
|
||||
private final EmailService emailService;
|
||||
private final MessageSource messageSource;
|
||||
|
||||
// minutos de validez del token
|
||||
private static final long TOKEN_MINUTES = 60;
|
||||
@ -34,22 +36,40 @@ public class SignupService {
|
||||
RoleDao roleRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
VerificationTokenRepository tokenRepository,
|
||||
EmailService emailService) {
|
||||
EmailService emailService,
|
||||
MessageSource messageSource) {
|
||||
this.userRepository = userRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.tokenRepository = tokenRepository;
|
||||
this.emailService = emailService;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void register(SignupForm form, Locale locale) {
|
||||
if (!form.getPassword().equals(form.getPasswordConfirm())) {
|
||||
throw new IllegalArgumentException("Las contraseñas no coinciden");
|
||||
throw new IllegalArgumentException(
|
||||
messageSource.getMessage("login.signup.error.password.mismatch", null, locale));
|
||||
}
|
||||
|
||||
if (userRepository.existsByUserNameIgnoreCase(form.getUsername())) {
|
||||
throw new IllegalArgumentException("El correo ya está registrado");
|
||||
String email = form.getUsername().trim().toLowerCase();
|
||||
|
||||
var existingLite = userRepository.findLiteByUserNameIgnoreCase(email);
|
||||
|
||||
if (existingLite.isPresent()) {
|
||||
var lite = existingLite.get();
|
||||
|
||||
boolean isDeleted = lite.getDeleted();
|
||||
if (isDeleted) {
|
||||
// caso: existe pero borrado
|
||||
throw new IllegalArgumentException(
|
||||
messageSource.getMessage("login.signup.error.email.exists-archived", null, locale));
|
||||
} else {
|
||||
// caso: existe y no borrado
|
||||
throw new IllegalArgumentException(
|
||||
messageSource.getMessage("login.signup.error.email.exists", null, locale));
|
||||
}
|
||||
}
|
||||
|
||||
// Crear usuario deshabilitado
|
||||
@ -85,7 +105,6 @@ public class SignupService {
|
||||
locale);
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public boolean verify(String tokenValue) {
|
||||
var tokenOpt = tokenRepository.findByToken(tokenValue);
|
||||
|
||||
@ -57,7 +57,8 @@ public class User {
|
||||
@Column(name = "deleted_by")
|
||||
private Long deletedBy;
|
||||
|
||||
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
|
||||
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE,
|
||||
CascadeType.REMOVE }, orphanRemoval = true)
|
||||
@SQLRestriction("deleted = false")
|
||||
@JsonIgnore
|
||||
private Set<UserRole> rolesLink = new HashSet<>();
|
||||
|
||||
@ -29,6 +29,15 @@ public interface UserDao extends JpaRepository<User, Long>, JpaSpecificationExec
|
||||
|
||||
boolean existsByUserNameIgnoreCase(String userName);
|
||||
|
||||
// Para comprobar si existe al hacer signup
|
||||
@Query(value = """
|
||||
SELECT id, deleted, enabled
|
||||
FROM users
|
||||
WHERE LOWER(username) = LOWER(:userName)
|
||||
LIMIT 1
|
||||
""", nativeQuery = true)
|
||||
Optional<UserLite> findLiteByUserNameIgnoreCase(@Param("userName") String userName);
|
||||
|
||||
boolean existsByUserNameIgnoreCaseAndIdNot(String userName, Long id);
|
||||
|
||||
// Nuevo: para login/negocio "activo"
|
||||
|
||||
9
src/main/java/com/imprimelibros/erp/users/UserLite.java
Normal file
9
src/main/java/com/imprimelibros/erp/users/UserLite.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.imprimelibros.erp.users;
|
||||
|
||||
// Proyección para consultas ligeras de usuarios (id, enabled, deleted)
|
||||
public interface UserLite {
|
||||
|
||||
Long getId();
|
||||
Boolean getDeleted();
|
||||
Boolean getEnabled();
|
||||
}
|
||||
Reference in New Issue
Block a user