package ru.soune.nocopy.service.mail; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; 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.transaction.annotation.Transactional; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.EmailVerificationToken; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.repository.EmailVerificationTokenRepository; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.time.LocalDateTime; import java.util.Map; @Service @RequiredArgsConstructor @Slf4j public class EmailService { private final JavaMailSender mailSender; private final EmailVerificationTokenRepository emailVerificationTokenRepository; @Value("${app.email.verification.standart-subject}") private String standartSubject; @Value("${app.email.verification.standart-address-from}") private String standartAdressFrom; @Value("${app.email.verification.user}") private String user; @Value("${app.email.verification.drop-password-subject}") private String dropPasswordSubject; // @Value("${server.baseurl}") // private String baseUrl; @Value("${BASE_URL}") private String baseUrl; @Value("${server.front.port}") private String frontPort; public void sendEmail(String from, String sendTo, String subject, String body) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); helper.setFrom(from); helper.setTo(sendTo); helper.setSubject(subject); helper.setText(body, true); mailSender.send(message); } public void sendVerificationEmail(User user, String code) throws MessagingException, IOException { String htmlContent = loadAndProcessTemplate(Map.of("username", user.getFullName(), "code", code), "templates/email-verification.html"); sendEmail("noreply@no-copy.ru", user.getEmail(), standartAdressFrom, htmlContent); } public void sendTokensNotFoundEmail(User user, Integer currentTokens, Integer missingTokens) throws MessagingException, IOException { String htmlContent = loadAndProcessTemplate(Map.of("username", user.getFullName(), "current_tokens", String.valueOf(currentTokens), "missing_tokens", String.valueOf(missingTokens), "link_to_token_board", baseUrl + "/pages/payment"), "templates/token-not-found.html"); sendEmail("noreply@no-copy.ru", user.getEmail(), standartAdressFrom, htmlContent); } @Transactional public EmailVerificationToken createEmailVerificationToken(AuthToken authToken) { EmailVerificationToken existingToken = emailVerificationTokenRepository .findByUserId(authToken.getUser().getId()); if (existingToken != null) { emailVerificationTokenRepository.deleteById(existingToken.getId()); } EmailVerificationToken emailToken = new EmailVerificationToken(); emailToken.setUser(authToken.getUser()); emailToken.setToken(generateVerificationCode()); emailToken.setCreatedAt(LocalDateTime.now()); return emailVerificationTokenRepository.save(emailToken); } private String generateVerificationCode() { SecureRandom random = new SecureRandom(); int code = 100000 + random.nextInt(900000); return String.valueOf(code); } private String loadAndProcessTemplate(String username, String code, String templatePath) throws IOException { ClassPathResource resource = new ClassPathResource(templatePath); String template = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); String safeUsername = escapeHtml(username != null ? username : user); String safeCode = escapeHtml(code); return template .replace("{{username}}", safeUsername) .replace("{{code}}", safeCode); } private String loadAndProcessTemplate(Map fieldsForTemplate, String templatePath) throws IOException { ClassPathResource resource = new ClassPathResource(templatePath); String template = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); for (Map.Entry entry : fieldsForTemplate.entrySet()) { template = template.replace("{{" + entry.getKey() + "}}", escapeHtml(entry.getValue())); } return template; } private String escapeHtml(String text) { if (text == null) return ""; return text .replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """) .replace("'", "'"); } public void sendResetPasswordEmail(User user, String code) { try { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); helper.setFrom(standartAdressFrom); helper.setTo(user.getEmail()); helper.setSubject(dropPasswordSubject); String htmlContent = loadAndProcessTemplate(user.getFullName(), code, "templates/reset-password.html"); //TODO frontPort убрать после настройки редиректа nginx String resetLink = escapeHtml(baseUrl + "/reset-password?email=" + user.getEmail()); htmlContent = htmlContent.replace("{{resetLink}}", resetLink); helper.setText(htmlContent, true); mailSender.send(message); log.info("Email sent to: {}", user.getEmail()); } catch (Exception e) { log.error("Failed to send email: {}", e.getMessage()); } } }