Version 1.3.0

- Implemented authentication and billing routes for Jpn region.
- Refactored and changed the project structure from CommonJS to ES Modules
This commit is contained in:
Junior 2025-04-29 16:20:09 -03:00
parent 9584e58143
commit c3d9e7afb5
76 changed files with 3847 additions and 1109 deletions

View file

@ -1,2 +1,2 @@
[LAUNCHER]
version=1.2.0
version=1.4.0

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News Panel</title>
<link rel="stylesheet" href="/launcher/news/style.css">
<link rel="stylesheet" href="/launcher/news/css/style.css">
</head>
<body oncontextmenu="return false;">
<div class="slider-container">
@ -28,20 +28,20 @@
<div class="tab-link" data-tab="info">Info</div>
</div>
<div class="tab-content tab events active">
<a href="https://your-website.com/news/Halloween.html">Halloween</a> - <span class="tab-date">20/10/2023</span><br>
<a href="https://your-website.com/news/Winter.html">Winter</a> - <span class="tab-date">10/12/2023</span><br>
<a href="https://your-website.com/news/Happy_New_Year.html">Happy New Year</a> - <span class="tab-date">01/01/2024</span><br>
<a href="https://your-website.com/news/Halloween.html">Halloween</a> - <span class="tab-date">20/10/2025</span><br>
<a href="https://your-website.com/news/Winter.html">Winter</a> - <span class="tab-date">10/12/2025</span><br>
<a href="https://your-website.com/news/Happy_New_Year.html">Happy New Year</a> - <span class="tab-date">01/01/2025</span><br>
</div>
<div class="tab-content tab notices">
<a href="#">Notice 1</a> - <span class="tab-date">01/01/2023</span><br>
<a href="#">Notice 2</a> - <span class="tab-date">02/01/2023</span><br>
<a href="#">Notice 3</a> - <span class="tab-date">03/01/2023</span><br>
<a href="#">Notice 1</a> - <span class="tab-date">01/01/2025</span><br>
<a href="#">Notice 2</a> - <span class="tab-date">02/01/2025</span><br>
<a href="#">Notice 3</a> - <span class="tab-date">03/01/2025</span><br>
</div>
<div class="tab-content tab info">
<a href="#">Info 1</a> - <span class="tab-date">01/01/2023</span><br>
<a href="#">Info 2</a> - <span class="tab-date">02/01/2023</span><br>
<a href="#">Info 3</a> - <span class="tab-date">03/01/2023</span><br>
<a href="#">Info 1</a> - <span class="tab-date">01/01/2025</span><br>
<a href="#">Info 2</a> - <span class="tab-date">02/01/2025</span><br>
<a href="#">Info 3</a> - <span class="tab-date">03/01/2025</span><br>
</div>
<script src="/launcher/news/script.js"></script>
<script src="/launcher/news/js/script.js"></script>
</body>
</html>

134
public/site/Signup.html Normal file
View file

