From 817875c8af0200fc8458ffea85f4d020e8e6ff74 Mon Sep 17 00:00:00 2001 From: abdulkeremkilic Date: Sun, 3 Aug 2025 18:06:39 +0300 Subject: [PATCH 01/10] Add email sending functionality with Google SMTP Introduces MailService, MailController, and MailModel to enable sending emails via Google SMTP. Adds configuration classes and properties for SMTP settings, allowing email dispatch through a REST endpoint. --- .../config/smtp/AbstractSmtpConfig.java | 58 ++++++++++++++++++ .../gymcore/config/smtp/GoogleSmtpConfig.java | 9 +++ .../gymcore/controller/MailController.java | 22 +++++++ .../org/justjava/gymcore/model/MailModel.java | 4 ++ .../justjava/gymcore/service/MailService.java | 60 +++++++++++++++++++ src/main/resources/application.properties | 8 +++ 6 files changed, 161 insertions(+) create mode 100644 src/main/java/org/justjava/gymcore/config/smtp/AbstractSmtpConfig.java create mode 100644 src/main/java/org/justjava/gymcore/config/smtp/GoogleSmtpConfig.java create mode 100644 src/main/java/org/justjava/gymcore/controller/MailController.java create mode 100644 src/main/java/org/justjava/gymcore/model/MailModel.java create mode 100644 src/main/java/org/justjava/gymcore/service/MailService.java diff --git a/src/main/java/org/justjava/gymcore/config/smtp/AbstractSmtpConfig.java b/src/main/java/org/justjava/gymcore/config/smtp/AbstractSmtpConfig.java new file mode 100644 index 0000000..046a72a --- /dev/null +++ b/src/main/java/org/justjava/gymcore/config/smtp/AbstractSmtpConfig.java @@ -0,0 +1,58 @@ +package org.justjava.gymcore.config.smtp; + +public abstract class AbstractSmtpConfig { + private String host; + private String port; + private String username; //use with encryption + private String password; //use with encryption + private String auth; + private String starttls; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getAuth() { + return auth; + } + + public void setAuth(String auth) { + this.auth = auth; + } + + public String getStarttls() { + return starttls; + } + + public void setStarttls(String starttls) { + this.starttls = starttls; + } +} diff --git a/src/main/java/org/justjava/gymcore/config/smtp/GoogleSmtpConfig.java b/src/main/java/org/justjava/gymcore/config/smtp/GoogleSmtpConfig.java new file mode 100644 index 0000000..e3b58fa --- /dev/null +++ b/src/main/java/org/justjava/gymcore/config/smtp/GoogleSmtpConfig.java @@ -0,0 +1,9 @@ +package org.justjava.gymcore.config.smtp; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "smtp.google") +public class GoogleSmtpConfig extends AbstractSmtpConfig { +} diff --git a/src/main/java/org/justjava/gymcore/controller/MailController.java b/src/main/java/org/justjava/gymcore/controller/MailController.java new file mode 100644 index 0000000..e4dd9da --- /dev/null +++ b/src/main/java/org/justjava/gymcore/controller/MailController.java @@ -0,0 +1,22 @@ +package org.justjava.gymcore.controller; + +import org.justjava.gymcore.model.MailModel; +import org.justjava.gymcore.service.MailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value = "/mail") +public class MailController { + + @Autowired + private MailService mailService; + + @PostMapping("/sendMail/{to}") + public ResponseEntity sendMail(@PathVariable("to") String to, @RequestBody MailModel mailModel) { + return mailService.sendMail(to, mailModel.subject(), mailModel.body()); + } + +} diff --git a/src/main/java/org/justjava/gymcore/model/MailModel.java b/src/main/java/org/justjava/gymcore/model/MailModel.java new file mode 100644 index 0000000..16ae3b6 --- /dev/null +++ b/src/main/java/org/justjava/gymcore/model/MailModel.java @@ -0,0 +1,4 @@ +package org.justjava.gymcore.model; + +public record MailModel(String subject, String body) { +} diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java new file mode 100644 index 0000000..f8f62a7 --- /dev/null +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -0,0 +1,60 @@ +package org.justjava.gymcore.service; + +import jakarta.mail.*; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.justjava.gymcore.config.smtp.GoogleSmtpConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MailService { + @Autowired + private GoogleSmtpConfig googleSmtpConfig; + + // Host, Host e-mail, password and port will be provided under yaml file. If you want to get configurations from yaml file use smtpConfigurationService. + public ResponseEntity sendMail(String to, String subject, String body) { + Logger logger = Logger.getLogger(MailService.class.getName()); + + + Properties props = new Properties(); + props.put("mail.transport.protocol", "smtp"); + props.put("mail.smtp.host", googleSmtpConfig.getHost()); + props.put("mail.smtp.auth", googleSmtpConfig.getAuth()); + props.put("mail.smtp.port", googleSmtpConfig.getPort()); + props.put("mail.smtp.starttls.enable", googleSmtpConfig.getStarttls()); + + + Authenticator authenticator = new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(googleSmtpConfig.getUsername(), googleSmtpConfig.getPassword()); + } + }; + + Session session = Session.getDefaultInstance(props, authenticator); + + try { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(googleSmtpConfig.getUsername())); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false)); + message.setSubject(subject); + message.setText(body); + + Transport.send(message); + } catch (MessagingException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6e95269..09315f0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,3 +24,11 @@ logging.file.name=gymcore.log logging.appender.type=FILE logging.level.root=INFO logging.level.org.justjava.gymcore=INFO + +#Google SMTP configurations +smtp.google.host: smtp.gmail.com +smtp.google.port: 587 +# smtp.google.username: your desired google mail address goes here +# smtp.google.password: you generated google account application code goes here. +smtp.google.auth: true +smtp.google.starttls: true From 19e9ff7bdcb2ddafdbf52528913cd4965c3e56b8 Mon Sep 17 00:00:00 2001 From: Abdulkerem Kilic <83244648+abdulkeremkilic@users.noreply.github.com> Date: Sun, 3 Aug 2025 20:09:45 +0300 Subject: [PATCH 02/10] Update src/main/java/org/justjava/gymcore/model/MailModel.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../java/org/justjava/gymcore/model/MailModel.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/justjava/gymcore/model/MailModel.java b/src/main/java/org/justjava/gymcore/model/MailModel.java index 16ae3b6..fda8d99 100644 --- a/src/main/java/org/justjava/gymcore/model/MailModel.java +++ b/src/main/java/org/justjava/gymcore/model/MailModel.java @@ -1,4 +1,16 @@ package org.justjava.gymcore.model; -public record MailModel(String subject, String body) { +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record MailModel( + @NotBlank(message = "Email subject cannot be blank") + @Size(max = 998, message = "Email subject cannot exceed 998 characters") + String subject, + + @NotBlank(message = "Email body cannot be blank") + @Size(max = 50000, message = "Email body cannot exceed 50KB") + String body +) { +} } From 1f3adca6056d1dc5db8831ca5c0b58ff61ddb306 Mon Sep 17 00:00:00 2001 From: abdulkeremkilic Date: Sun, 3 Aug 2025 22:23:51 +0300 Subject: [PATCH 03/10] Reformat MailModel record indentation Adjusted indentation in MailModel.java for improved readability and consistency. No functional changes were made. --- .../java/org/justjava/gymcore/model/MailModel.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/justjava/gymcore/model/MailModel.java b/src/main/java/org/justjava/gymcore/model/MailModel.java index fda8d99..cd83d54 100644 --- a/src/main/java/org/justjava/gymcore/model/MailModel.java +++ b/src/main/java/org/justjava/gymcore/model/MailModel.java @@ -4,13 +4,12 @@ import jakarta.validation.constraints.Size; public record MailModel( - @NotBlank(message = "Email subject cannot be blank") - @Size(max = 998, message = "Email subject cannot exceed 998 characters") - String subject, + @NotBlank(message = "Email subject cannot be blank") + @Size(max = 998, message = "Email subject cannot exceed 998 characters") + String subject, - @NotBlank(message = "Email body cannot be blank") - @Size(max = 50000, message = "Email body cannot exceed 50KB") - String body + @NotBlank(message = "Email body cannot be blank") + @Size(max = 50000, message = "Email body cannot exceed 50KB") + String body ) { } -} From 26cd517a5f1506dc2f3f1031e6be90d0ac908ce6 Mon Sep 17 00:00:00 2001 From: Abdulkerem Kilic <83244648+abdulkeremkilic@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:17:41 +0300 Subject: [PATCH 04/10] Update src/main/java/org/justjava/gymcore/controller/MailController.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../org/justjava/gymcore/controller/MailController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/justjava/gymcore/controller/MailController.java b/src/main/java/org/justjava/gymcore/controller/MailController.java index e4dd9da..f82b55d 100644 --- a/src/main/java/org/justjava/gymcore/controller/MailController.java +++ b/src/main/java/org/justjava/gymcore/controller/MailController.java @@ -11,8 +11,11 @@ @RequestMapping(value = "/mail") public class MailController { - @Autowired - private MailService mailService; + private final MailService mailService; + + public MailController(MailService mailService) { + this.mailService = mailService; + } @PostMapping("/sendMail/{to}") public ResponseEntity sendMail(@PathVariable("to") String to, @RequestBody MailModel mailModel) { From 8c27339301a40d7a0abea7d764d987f6847af33d Mon Sep 17 00:00:00 2001 From: Abdulkerem Kilic <83244648+abdulkeremkilic@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:22:40 +0300 Subject: [PATCH 05/10] Update src/main/resources/application.properties Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/main/resources/application.properties | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 09315f0..3ae7937 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -28,7 +28,12 @@ logging.level.org.justjava.gymcore=INFO #Google SMTP configurations smtp.google.host: smtp.gmail.com smtp.google.port: 587 -# smtp.google.username: your desired google mail address goes here -# smtp.google.password: you generated google account application code goes here. +# smtp.google.username: ${SMTP_USERNAME:your-email@gmail.com} +# smtp.google.password: ${SMTP_PASSWORD:your-app-password} smtp.google.auth: true smtp.google.starttls: true + +# Security Note: +# 1. Use environment variables for credentials in production +# 2. For Gmail, use App Passwords instead of regular passwords +# 3. Enable 2FA on your Google account before generating App Passwords From 3dec3aadac628b6f2d3bb2763c906957700a8aac Mon Sep 17 00:00:00 2001 From: Abdulkerem Kilic <83244648+abdulkeremkilic@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:24:58 +0300 Subject: [PATCH 06/10] Update src/main/java/org/justjava/gymcore/service/MailService.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/main/java/org/justjava/gymcore/service/MailService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java index f8f62a7..a74acba 100644 --- a/src/main/java/org/justjava/gymcore/service/MailService.java +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -41,7 +41,7 @@ protected PasswordAuthentication getPasswordAuthentication() { } }; - Session session = Session.getDefaultInstance(props, authenticator); + Session session = Session.getInstance(props, authenticator); try { Message message = new MimeMessage(session); From fcc3b225d5db5fcaf625bc055e911908bedf010f Mon Sep 17 00:00:00 2001 From: Abdulkerem Kilic <83244648+abdulkeremkilic@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:27:09 +0300 Subject: [PATCH 07/10] Update src/main/java/org/justjava/gymcore/service/MailService.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/main/java/org/justjava/gymcore/service/MailService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java index a74acba..bde9a4c 100644 --- a/src/main/java/org/justjava/gymcore/service/MailService.java +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -23,7 +23,10 @@ public class MailService { private GoogleSmtpConfig googleSmtpConfig; // Host, Host e-mail, password and port will be provided under yaml file. If you want to get configurations from yaml file use smtpConfigurationService. - public ResponseEntity sendMail(String to, String subject, String body) { + public ResponseEntity sendMail(String to, String subject, String body) { + // existing mail‐sending logic + return ResponseEntity.ok().build(); + } Logger logger = Logger.getLogger(MailService.class.getName()); From 0413e42d4ed387ff80ee0d4d1be398c98a59c624 Mon Sep 17 00:00:00 2001 From: abdulkeremkilic Date: Mon, 4 Aug 2025 23:32:00 +0300 Subject: [PATCH 08/10] Improve MailService logging and response type Changed sendMail return type to ResponseEntity and replaced logger usage with log for better logging. Enhanced success and error log messages for email sending. --- .../java/org/justjava/gymcore/service/MailService.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java index bde9a4c..cc5238d 100644 --- a/src/main/java/org/justjava/gymcore/service/MailService.java +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -23,12 +23,7 @@ public class MailService { private GoogleSmtpConfig googleSmtpConfig; // Host, Host e-mail, password and port will be provided under yaml file. If you want to get configurations from yaml file use smtpConfigurationService. - public ResponseEntity sendMail(String to, String subject, String body) { - // existing mail‐sending logic - return ResponseEntity.ok().build(); - } - Logger logger = Logger.getLogger(MailService.class.getName()); - + public ResponseEntity sendMail(String to, String subject, String body) { Properties props = new Properties(); props.put("mail.transport.protocol", "smtp"); @@ -54,8 +49,9 @@ protected PasswordAuthentication getPasswordAuthentication() { message.setText(body); Transport.send(message); + log.info("Email sent successfully to: {}", to); } catch (MessagingException e) { - logger.log(Level.SEVERE, e.getMessage(), e); + log.error("Failed to send email to {}: {}", to, e.getMessage(), e);; } return new ResponseEntity<>(HttpStatus.OK); From 6c72aefc4ae03e5b5795637b8ea655077dee98ce Mon Sep 17 00:00:00 2001 From: abdulkeremkilic Date: Tue, 5 Aug 2025 23:36:04 +0300 Subject: [PATCH 09/10] Improve mail sending error handling and validation Refactored MailController and MailService to provide better error handling and email address validation. MailController now returns a success or error message, and MailService throws MessagingException instead of handling it internally. Removed unused imports and simplified constructor usage with Lombok's @RequiredArgsConstructor. --- .../gymcore/controller/MailController.java | 24 +++++++++++++------ .../justjava/gymcore/service/MailService.java | 16 ++----------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/justjava/gymcore/controller/MailController.java b/src/main/java/org/justjava/gymcore/controller/MailController.java index f82b55d..09f9780 100644 --- a/src/main/java/org/justjava/gymcore/controller/MailController.java +++ b/src/main/java/org/justjava/gymcore/controller/MailController.java @@ -1,25 +1,35 @@ package org.justjava.gymcore.controller; +import jakarta.mail.MessagingException; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import lombok.RequiredArgsConstructor; import org.justjava.gymcore.model.MailModel; import org.justjava.gymcore.service.MailService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping(value = "/mail") +@RequiredArgsConstructor public class MailController { private final MailService mailService; - public MailController(MailService mailService) { - this.mailService = mailService; - } - @PostMapping("/sendMail/{to}") - public ResponseEntity sendMail(@PathVariable("to") String to, @RequestBody MailModel mailModel) { - return mailService.sendMail(to, mailModel.subject(), mailModel.body()); + public ResponseEntity sendMail( @PathVariable("to") + @Email(message = "Invalid email address format") + String to, + @Valid @RequestBody MailModel mailModel) { + try { + mailService.sendMail(to, mailModel.subject(), mailModel.body()); + return ResponseEntity.ok() + .body("Email sent successfully to: " + to); + } catch (MessagingException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Failed to send email: " + e.getMessage()); + } } } diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java index cc5238d..f090440 100644 --- a/src/main/java/org/justjava/gymcore/service/MailService.java +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -6,24 +6,19 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.justjava.gymcore.config.smtp.GoogleSmtpConfig; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; @Slf4j @Service @RequiredArgsConstructor public class MailService { - @Autowired + private GoogleSmtpConfig googleSmtpConfig; // Host, Host e-mail, password and port will be provided under yaml file. If you want to get configurations from yaml file use smtpConfigurationService. - public ResponseEntity sendMail(String to, String subject, String body) { + public void sendMail(String to, String subject, String body) throws MessagingException { Properties props = new Properties(); props.put("mail.transport.protocol", "smtp"); @@ -40,8 +35,6 @@ protected PasswordAuthentication getPasswordAuthentication() { }; Session session = Session.getInstance(props, authenticator); - - try { Message message = new MimeMessage(session); message.setFrom(new InternetAddress(googleSmtpConfig.getUsername())); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false)); @@ -50,10 +43,5 @@ protected PasswordAuthentication getPasswordAuthentication() { Transport.send(message); log.info("Email sent successfully to: {}", to); - } catch (MessagingException e) { - log.error("Failed to send email to {}: {}", to, e.getMessage(), e);; - } - - return new ResponseEntity<>(HttpStatus.OK); } } From 333cccca12c18c60d7ae920838d1905995327b00 Mon Sep 17 00:00:00 2001 From: abdulkeremkilic Date: Tue, 5 Aug 2025 23:41:07 +0300 Subject: [PATCH 10/10] Make googleSmtpConfig final in MailService Changed googleSmtpConfig to be final in MailService to ensure immutability and proper usage with @RequiredArgsConstructor. --- src/main/java/org/justjava/gymcore/service/MailService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/justjava/gymcore/service/MailService.java b/src/main/java/org/justjava/gymcore/service/MailService.java index f090440..eb300f8 100644 --- a/src/main/java/org/justjava/gymcore/service/MailService.java +++ b/src/main/java/org/justjava/gymcore/service/MailService.java @@ -15,7 +15,7 @@ @RequiredArgsConstructor public class MailService { - private GoogleSmtpConfig googleSmtpConfig; + private final GoogleSmtpConfig googleSmtpConfig; // Host, Host e-mail, password and port will be provided under yaml file. If you want to get configurations from yaml file use smtpConfigurationService. public void sendMail(String to, String subject, String body) throws MessagingException {