Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RestroHub/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
logs/
2 changes: 2 additions & 0 deletions RestroHub/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public ResponseEntity<ApiResponse<AuthResponse>> login(
return ResponseEntity.ok(ApiResponse.success(authResponse, "Login successful"));
}



@PostMapping("/refresh")
@Operation(
summary = "Refresh access token",
Expand Down Expand Up @@ -147,7 +149,36 @@ public ResponseEntity<ApiResponse<AuthResponse>> refreshToken(

return ResponseEntity.ok(ApiResponse.success(authResponse, "Token refreshed successfully"));
}

@PostMapping("/forgot-password")
public ResponseEntity<String> 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<String> 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");
}
Comment thread
ARYAN-MISHRA-2006 marked this conversation as resolved.
authService.resetPassword(token, newPassword);

return ResponseEntity.ok("Password reset successful");
}
Comment thread
ARYAN-MISHRA-2006 marked this conversation as resolved.

@PostMapping("/logout")
@Operation(
summary = "Logout user",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ public interface AuthService {
AuthResponse refreshToken(RefreshTokenRequest refreshTokenRequest);

void logout(String token);

void forgotPassword(String email);

void resetPassword(String token, String newPassword);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,25 @@
import java.util.List;
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
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) {
Expand Down Expand Up @@ -116,4 +127,39 @@ public void logout(String token) {
SecurityContextHolder.clearContext();
log.info("User logged out successfully");
}

@Override
public void forgotPassword(String email) {

User user = userRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("User not found"));

String token = UUID.randomUUID().toString();

user.setResetToken(token);
user.setResetTokenExpiry(LocalDateTime.now().plusMinutes(30));

userRepository.save(user);

emailService.sendResetEmail(user.getEmail(), token);
Comment thread
ARYAN-MISHRA-2006 marked this conversation as resolved.
Outdated
}
@Override
public void resetPassword(String token, String newPassword) {

User user = userRepository.findByResetToken(token)
.orElseThrow(() -> new RuntimeException("Invalid reset token"));

if (user.getResetTokenExpiry().isBefore(LocalDateTime.now())) {
throw new RuntimeException("Reset token expired");
}

user.setPassword(passwordEncoder.encode(newPassword));

user.setResetToken(null);
user.setResetTokenExpiry(null);

userRepository.save(user);

System.out.println("Password reset successful");
}
}
Original file line number Diff line number Diff line change
@@ -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");
Comment thread
ARYAN-MISHRA-2006 marked this conversation as resolved.

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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class SecurityConfig {
"/api/v1/users/register",
// Public api endpoints
"/public/api/v1/**"


};

private static final String[] PUBLIC_GET_URLS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByEmail(String email);

Optional<User> findByResetToken(String resetToken);

boolean existsByEmail(String email);

boolean existsByEmailAndUserIdNot(String email, Long userId);
Expand Down
4 changes: 2 additions & 2 deletions RestroHub/src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Comment thread
ARYAN-MISHRA-2006 marked this conversation as resolved.
spring.datasource.driver-class-name=org.postgresql.Driver
# ===============================
# HikariCP
Expand Down Expand Up @@ -52,4 +52,4 @@ spring.servlet.multipart.max-request-size=10MB
security.jwt.secret=${JWT_SECRET:your-256-bit-secret-key-here-change-in-production}
security.jwt.expiration=${JWT_EXPIRATION:86400000}
security.jwt.refresh-expiration=${JWT_REFRESH_EXPIRATION:604800000}
security.cors.allowed-origins=http://localhost:5173,http://localhost:3000,http://localhost:3002
security.cors.allowed-origins=http://localhost:5173,http://localhost:3000,http://localhost:3002