@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rusty Hearts - Account Management</title>
<link rel="stylesheet" href="/site/css/style.css">
<link href="https://fonts.googleapis.com/css2?family=MedievalSharp&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<div class="container">
<div class="left-panel">
<div class="logo-container">
<img src="/site/images/rh_logo.png" alt="Rusty Hearts Logo" class="logo">
</div>
<div class="game-description">
<h3>Join the Battle</h3>
<p>Experience a action hack'n'slash multiplayer online game with fast-paced and highly-stylized brawling combat combined with a solo or team-based dungeon exploration experience.</p>
</div>
</div>
<div class="right-panel">
<div class="form-tabs">
<button class="tab-button active" data-tab="register">Create Account</button>
<button class="tab-button" data-tab="reset">Reset Password</button>
</div>
<!-- Register Form -->
<div id="register-form" class="form-content active">
<form id="signupForm">
<div class="form-group">
<label for="userName">Username</label>
<div class="input-with-icon">
<i class="fas fa-user"></i>
<input type="text" id="userName" name="userName" placeholder="6-16 alphanumeric characters" required>
</div>
<div class="error-message" id="userNameError"></div>
</div>
<div class="form-group">
<label for="email">Email</label>
<div class="input-with-icon">
<i class="fas fa-envelope"></i>
<input type="email" id="email" name="email" placeholder="your@email.com" required>
<button type="button" id="sendVerificationBtn" class="verification-btn">Send Code</button>
</div>
<div class="error-message" id="emailError"></div>
</div>
<div class="form-group">
<label for="verificationCode">Verification Code</label>
<div class="input-with-icon">
<i class="fas fa-shield-alt"></i>
<input type="text" id="verificationCode" name="verificationCode" placeholder="Enter verification code" required>
</div>
<div class="error-message" id="verificationCodeError"></div>
</div>
<div class="form-group">
<label for="password">Password</label>
<div class="input-with-icon">
<i class="fas fa-lock"></i>
<input type="password" id="password" name="password" placeholder="8-16 characters" required>
</div>
<div class="error-message" id="passwordError"></div>
</div>
<div id="registerResponse" class="response-message"></div>
</form>
</div>
<!-- Reset Password Form -->
<div id="reset-form" class="form-content">
<form id="resetPasswordForm">
<div class="form-group">
<label for="resetEmail">Email</label>
<div class="input-with-icon">
<i class="fas fa-envelope"></i>
<input type="email" id="resetEmail" name="email" placeholder="your@email.com" required>
<button type="button" id="sendResetVerificationBtn" class="verification-btn">Send Code</button>
</div>
<div class="error-message" id="resetEmailError"></div>
</div>
<div class="form-group">
<label for="resetVerificationCode">Verification Code</label>
<div class="input-with-icon">
<i class="fas fa-shield-alt"></i>
<input type="text" id="resetVerificationCode" name="verificationCode" placeholder="Enter verification code" required>
</div>
<div class="error-message" id="resetVerificationCodeError"></div>
</div>
<div class="form-group">
<label for="newPassword">New Password</label>
<div class="input-with-icon">
<i class="fas fa-lock"></i>
<input type="password" id="newPassword" name="password" placeholder="8-16 characters" required>
</div>
<div class="error-message" id="newPasswordError"></div>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<div class="input-with-icon">
<i class="fas fa-lock"></i>
<input type="password" id="confirmPassword" placeholder="Repeat your password" required>
</div>
<div class="error-message" id="confirmPasswordError"></div>
</div>
<div id="resetResponse" class="response-message"></div>
</form>
</div>
<!-- Dynamic Footer -->
<div class="form-footer">
<div class="footer-content register-footer active">
<button type="submit" form="signupForm" class="submit-btn">Create Account</button>
<div class="footer-links">
<div class="reset-link">
Forgot password? <a href="#" class="switch-tab" data-tab="reset">Reset Password</a>
</div>
</div>
</div>
<div class="footer-content reset-footer">
<button type="submit" form="resetPasswordForm" class="submit-btn">Reset Password</button>
<div class="footer-links">
<div class="register-link">
Need an account? <a href="#" class="switch-tab" data-tab="register">Create Account</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/site/js/script.js"></script>
</body>
</html>

442
public/site/css/style.css Normal file
View file

