diff --git a/RestroHub/.gitignore b/RestroHub/.gitignore new file mode 100644 index 00000000..d9ec3be2 --- /dev/null +++ b/RestroHub/.gitignore @@ -0,0 +1 @@ +logs/ diff --git a/RestroHub/build.gradle b/RestroHub/build.gradle index 8af798a7..6d2aa188 100644 --- a/RestroHub/build.gradle +++ b/RestroHub/build.gradle @@ -84,6 +84,8 @@ dependencies { // Logging (JSON format for production) implementation 'ch.qos.logback.contrib:logback-json-classic:0.1.5' implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5' + + implementation 'org.springframework.boot:spring-boot-starter-mail' } // tasks.named('test') { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java index 2000c370..297ddfda 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java @@ -228,7 +228,36 @@ public ResponseEntity> refreshToken( return ResponseEntity.ok(ApiResponse.success(authResponse, "Token refreshed successfully")); } + @PostMapping("/forgot-password") +public ResponseEntity forgotPassword(@RequestParam String email) { + if(email == null || email.trim().isEmpty()) { + return ResponseEntity.badRequest().body("Email cannot be empty"); +} +if(!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) { + return ResponseEntity.badRequest().body("Invalid email format"); +} + authService.forgotPassword(email); + + return ResponseEntity.ok("Reset token generated successfully"); +} + +@PostMapping("/reset-password") +public ResponseEntity resetPassword( + @RequestParam String token, + @RequestParam String newPassword) { + if(newPassword == null || newPassword.trim().isEmpty()) { + return ResponseEntity.badRequest().body("Password cannot be empty"); +} + +if(newPassword.length() < 6) { + return ResponseEntity.badRequest().body("Password must be at least 6 characters"); +} + authService.resetPassword(token, newPassword); + + return ResponseEntity.ok("Password reset successful"); +} + @PostMapping("/logout") @Operation( summary = "Logout user", diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java index 97eaae65..aaeb42dd 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java @@ -13,4 +13,8 @@ public interface AuthService { AuthResponse refreshToken(RefreshTokenRequest refreshTokenRequest); void logout(String token); + + void forgotPassword(String email); + + void resetPassword(String token, String newPassword); } \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java index b84736aa..3fcec250 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java @@ -30,6 +30,13 @@ import java.util.Collections; import java.util.stream.Collectors; +import com.restroly.qrmenu.user.entity.User; +import com.restroly.qrmenu.user.repository.UserRepository; + +import java.time.LocalDateTime; +import java.util.UUID; +import org.springframework.security.crypto.password.PasswordEncoder; + @Service @RequiredArgsConstructor @Slf4j @@ -41,6 +48,9 @@ public class AuthServiceImpl implements AuthService { private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; private final UserDetailsService userDetailsService; + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final EmailService emailService; @Override public AuthResponse login(LoginRequest loginRequest) { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java new file mode 100644 index 00000000..2079b7ee --- /dev/null +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java @@ -0,0 +1,31 @@ +package com.restroly.qrmenu.auth.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailService { + + private final JavaMailSender mailSender; + + public void sendResetEmail(String toEmail, String token) { + + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(System.getenv("MAIL_USERNAME")); + message.setTo(toEmail); + message.setSubject("Password Reset Request"); + + message.setText( + "Click the link below to reset your password:\n\n" + + "https://example.com/reset-password?token=" + + token + ); + + mailSender.send(message); + + System.out.println("Reset email sent successfully"); + } +} \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java index 1db1c176..d607d100 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java @@ -29,6 +29,12 @@ public class User { @Column(name = "user_password", nullable = false) private String password; + @Column(name = "reset_token") + private String resetToken; + + @Column(name = "reset_token_expiry") + private LocalDateTime resetTokenExpiry; + @Column(name = "phone_number") private String phoneNumber; diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java index 76fd6f48..01fa38ab 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java @@ -16,6 +16,8 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + Optional findByResetToken(String resetToken); + boolean existsByEmail(String email); boolean existsByEmailAndUserIdNot(String email, Long userId); diff --git a/RestroHub/src/main/resources/application-dev.properties b/RestroHub/src/main/resources/application-dev.properties index 4fefd091..2b4ccb71 100644 --- a/RestroHub/src/main/resources/application-dev.properties +++ b/RestroHub/src/main/resources/application-dev.properties @@ -3,7 +3,7 @@ # =============================== spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/RestroHub_DB spring.datasource.username=${DB_USERNAME:postgres} -spring.datasource.password=${DB_PASSWORD:postgres} +spring.datasource.password=${DB_PASSWORD:${DB_PASSWORD}} spring.datasource.driver-class-name=org.postgresql.Driver # =============================== # HikariCP