more checks and stuff for verification

This commit is contained in:
durn
2026-04-30 00:04:52 -06:00
parent e7be40bb8d
commit 37c3fb0979
7 changed files with 155 additions and 87 deletions
@@ -25,6 +25,11 @@ public class EmailController {
if (emailService.isValidEmail(email) == false) {
return ResponseEntity.status(429).body("Invalid Email Detected.");
}
if (emailService.isEmailTaken(email)) {
return ResponseEntity.status(409).body("An account with this email already exists.");
}
return ResponseEntity.ok().build();
}
@@ -58,6 +58,15 @@ public class UserController {
return new ResponseEntity<>(users, HttpStatus.OK);
}
@PostMapping("/check")
public ResponseEntity<?> isTaken(@RequestParam String name) {
if (userService.isUsernameTaken(name)) {
return ResponseEntity.status(409).body("An account with this username already exists.");
}
return ResponseEntity.ok().build();
}
// build get current user REST API
@GetMapping("/me")
public UserDto getLoggedInUser(Principal principal) {
@@ -11,6 +11,8 @@ public interface UserRepo extends JpaRepository<User, Integer> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String Email);
List<User> findByUsernameContainingIgnoreCase(String name);
}
@@ -7,6 +7,9 @@ import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import com.example.demo.repository.UserRepo;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
@@ -23,6 +26,9 @@ public class EmailService {
@Autowired
private OtpStore otpStore;
@Autowired
private UserRepo userRepository;
public void sendOtpEmail(String toEmail) {
String otp = OtpUtil.generateOtp(6);
otpStore.save(toEmail, otp);
@@ -46,12 +52,17 @@ public class EmailService {
}
}
public boolean isEmailTaken(String email) {
return userRepository.findByEmail(email.toLowerCase().trim()).isPresent();
}
public boolean isValidEmail(String email) {
if (email == null || email.isBlank()) return false;
String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
if (!email.matches(emailRegex)) return false;
try {
InternetAddress emailAddr = new InternetAddress(email, true);
emailAddr.validate();
@@ -255,4 +255,9 @@ public class UserServiceImpl implements UserService {
return favoriteRepository.existsById(new FavoriteId(user.getId(), recipeId));
}
@Override
public Boolean isUsernameTaken(String username) {
return userRepository.findByUsername(username.toLowerCase().trim()).isPresent();
}
}
@@ -11,6 +11,8 @@ import com.example.demo.entity.User;
public interface UserService {
UserDto convertToDto(User user);
Boolean isUsernameTaken(String username);
User saveUser(User user);
List<UserDto> getAllUsers();
+127 -93
View File
@@ -87,108 +87,142 @@ function isProfane(str) {
error.textContent = message;
}
document.addEventListener("DOMContentLoaded", function () {
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById("createUserForm");
const passwordField = document.getElementById("password");
const confirmPasswordField = document.getElementById("confirmPassword");
const passwordError = document.getElementById("passwordError");
const form = document.getElementById("createUserForm");
const passwordField = document.getElementById("password");
const confirmPasswordField = document.getElementById("confirmPassword");
const usernameField = document.getElementById("username");
const emailField = document.getElementById("email");
const passwordError = document.getElementById("passwordError");
function checkPasswords() {
if (confirmPasswordField.value === "") {
confirmPasswordField.classList.remove("invalid");
passwordError.textContent = "Password cannot be blank";
return;
}
function checkPasswords() {
if (confirmPasswordField.value === "") {
confirmPasswordField.classList.remove("invalid");
passwordError.textContent = "";
return;
}
if (passwordField.value !== confirmPasswordField.value) {
confirmPasswordField.classList.add("invalid");
passwordError.textContent = "Passwords do not match.";
} else {
confirmPasswordField.classList.remove("invalid");
passwordError.textContent = "";
}
}
passwordField.addEventListener("input", checkPasswords);
confirmPasswordField.addEventListener("input", checkPasswords);
const csrfToken = () => document.querySelector('meta[name="_csrf"]').getAttribute("content");
const csrfHeader = () => document.querySelector('meta[name="_csrf_header"]').getAttribute("content");
emailField.addEventListener("blur", async () => {
if (!emailField.value) return;
const res = await fetch(`/api/email/check?email=${encodeURIComponent(emailField.value)}`,
{ method: "POST", headers: { [csrfHeader()]: csrfToken() } });
if (!res.ok) showError(emailField, await res.text() || "Email already in use.");
else clearError(emailField);
});
usernameField.addEventListener("blur", async () => {
if (!usernameField.value) {
clearError(usernameField);
return;
}
if (isProfane(usernameField.value)) {
showError(usernameField, "Username contains inappropriate language.");
return;
}
clearError(usernameField);
const res = await fetch(`/api/users/check?name=${encodeURIComponent(usernameField.value)}`,
{ method: "POST", headers: { [csrfHeader()]: csrfToken() } });
if (!res.ok) showError(usernameField, await res.text() || "Username already taken.");
else clearError(usernameField);
})
form.addEventListener("submit", async function(e) {
e.preventDefault();
[usernameField, emailField, passwordField, confirmPasswordField].forEach(clearError);
passwordError.textContent = "";
const password = passwordField.value;
const confirmPassword = confirmPasswordField.value;
const name = usernameField.value;
const email = emailField.value;
if (password !== confirmPassword) {
confirmPasswordField.classList.add("invalid");
passwordError.textContent = "Passwords do not match.";
return;
}
if (isProfane(name)) {
showError(usernameField, "Username contains inappropriate language.");
return;
}
try {
const headers = { [csrfHeader()]: csrfToken() };
const emailRes = await fetch(`/api/email/check?email=${encodeURIComponent(email)}`,
{ method: "POST", headers });
if (!emailRes.ok) {
passwordError.style.color = "red";
passwordError.textContent = await emailRes.text() || "Invalid email detected.";
return;
}
const userRes = await fetch(`/api/users/check?name=${encodeURIComponent(name)}`,
{ method: "POST", headers });
if (!userRes.ok) {
passwordError.style.color = "red";
passwordError.textContent = await userRes.text() || "Username already taken.";
return;
}
sessionStorage.setItem("pendingEmail", email);
sessionStorage.setItem("pendingUser", JSON.stringify({
username: name, email, hashedpassword: password, role: "ROLE_USER"
}));
if (passwordField.value !== confirmPasswordField.value) {
confirmPasswordField.classList.add("invalid");
passwordError.textContent = "Passwords do not match.";
} else {
confirmPasswordField.classList.remove("invalid");
passwordError.textContent = "";
}
}
const sendRes = await fetch(`/api/email/send?email=${encodeURIComponent(email)}`,
{ method: "POST", headers });
if (!sendRes.ok) {
passwordError.style.color = "red";
passwordError.textContent = "Failed to send verification email. Please check your address.";
return;
}
passwordField.addEventListener("input", checkPasswords);
confirmPasswordField.addEventListener("input", checkPasswords);
passwordError.style.color = "green";
passwordError.textContent = "Check your email for a verification code...";
setTimeout(() => { window.location.href = "/verify"; }, 1500);
form.addEventListener("submit", async function(e) {
e.preventDefault();
const password = passwordField.value;
const confirmPassword = confirmPasswordField.value;
const name = document.getElementById("username").value;
const email = document.getElementById("email").value;
if (password !== confirmPassword) {
confirmPasswordField.classList.add("invalid");
passwordError.textContent = "Passwords do not match.";
return;
}
if (isProfane(document.getElementById("username").value)) {
showError(username, 'Username contains inappropriate language');
return;
}
} catch (error) {
passwordError.style.color = "red";
passwordError.textContent = "Could not connect to the server.";
console.error("Request error:", error);
}
});
});
const userData = {
username: document.getElementById("username").value,
email: document.getElementById("email").value,
hashedpassword: password,
role: "ROLE_USER"
};
function showError(input, message) {
input.classList.add("invalid");
passwordError.style.color = "red";
passwordError.textContent = message;
}
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');
try {
const checkResponse = await fetch(`/api/email/check?email=${encodeURIComponent(email)}`, {
method: "POST",
headers: { [csrfHeader]: csrfToken }
});
if (!checkResponse.ok) {
const errorText = await checkResponse.text();
passwordError.style.color = "red";
passwordError.textContent = errorText || "Invalid Email Detected.";
return;
}
sessionStorage.setItem("pendingEmail", email);
sessionStorage.setItem("pendingUser", JSON.stringify({
username: name,
email: email,
hashedpassword: password,
role: "ROLE_USER"
}));
const sendResponse = await fetch(`/api/email/send?email=${encodeURIComponent(email)}`, {
method: "POST",
headers: { [csrfHeader]: csrfToken }
});
if (!sendResponse.ok) {
passwordError.style.color = "red";
passwordError.textContent = "Failed to send verification email. Please check your address.";
return;
}
passwordError.style.color = "green";
passwordError.textContent = "Check your email for a verification code...";
setTimeout(function () {
window.location.href = "/verify";
}, 1500);
} catch (error) {
passwordError.style.color = "red";
passwordError.textContent = "Could not connect to the server.";
console.error("Request error:", error);
}
});
});
function clearError(input) {
input.classList.remove("invalid");
passwordError.textContent = "";
}
</script>