@ -0,0 +1,442 @@
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Open Sans', sans-serif;
background-color: #1a1a1a;
background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('/site/images/rh1920x1200.jpg');
background-size: cover;
background-repeat: no-repeat;
color: #e0e0e0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
display: flex;
max-width: 1200px;
width: 90%;
max-height: 90vh;
min-height: 600px;
background-color: #2a2a2a;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
/* Left Panel Styles */
.left-panel {
background-color: white;
flex: 1;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
padding: 0px;
display: flex;
flex-direction: column;
position: relative;
align-items: center;
justify-content: space-between;
}
.logo-container {
margin-bottom: 10px;
}
.logo {
max-width: 300px;
}
.game-description {
text-align: center;
}
.game-description h2, h3 {
font-family: 'MedievalSharp', cursive;
color: #c62828;
margin-bottom: 10px;
font-size: 24px;
}
.game-description p {
font-size: 14px;
line-height: 1.5;
}
/* Right Panel Styles */
.right-panel {
flex: 1;
display: flex;
flex-direction: column;
padding: 20px;
overflow: hidden;
}
.form-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 20px 0;
}
.form-container h2 {
font-family: 'MedievalSharp', cursive;
color: #c62828;
margin-bottom: 30px;
text-align: center;
font-size: 28px;
}
.form-group {
margin-bottom: 20px;
flex-shrink: 0;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #e0e0e0;
}
#signupForm {
display: flex;
flex-direction: column;
min-height: 100%;
}
.input-with-icon {
position: relative;
display: flex;
align-items: center;
}
.input-with-icon i {
position: absolute;
left: 15px;
color: #777;
}
.input-with-icon input {
width: 100%;
padding: 12px 120px 12px 40px;
border: 2px solid #444;
border-radius: 5px;
background-color: #333;
color: #e0e0e0;
font-size: 14px;
transition: all 0.3s;
}
.input-with-icon input:focus {
border-color: #c62828;
outline: none;
box-shadow: 0 0 5px rgba(198, 40, 40, 0.5);
}
.error-message {
color: #ff5252;
font-size: 12px;
margin-top: 5px;
height: 16px;
}
.form-wrapper {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.form-header {
flex-shrink: 0;
padding: 10px 0;
}
.form-scrollable {
flex: 1;
overflow-y: auto;
padding: 10px 0;
}
.form-content {
flex: 1;
overflow-y: auto;
padding: 10px 0;
margin-bottom: 10px;
}
/* Better scrollbar styling */
.form-content::-webkit-scrollbar {
width: 6px;
}
.form-content::-webkit-scrollbar-track {
background: #333;
}
.form-content::-webkit-scrollbar-thumb {
background: #c62828;
border-radius: 3px;
}
/* Form Footer - Always Visible */
.form-footer {
flex-shrink: 0;
padding: 4px 0;
margin-top: auto;
border-top: 1px solid #444;
background-color: #2a2a2a;
position: sticky;
bottom: 0;
}
.submit-btn {
width: 100%;
padding: 12px;
background-color: #c62828;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s;
}
.submit-btn:hover {
background-color: #b71c1c;
}
.login-link,
.reset-link,
.register-link {
text-align: center;
font-size: 14px;
}
.login-link a {
color: #c62828;
text-decoration: none;
font-weight: 600;
}
.login-link a:hover {
text-decoration: underline;
}
.response-message {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
text-align: center;
display: none;
}
.response-message.success {
background-color: rgba(76, 175, 80, 0.2);
color: #4CAF50;
display: block;
}
.response-message.error {
background-color: rgba(244, 67, 54, 0.2);
color: #f44336;
display: block;
}
.legal-links {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
font-size: 12px;
}
.legal-links a {
color: #777;
text-decoration: none;
}
.legal-links a:hover {
color: #c62828;
}
/* Footer Styles */
.footer-content {
display: none;
width: 100%;
}
.footer-content.active {
display: block;
}
.footer-links {
margin-top: 15px;
display: flex;
flex-direction: column;
gap: 8px;
}
.switch-tab {
color: #c62828;
text-decoration: none;
font-weight: 600;
cursor: pointer;
}
.switch-tab:hover {
text-decoration: underline;
}
/* Responsive Styles */
@media (max-width: 480px) {
.footer-links {
flex-direction: column;
}
}
@media (max-height: 700px) {
.container {
max-height: 95vh;
min-height: 400px;
}
.form-group {
margin-bottom: 12px;
}
.input-with-icon input {
padding: 10px 100px 10px 35px;
font-size: 13px;
}
.form-group label {
font-size: 14px;
}
}
@media (max-width: 768px) {
.container {
flex-direction: column;
max-height: none;
height: auto;
}
.left-panel {
padding: 20px;
}
.game-screenshot {
display: none;
}
}
/* Verification Button Styles */
.verification-btn {
position: absolute;
right: 10px;
background-color: #333;
color: #c62828;
border: 1px solid #c62828;
border-radius: 3px;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
transition: all 0.3s;
}
.verification-btn:hover {
background-color: #c62828;
color: white;
}
.verification-btn:disabled {
background-color: #333;
color: #777;
border-color: #444;
cursor: not-allowed;
}
/* Tab Styles */
.form-tabs {
display: flex;
border-bottom: 1px solid #444;
margin-bottom: 20px;
}
.tab-button {
flex: 1;
padding: 12px 0;
background: none;
border: none;
color: #777;
font-family: 'MedievalSharp', cursive;
font-size: 18px;
cursor: pointer;
transition: all 0.3s;
position: relative;
}
.tab-button.active {
color: #c62828;
}
.tab-button.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 2px;
background-color: #c62828;
}
/* Form Content */
.form-content {
display: none;
flex: 1;
overflow-y: auto;
padding: 10px 0;
margin-bottom: 10px;
}
.form-content.active {
display: block;
}
/* Submit Buttons */
#resetSubmit {
display: none;
}
/* Animation */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.form-content.active {
animation: fadeIn 0.3s ease-out;
}
.password-match {
border-color: #4CAF50 !important;
box-shadow: 0 0 5px rgba(76, 175, 80, 0.5) !important;
}
.password-mismatch {
border-color: #f44336 !important;
box-shadow: 0 0 5px rgba(244, 67, 54, 0.5) !important;
}

