Skip to content

Commit e63aaab

Browse files
Returning Object instead of String, DTO Added
1 parent eae790a commit e63aaab

File tree

12 files changed

+190
-71
lines changed

12 files changed

+190
-71
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.spacehub.DTO;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class EmailRequest {
7+
8+
private String email;
9+
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.spacehub.DTO;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class LoginRequest {
7+
8+
private String email;
9+
private String password;
10+
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.spacehub.DTO;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class OTPRequest {
7+
8+
private String email;
9+
private String otp;
10+
11+
}
Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package org.spacehub.controller;
22

3+
import org.spacehub.DTO.EmailRequest;
4+
import org.spacehub.DTO.LoginRequest;
5+
import org.spacehub.DTO.OTPRequest;
6+
import org.spacehub.entities.ApiResponse;
37
import org.spacehub.entities.User;
48
import org.spacehub.entities.RegistrationRequest;
59
import org.spacehub.security.EmailValidator;
610
import org.spacehub.service.OTPService;
11+
import org.spacehub.service.UserService;
712
import org.spacehub.service.VerificationService;
813
import org.spacehub.service.RegistrationService;
914
import org.springframework.beans.factory.annotation.Autowired;
1015
import org.springframework.http.ResponseEntity;
11-
import org.springframework.web.bind.annotation.GetMapping;
1216
import org.springframework.web.bind.annotation.PostMapping;
1317
import org.springframework.web.bind.annotation.RequestBody;
14-
import org.springframework.web.bind.annotation.RequestParam;
1518
import org.springframework.web.bind.annotation.RequestMapping;
1619
import org.springframework.web.bind.annotation.RestController;
1720

@@ -26,68 +29,86 @@ public class UserController {
2629
@Autowired
2730
private OTPService otpService;
2831

32+
@Autowired
33+
private UserService userService;
34+
2935
public UserController(VerificationService verificationService, RegistrationService registrationService, EmailValidator emailValidator) {
3036
this.verificationService = verificationService;
3137
this.registrationService = registrationService;
3238
this.emailValidator = emailValidator;
3339
}
3440

3541
@PostMapping("/login")
36-
public ResponseEntity<String> login(@RequestBody User user) {
37-
String email = emailValidator.normalize(user.getEmail());
42+
public ResponseEntity<ApiResponse<String>> login(@RequestBody LoginRequest request) {
43+
String email = emailValidator.normalize(request.getEmail());
3844
if (!emailValidator.test(email)) {
39-
return ResponseEntity.badRequest().body("Invalid email format!");
45+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "Invalid email format!", null));
4046
}
47+
48+
User user = new User();
4149
user.setEmail(email);
50+
user.setPassword(request.getPassword());
51+
4252
String token = verificationService.check(user);
43-
if (token == null) return ResponseEntity.status(401).body("Invalid credentials");
44-
return ResponseEntity.ok(token);
53+
if (token == null) {
54+
return ResponseEntity.status(401).body(new ApiResponse<>(401, "Invalid credentials", null));
55+
}
56+
return ResponseEntity.ok(new ApiResponse<>(200, "Login successful", token));
4557
}
4658

4759
@PostMapping("/registration")
48-
public ResponseEntity<String> register(@RequestBody RegistrationRequest request) {
60+
public ResponseEntity<ApiResponse<String>> register(@RequestBody RegistrationRequest request) {
4961
String email = emailValidator.normalize(request.getEmail());
5062
if (!emailValidator.test(email)) {
51-
return ResponseEntity.badRequest().body("Invalid email format!");
63+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "Invalid email format!", null));
5264
}
5365
request.setEmail(email);
5466
try {
55-
String result = registrationService.register(request);
56-
return ResponseEntity.status(201).body(result);
57-
} catch (IllegalStateException ex) {
58-
return ResponseEntity.badRequest().body(ex.getMessage());
59-
} catch (Exception ex) {
60-
return ResponseEntity.internalServerError().body("Registration failed");
67+
String token = registrationService.register(request);
68+
return ResponseEntity.status(201).body(new ApiResponse<>(201, "Registration successful", token));
69+
} catch (IllegalStateException e) {
70+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, e.getMessage(), null));
71+
} catch (Exception e) {
72+
return ResponseEntity.internalServerError().body(new ApiResponse<>(500, "Registration failed", null));
6173
}
6274
}
6375

