diff --git a/pom.xml b/pom.xml index ccbf697..4ff00b9 100644 --- a/pom.xml +++ b/pom.xml @@ -38,14 +38,15 @@ - scm:git:https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git + scm:git:https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git + ssh://git@gitea.gofwd.group:2225/Forward_Group/ballistic-builder-spring.git - 17 + 21 ${java.version} ${java.version} @@ -63,6 +64,25 @@ --> + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + + + org.springframework.boot + spring-boot-starter-mail + + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + org.springframework.boot spring-boot-starter-web @@ -155,8 +175,9 @@ maven-compiler-plugin 3.11.0 - ${maven.compiler.source} - ${maven.compiler.target} + 21 + 21 + --enable-preview diff --git a/sql/email_requests_schema.sql b/sql/email_requests_schema.sql new file mode 100644 index 0000000..2fac6cf --- /dev/null +++ b/sql/email_requests_schema.sql @@ -0,0 +1,13 @@ +CREATE TABLE email_requests ( + id BIGSERIAL PRIMARY KEY, + recipient VARCHAR(255) NOT NULL, + subject VARCHAR(255) NOT NULL, + body TEXT, + sent_at TIMESTAMP, + status VARCHAR(50) NOT NULL, + error_message TEXT, + created_at TIMESTAMP NOT NULL +); + +CREATE INDEX idx_email_requests_status ON email_requests(status); +CREATE INDEX idx_email_requests_created_at ON email_requests(created_at); diff --git a/src/main/java/group/goforward/battlbuilder/ApiResponse.java b/src/main/java/group/goforward/battlbuilder/ApiResponse.java index d23af40..4efb1bf 100644 --- a/src/main/java/group/goforward/battlbuilder/ApiResponse.java +++ b/src/main/java/group/goforward/battlbuilder/ApiResponse.java @@ -24,12 +24,12 @@ public class ApiResponse { this.timestamp = LocalDateTime.now(); } - public static ApiResponse success(T data, String message) { - String[] msg = {message}; - return new ApiResponse<>(API_SUCCESS, msg, data); + public static ApiResponse error(String message, T data) { + String[] msg = {message}; // ✅ Include the message + return new ApiResponse<>(API_ERROR, msg, data); } - public static ApiResponse success(T data) { + public static ApiResponse success(T data, String emailSentSuccessfully) { String[] msg = {}; return new ApiResponse<>(API_SUCCESS, msg, data); } @@ -42,6 +42,7 @@ public class ApiResponse { return new ApiResponse<>(API_ERROR, msg, null); } + public String[] getMessages() { return messages; } diff --git a/src/main/java/group/goforward/battlbuilder/configuration/PasswordConfig.java b/src/main/java/group/goforward/battlbuilder/configuration/PasswordConfig.java index 1d7374a..7533c48 100644 --- a/src/main/java/group/goforward/battlbuilder/configuration/PasswordConfig.java +++ b/src/main/java/group/goforward/battlbuilder/configuration/PasswordConfig.java @@ -1,11 +1,17 @@ +/* package group.goforward.battlbuilder.configuration; -// @Configuration -// public class PasswordConfig { +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; -// @Bean -// public PasswordEncoder passwordEncoder() { +@Configuration + public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { // // BCrypt default password -// return new BCryptPasswordEncoder(); -// } -// } \ No newline at end of file + return new BCryptPasswordEncoder(); + } + }*/ diff --git a/src/main/java/group/goforward/battlbuilder/controllers/admin/PartCategoryAdminController.java b/src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPartCategoryController.java similarity index 90% rename from src/main/java/group/goforward/battlbuilder/controllers/admin/PartCategoryAdminController.java rename to src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPartCategoryController.java index 130b9c4..5ae1c30 100644 --- a/src/main/java/group/goforward/battlbuilder/controllers/admin/PartCategoryAdminController.java +++ b/src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPartCategoryController.java @@ -9,11 +9,11 @@ import java.util.List; @RestController @RequestMapping("/api/admin/part-categories") @CrossOrigin // keep it loose for now, you can tighten origins later -public class PartCategoryAdminController { +public class AdminPartCategoryController { private final PartCategoryRepository partCategories; - public PartCategoryAdminController(PartCategoryRepository partCategories) { + public AdminPartCategoryController(PartCategoryRepository partCategories) { this.partCategories = partCategories; } diff --git a/src/main/java/group/goforward/battlbuilder/controllers/admin/PlatformController.java b/src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPlatformController.java similarity index 91% rename from src/main/java/group/goforward/battlbuilder/controllers/admin/PlatformController.java rename to src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPlatformController.java index 223ce93..307ef24 100644 --- a/src/main/java/group/goforward/battlbuilder/controllers/admin/PlatformController.java +++ b/src/main/java/group/goforward/battlbuilder/controllers/admin/AdminPlatformController.java @@ -13,11 +13,11 @@ import java.util.List; @RestController @RequestMapping("/api/platforms") @CrossOrigin -public class PlatformController { +public class AdminPlatformController { private final PlatformRepository platformRepository; - public PlatformController(PlatformRepository platformRepository) { + public AdminPlatformController(PlatformRepository platformRepository) { this.platformRepository = platformRepository; } diff --git a/src/main/java/group/goforward/battlbuilder/controllers/utils/EmailController.java b/src/main/java/group/goforward/battlbuilder/controllers/utils/EmailController.java new file mode 100644 index 0000000..56baedd --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/controllers/utils/EmailController.java @@ -0,0 +1,61 @@ + +package group.goforward.battlbuilder.controllers.utils; + +import group.goforward.battlbuilder.ApiResponse; +import group.goforward.battlbuilder.dto.EmailRequestDto; +import group.goforward.battlbuilder.model.EmailRequest; +import group.goforward.battlbuilder.services.utils.EmailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/email") +public class EmailController { + + private static final String EMAIL_STATUS_SENT = "SENT"; + + private final EmailService emailService; + + @Autowired + public EmailController(EmailService emailService) { + this.emailService = emailService; + } + + @PostMapping("/send") + public ResponseEntity> sendEmail(@RequestBody EmailRequestDto emailDto) { + try { + EmailRequest emailRequest = emailService.sendEmail( + emailDto.getRecipient(), + emailDto.getSubject(), + emailDto.getBody() + ); + return buildEmailResponse(emailRequest); + } catch (Exception e) { + return buildErrorResponse(e.getMessage()); + } + } + + private ResponseEntity> buildEmailResponse(EmailRequest emailRequest) { + if (EMAIL_STATUS_SENT.equals(emailRequest.getStatus())) { + return ResponseEntity.ok( + ApiResponse.success(emailRequest, "Email sent successfully") + ); + } else { + String errorMessage = "Failed to send email: " + emailRequest.getErrorMessage(); + return ResponseEntity.status(500).body( + ApiResponse.error(errorMessage, emailRequest) + ); + } + } + + private ResponseEntity> buildErrorResponse(String exceptionMessage) { + String errorMessage = "Error processing email request: " + exceptionMessage; + return ResponseEntity.status(500).body( + ApiResponse.error(errorMessage, null) + ); + } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java b/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java new file mode 100644 index 0000000..4c375db --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/dto/EmailRequestDto.java @@ -0,0 +1,33 @@ +package group.goforward.battlbuilder.dto; + +// DTO for request +public class EmailRequestDto { + private String recipient; + private String subject; + private String body; + + public String getRecipient() { + return recipient; + } + + public void setRecipient(String recipient) { + this.recipient = recipient; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } +} + diff --git a/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java b/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java new file mode 100644 index 0000000..8e785a7 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/model/EmailRequest.java @@ -0,0 +1,107 @@ +package group.goforward.battlbuilder.model; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "email_requests") +public class EmailRequest { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String recipient; + + @Column(nullable = false) + private String subject; + + @Column(columnDefinition = "TEXT") + private String body; + + @Column(name = "sent_at") + private LocalDateTime sentAt; + + @Column(nullable = false) + private String status; // PENDING, SENT, FAILED + + @Column(name = "error_message") + private String errorMessage; + + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @PrePersist + protected void onCreate() { + createdAt = LocalDateTime.now(); + if (status == null) { + status = "PENDING"; + } + } + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getRecipient() { + return recipient; + } + + public void setRecipient(String recipient) { + this.recipient = recipient; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public LocalDateTime getSentAt() { + return sentAt; + } + + public void setSentAt(LocalDateTime sentAt) { + this.sentAt = sentAt; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/src/main/java/group/goforward/battlbuilder/repos/EmailRequestRepository.java b/src/main/java/group/goforward/battlbuilder/repos/EmailRequestRepository.java new file mode 100644 index 0000000..4ec6b32 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/repos/EmailRequestRepository.java @@ -0,0 +1,12 @@ +package group.goforward.battlbuilder.repos; + +import group.goforward.battlbuilder.model.EmailRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface EmailRequestRepository extends JpaRepository { + List findByStatus(String status); +} diff --git a/src/main/java/group/goforward/battlbuilder/services/utils/EmailService.java b/src/main/java/group/goforward/battlbuilder/services/utils/EmailService.java new file mode 100644 index 0000000..056acdb --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/services/utils/EmailService.java @@ -0,0 +1,7 @@ +package group.goforward.battlbuilder.services.utils; + +import group.goforward.battlbuilder.model.EmailRequest; + +public interface EmailService { + EmailRequest sendEmail(String recipient, String subject, String body); +} diff --git a/src/main/java/group/goforward/battlbuilder/services/utils/impl/EmailServiceImpl.java b/src/main/java/group/goforward/battlbuilder/services/utils/impl/EmailServiceImpl.java new file mode 100644 index 0000000..72388bc --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/services/utils/impl/EmailServiceImpl.java @@ -0,0 +1,60 @@ +package group.goforward.battlbuilder.services.utils.impl; + +import group.goforward.battlbuilder.model.EmailRequest; +import group.goforward.battlbuilder.repos.EmailRequestRepository; +import group.goforward.battlbuilder.services.utils.EmailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +public class EmailServiceImpl implements EmailService { + + @Autowired + private JavaMailSender mailSender; + + @Autowired + private EmailRequestRepository emailRequestRepository; + + @Value("${spring.mail.username}") + private String fromEmail; + + @Transactional + public EmailRequest sendEmail(String recipient, String subject, String body) { + // Create and save email request + EmailRequest emailRequest = new EmailRequest(); + emailRequest.setRecipient(recipient); + emailRequest.setSubject(subject); + emailRequest.setBody(body); + emailRequest.setStatus("PENDING"); + + emailRequest = emailRequestRepository.save(emailRequest); + + try { + // Send email + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(fromEmail); + message.setTo(recipient); + message.setSubject(subject); + message.setText(body); + + mailSender.send(message); + + // Update status + emailRequest.setStatus("SENT"); + emailRequest.setSentAt(LocalDateTime.now()); + + } catch (Exception e) { + // Update status with error + emailRequest.setStatus("FAILED"); + emailRequest.setErrorMessage(e.getMessage()); + } + + return emailRequestRepository.save(emailRequest); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e91149f..cd0b324 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,3 +24,11 @@ spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp +# SMTP Configuration +spring.mail.host=smtp.battl.builder +spring.mail.port=587 +spring.mail.username=info@battl.builder +spring.mail.password=Cul8rman2025! +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/views/platform-manager.jsp b/src/main/webapp/WEB-INF/views/platform-manager.jsp index db90424..abb2a54 100644 --- a/src/main/webapp/WEB-INF/views/platform-manager.jsp +++ b/src/main/webapp/WEB-INF/views/platform-manager.jsp @@ -3,7 +3,7 @@ - Platform Manager + BattlBuilder Platform Manager