BIN
public/site/images/001.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
public/site/images/002.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
public/site/images/006.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
public/site/images/012.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/site/images/020.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
public/site/images/021.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
public/site/images/022.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
public/site/images/023.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
public/site/images/rh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

461
public/site/js/script.js Normal file
View file

@ -0,0 +1,461 @@
document.addEventListener("DOMContentLoaded", function () {
function setRandomBackground() {
const images = ["001.jpg", "002.jpg", "006.jpg", "012.jpg", "020.jpg", "021.jpg", "022.jpg", "023.jpg"];
const randomImage = images[Math.floor(Math.random() * images.length)];
const panel = document.querySelector(".left-panel");
if (panel) {
panel.style.backgroundImage = `linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url('/site/images/${randomImage}')`;
}
}
setRandomBackground();
// Tab switching functionality
const tabs = document.querySelectorAll(".tab-button");
const formContents = document.querySelectorAll(".form-content");
const footers = document.querySelectorAll(".footer-content");
// Switch tab function
function switchTab(tabName) {
// Update tabs
tabs.forEach((tab) => {
tab.classList.toggle("active", tab.getAttribute("data-tab") === tabName);
});
// Update forms
formContents.forEach((form) => {
form.classList.toggle("active", form.id === `${tabName}-form`);
});
// Update footers
footers.forEach((footer) => {
footer.classList.toggle(
"active",
footer.classList.contains(`${tabName}-footer`)
);
});
}
// Tab click event
tabs.forEach((tab) => {
tab.addEventListener("click", function () {
const tabName = this.getAttribute("data-tab");
switchTab(tabName);
});
});
// Switch tab link click event
document.querySelectorAll(".switch-tab").forEach((link) => {
link.addEventListener("click", function (e) {
e.preventDefault();
const tabName = this.getAttribute("data-tab");
switchTab(tabName);
});
});
// Register form functionality
const signupForm = document.getElementById("signupForm");
const registerResponse = document.getElementById("registerResponse");
const sendVerificationBtn = document.getElementById("sendVerificationBtn");
let cooldownInterval;
// Password reset form functionality
const resetPasswordForm = document.getElementById("resetPasswordForm");
const resetResponse = document.getElementById("resetResponse");
const sendResetVerificationBtn = document.getElementById(
"sendResetVerificationBtn"
);
let resetCooldownInterval;
// Shared functions
function startCooldown(button, intervalVar, seconds = 60) {
let secondsLeft = seconds;
updateButtonText(button, secondsLeft);
intervalVar = setInterval(() => {
secondsLeft--;
updateButtonText(button, secondsLeft);
if (secondsLeft <= 0) {
clearInterval(intervalVar);
resetVerificationButton(button);
}
}, 1000);
return intervalVar;
}
function updateButtonText(button, seconds) {
button.textContent = `Resend (${seconds}s)`;
}
function resetVerificationButton(button) {
button.disabled = false;
button.textContent = "Send Code";
}
function showError(elementId, message) {
const element = document.getElementById(elementId);
if (element) {
element.textContent = message;
}
}
function clearErrorMessages(formPrefix = "") {
const errorElements = document.querySelectorAll(
`.error-message${formPrefix ? `[id^=${formPrefix}]` : ""}`
);
errorElements.forEach((element) => {
element.textContent = "";
});
}
function showResponseMessage(element, message, type) {
element.textContent = message;
element.className = "response-message " + type;
}
// Verification code sending functionality for REGISTER form
sendVerificationBtn.addEventListener("click", async function () {
const email = document.getElementById("email").value.trim();
// Clear previous error
document.getElementById("emailError").textContent = "";
// Basic email validation
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
showError("emailError", "Please enter a valid email address");
return;
}
// Disable button and start cooldown
sendVerificationBtn.disabled = true;
cooldownInterval = startCooldown(sendVerificationBtn, cooldownInterval);
try {
const response = await fetch("/launcher/SendVerificationEmailAction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({ email }),
});
const data = await response.json();
if (!response.ok || !data.success) {
if (data.message === "AccountExists") {
showError("emailError", "Email is already registered");
} else {
showError(
"emailError",
"Failed to send verification code: " + data.message
);
}
resetVerificationButton(sendVerificationBtn);
} else {
showResponseMessage(
registerResponse,
"Verification code sent to your email",
"success"
);
}
} catch (error) {
console.error("Error sending verification:", error);
showError("emailError", "Failed to send verification code");
resetVerificationButton(sendVerificationBtn);
}
});
// Verification code sending functionality for PASSWORD RESET form
sendResetVerificationBtn.addEventListener("click", async function () {
const email = document.getElementById("resetEmail").value.trim();
clearErrorMessages("reset");
document.getElementById("resetEmailError").textContent = "";
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
showError("resetEmailError", "Please enter a valid email address");
return;
}
sendResetVerificationBtn.disabled = true;
resetCooldownInterval = startCooldown(
sendResetVerificationBtn,
resetCooldownInterval
);
try {
const response = await fetch("/launcher/SendPasswordResetEmailAction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({ email }),
});
const data = await response.json();
if (!response.ok || !data.success) {
if (data.message === "AccountNotFound") {
showError("resetEmailError", "No account found with this email");
} else {
showError(
"resetEmailError",
"Failed to send verification code: " + data.message
);
}
resetVerificationButton(sendResetVerificationBtn);
} else {
showResponseMessage(
resetResponse,
"Password reset code sent to your email",
"success"
);
}
} catch (error) {
console.error("Error sending verification:", error);
showError("resetEmailError", "Failed to send verification code");
resetVerificationButton(sendResetVerificationBtn);
}
});
// Form submission handlers
signupForm.addEventListener("submit", async function (e) {
e.preventDefault();
clearErrorMessages();
showResponseMessage(registerResponse, "", "");
const formData = {
userName: document.getElementById("userName").value.trim(),
email: document.getElementById("email").value.trim(),
password: document.getElementById("password").value.trim(),
verificationCode: document
.getElementById("verificationCode")
.value.trim(),
};
// Validation
try {
const response = await fetch("/launcher/SignupAction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams(formData),
});
const data = await response.json();
if (response.ok) {
if (data.success) {
showResponseMessage(
registerResponse,
"Account created successfully!",
"success"
);
} else {
handleServerErrors(data.message, formData, "");
}
} else {
showResponseMessage(
registerResponse,
data.message || "An error occurred. Please try again.",
"error"
);
}
} catch (error) {
console.error("Error:", error);
showResponseMessage(
registerResponse,
"An error occurred. Please try again.",
"error"
);
}
});
resetPasswordForm.addEventListener("submit", async function (e) {
e.preventDefault();
clearErrorMessages("reset");
showResponseMessage(resetResponse, "", "");
const formData = {
email: document.getElementById("resetEmail").value.trim(),
password: document.getElementById("newPassword").value.trim(),
verificationCode: document
.getElementById("resetVerificationCode")
.value.trim(),
};
// Validate the form
if (!validateResetForm(formData)) {
return;
}
try {
const response = await fetch("/launcher/ResetPasswordAction", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams(formData),
});
const data = await response.json();
if (response.ok) {
if (data.success) {
showResponseMessage(
resetResponse,
"Password changed successfully!",
"success"
);
// Clear password fields on success
document.getElementById("newPassword").value = "";
document.getElementById("confirmPassword").value = "";
} else {
handleServerErrors(data.message, formData, "reset");
}
} else {
showResponseMessage(
resetResponse,
data.message || "An error occurred. Please try again.",
"error"
);
}
} catch (error) {
console.error("Error:", error);
showResponseMessage(
resetResponse,
"An error occurred. Please try again.",
"error"
);
}
});
function validateResetForm(formData) {
let isValid = true;
// Email validation
if (!formData.email) {
showError("resetEmailError", "Email is required");
isValid = false;
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
showError("resetEmailError", "Please enter a valid email address");
isValid = false;
}
// Verification code validation
if (!formData.verificationCode) {
showError("resetVerificationCodeError", "Verification code is required");
isValid = false;
} else if (!/^[0-9]+$/.test(formData.verificationCode)) {
showError(
"resetVerificationCodeError",
"Verification code must contain only numbers"
);
isValid = false;
}
// Password validation
const newPassword = document.getElementById("newPassword").value.trim();
const confirmPassword = document
.getElementById("confirmPassword")
.value.trim();
if (!newPassword) {
showError("newPasswordError", "Password is required");
isValid = false;
} else if (newPassword.length < 8 || newPassword.length > 16) {
showError("newPasswordError", "Password must be 8-16 characters");
isValid = false;
}
if (!confirmPassword) {
showError("confirmPasswordError", "Please confirm your password");
isValid = false;
} else if (newPassword !== confirmPassword) {
showError("confirmPasswordError", "Passwords do not match");
showError("newPasswordError", "Passwords do not match");
isValid = false;
}
return isValid;
}
// real-time password matching feedback
const newPasswordInput = document.getElementById("newPassword");
const confirmPasswordInput = document.getElementById("confirmPassword");
function checkPasswordMatch() {
const newPassword = newPasswordInput.value;
const confirmPassword = confirmPasswordInput.value;
if (newPassword && confirmPassword) {
if (newPassword === confirmPassword) {
newPasswordInput.classList.add("password-match");
newPasswordInput.classList.remove("password-mismatch");
confirmPasswordInput.classList.add("password-match");
confirmPasswordInput.classList.remove("password-mismatch");
showError("confirmPasswordError", "");
showError("newPasswordError", "");
} else {
newPasswordInput.classList.add("password-mismatch");
newPasswordInput.classList.remove("password-match");
confirmPasswordInput.classList.add("password-mismatch");
confirmPasswordInput.classList.remove("password-match");
}
} else {
newPasswordInput.classList.remove("password-match", "password-mismatch");
confirmPasswordInput.classList.remove(
"password-match",
"password-mismatch"
);
}
}
newPasswordInput.addEventListener("input", checkPasswordMatch);
confirmPasswordInput.addEventListener("input", checkPasswordMatch);
function handleServerErrors(errorCode, formData, prefix = "") {
switch (errorCode) {
case "UsernameExists":
showError(`${prefix}userNameError`, "Username is already in use");
break;
case "EmailExists":
showError(`${prefix}EmailError`, "Email is already registered");
break;
case "AccountNotFound":
showError(`${prefix}EmailError`, "No account found with this email");
break;
case "InvalidVerificationCode":
showError(
`${prefix}VerificationCodeError`,
"Invalid verification code"
);
break;
case "ExpiredVerificationCode":
showError(
`${prefix}VerificationCodeError`,
"Verification code has expired, please request a new one"
);
break;
case "SamePassword":
showResponseMessage(
resetResponse,
"New password cannot be the same as the old password",
"error"
);
break;
default:
const responseElement = prefix ? resetResponse : registerResponse;
showResponseMessage(
responseElement,
"An error occurred: " + errorCode,
"error"
);
}
}
});