64-
@GetMapping("/sendotp")
65-
public ResponseEntity<String> sendOTP(@RequestParam(required = false) String email) {
66-
email = emailValidator.normalize(email);
76+
@PostMapping("/sendotp")
77+
public ResponseEntity<ApiResponse<String>> sendOTP(@RequestBody EmailRequest request) {
78+
String email = emailValidator.normalize(request.getEmail());
6779
if (email == null || !emailValidator.test(email)) {
68-
return ResponseEntity.badRequest().body("Invalid or missing email!");
80+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "Invalid or missing email!", null));
6981
}
70-
try {
71-
boolean allowed = otpService.canSendOTP(email);
72-
if (!allowed) {
73-
long secondsLeft = otpService.cooldownTime(email);
74-
return ResponseEntity.badRequest().body("Please wait " + secondsLeft + " seconds before requesting OTP again.");
75-
}
76-
otpService.sendOTP(email);
77-
return ResponseEntity.ok("OTP sent successfully to " + email);
78-
} catch (Exception e) {
79-
return ResponseEntity.status(500).body("Error sending OTP: " + e.getMessage());
82+
if (!otpService.canSendOTP(email)) {
83+
long secondsLeft = otpService.cooldownTime(email);
84+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "Please wait " + secondsLeft + " seconds before requesting OTP again.", null));
8085
}
86+
otpService.sendOTP(email);
87+
return ResponseEntity.ok(new ApiResponse<>(200, "OTP sent successfully to " + email, null));
8188
}
8289

83-
@GetMapping("/validateotp")
84-
public ResponseEntity<String> validateOTP(@RequestParam String otp) {
85-
boolean valid = otpService.validateOTP(otp);
90+
@PostMapping("/validateotp")
91+
public ResponseEntity<ApiResponse<String>> validateOTP(@RequestBody OTPRequest request) {
92+
boolean valid = otpService.validateOTP(request.getEmail(), request.getOtp());
8693
if (valid) {
87-
return ResponseEntity.ok("OTP is valid");
94+
return ResponseEntity.ok(new ApiResponse<>(200, "OTP is valid", null));
8895
} else {
89-
return ResponseEntity.status(400).body("OTP is invalid or expired");
96+
return ResponseEntity.status(400).body(new ApiResponse<>(400, "OTP is invalid or expired", null));
97+
}
98+
}
99+
100+
@PostMapping("/forgotpassword")
101+
public ResponseEntity<ApiResponse<String>> forgotPassword(@RequestBody EmailRequest request) {
102+
String email = emailValidator.normalize(request.getEmail());
103+
if (email == null || !emailValidator.test(email)) {
104+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "Invalid email format!", null));
90105
}
106+
if (!userService.checkUser(email)) {
107+
return ResponseEntity.badRequest().body(new ApiResponse<>(400, "User with this email does not exist", null));
108+
}
109+
otpService.sendOTP(email);
110+
return ResponseEntity.ok(new ApiResponse<>(200, "OTP sent to your email", null));
91111
}
92112

113+
93114
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.spacehub.entities;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
@Data
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class ApiResponse<T> {
11+
12+
private int status;
13+
private String message;
14+
private T data;
15+
16+
}

src/main/java/org/spacehub/entities/MobileSmsRequest.java

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.spacehub.entities;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
import java.time.Instant;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@Entity
12+
@Table(name = "otps")
13+
public class OTP {
14+
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
private Long id;
18+
19+
@Column(nullable = false)
20+
private String email;
21+
22+
@Column(nullable = false)
23+
private String code;
24+
25+
@Column(nullable = false)
26+
private Instant createdAt;
27+
28+
@Column(nullable = false)
29+
private Instant expiresAt;
30+
31+
}

src/main/java/org/spacehub/entities/User.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public class User implements UserDetails {
4343
private String password;
4444
@Enumerated(EnumType.STRING)
4545
private UserRole userRole;
46-
private Boolean locked = false; // Determine if account is locked
47-
private Boolean enabled = false; // For after email verification
46+
private Boolean locked = false;
47+
private Boolean enabled = false;
4848

4949
public User(String firstName,
5050
String lastName,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.spacehub.repository;
2+
3+
import org.spacehub.entities.OTP;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
import java.util.Optional;
8+
9+
@Repository
10+
public interface OTPRepository extends JpaRepository<OTP, Long> {
11+
12+
Optional<OTP> findTopByEmail(String email);
13+
14+
Optional<OTP> findByEmailAndCode(String email, String code);
15+
}
Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package org.spacehub.service;
22

3+
import org.spacehub.entities.OTP;
4+
import org.spacehub.repository.OTPRepository;
35
import org.springframework.beans.factory.annotation.Autowired;
46
import org.springframework.mail.SimpleMailMessage;
57
import org.springframework.mail.javamail.JavaMailSender;
68
import org.springframework.stereotype.Service;
79

10+
import java.time.Duration;
811
import java.time.Instant;
9-
import java.util.HashMap;
10-
import java.util.Map;
12+
import java.util.Optional;
1113
import java.util.Random;
1214

1315
@Service
@@ -16,10 +18,10 @@ public class OTPService {
1618
@Autowired
1719
private JavaMailSender mailSender;
1820

19-
private final String DEFAULT_EMAIL = "[email protected]";
20-
private final Map<String, String> otpMap = new HashMap<>();
21-
private final Map<String, Instant> sendTimeMap = new HashMap<>();
21+
@Autowired
22+
private OTPRepository otpRepository;
2223

24+
private final String DEFAULT_EMAIL = "[email protected]";
2325
private static final int expirySeconds = 300;
2426
private static final int cooldownSeconds = 60;
2527

@@ -29,35 +31,43 @@ public void sendOTP(String email) {
2931
}
3032

3133
int num = new Random().nextInt(1000000);
32-
String otp = String.format("%06d", num);
34+
String otpCode = String.format("%06d", num);
35+
Instant now = Instant.now();
3336

34-
otpMap.put(email, otp);
35-
sendTimeMap.put(email, Instant.now());
37+
OTP otp = new OTP();
38+
otp.setEmail(email);
39+
otp.setCode(otpCode);
40+
otp.setCreatedAt(now);
41+
otp.setExpiresAt(now.plusSeconds(expirySeconds));
42+
43+
otpRepository.save(otp);
3644

3745
SimpleMailMessage message = new SimpleMailMessage();
3846
message.setFrom(DEFAULT_EMAIL);
3947
message.setTo(email);
4048
message.setSubject("OTP Verification");
41-
message.setText("Your OTP is: " + otp);
49+
message.setText("Your OTP is: " + otpCode); // send the code, not the OTP object
4250

4351
mailSender.send(message);
4452
}
4553

46-
public boolean validateOTP(String otp) {
47-
return otpMap.containsValue(otp);
54+
public boolean validateOTP(String email, String otpCode) {
55+
Optional<OTP> otpOptional = otpRepository.findByEmailAndCode(email, otpCode);
56+
return otpOptional.map(otp -> Instant.now().isBefore(otp.getExpiresAt())).orElse(false);
4857
}
4958

5059
public boolean canSendOTP(String email) {
51-
Instant lastSent = sendTimeMap.get(email);
52-
if (lastSent == null) return true;
53-
return Instant.now().isAfter(lastSent.plusSeconds(cooldownSeconds));
60+
Optional<OTP> lastOtp = otpRepository.findTopByEmail(email);
61+
return lastOtp.map(otp -> Duration.between(otp.getCreatedAt(), Instant.now()).getSeconds() >= cooldownSeconds)
62+
.orElse(true);
5463
}
5564

56-
5765
public long cooldownTime(String email) {
58-
Instant lastSent = sendTimeMap.get(email);
59-
if (lastSent == null) return 0;
60-
long diff = Instant.now().getEpochSecond() - lastSent.getEpochSecond();
61-
return Math.max(0, cooldownSeconds - diff);
66+
Optional<OTP> lastOtp = otpRepository.findTopByEmail(email);
67+
return lastOtp.map(otp -> {
68+
long elapsed = Duration.between(otp.getCreatedAt(), Instant.now()).getSeconds();
69+
return Math.max(0, cooldownSeconds - elapsed);
70+
})
71+
.orElse(0L);
6272
}
6373
}

0 commit comments

Comments
 (0)