mirror of
https://github.com/hyzendust/hyzendust.github.io.git
synced 2026-07-01 07:22:17 +02:00
Fix: otp verification
This commit is contained in:
@@ -199,75 +199,6 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── OTP step indicator ─────────────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.otp-steps {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__line {
|
|
||||||
flex: 1;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--background-color1, #ccc);
|
|
||||||
transition: background 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__num {
|
|
||||||
width: 1.8rem;
|
|
||||||
height: 1.8rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid var(--background-color1, #ccc);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 0.78rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--foreground-color3, #888);
|
|
||||||
background: var(--background-color);
|
|
||||||
transition:
|
|
||||||
border-color 0.25s ease,
|
|
||||||
color 0.25s ease,
|
|
||||||
background 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__label {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--foreground-color3, #888);
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: color 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Active step */
|
|
||||||
.otp-steps__item--active .otp-steps__num {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item--active .otp-steps__label {
|
|
||||||
color: var(--accent-color);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Completed step */
|
|
||||||
.otp-steps__item--done .otp-steps__num {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
background: var(--accent-color);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item--done .otp-steps__label {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── OTP panel ───────────────────────────────────────────────────────────── */
|
/* ── OTP panel ───────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.otp-panel__hint {
|
.otp-panel__hint {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,27 +5,8 @@
|
|||||||
<div class="auth-card">
|
<div class="auth-card">
|
||||||
<div id="auth-message" class="auth-message" aria-live="polite" style="display: none"></div>
|
<div id="auth-message" class="auth-message" aria-live="polite" style="display: none"></div>
|
||||||
|
|
||||||
<!-- ── Step indicator ─────────────────────────────────────────── -->
|
|
||||||
<div class="otp-steps" id="otp-steps" aria-label="Sign-up steps">
|
|
||||||
<div class="otp-steps__item otp-steps__item--active" id="step-ind-1">
|
|
||||||
<span class="otp-steps__num">1</span>
|
|
||||||
<span class="otp-steps__label">Details</span>
|
|
||||||
</div>
|
|
||||||
<div class="otp-steps__line"></div>
|
|
||||||
<div class="otp-steps__item" id="step-ind-2">
|
|
||||||
<span class="otp-steps__num">2</span>
|
|
||||||
<span class="otp-steps__label">Verify email</span>
|
|
||||||
</div>
|
|
||||||
<div class="otp-steps__line"></div>
|
|
||||||
<div class="otp-steps__item" id="step-ind-3">
|
|
||||||
<span class="otp-steps__num">3</span>
|
|
||||||
<span class="otp-steps__label">Done</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── STEP 1 — Account details ───────────────────────────────── -->
|
<!-- ── STEP 1 — Account details ───────────────────────────────── -->
|
||||||
<form id="signup-form" class="auth-form" novalidate>
|
<form id="signup-form" class="auth-form" novalidate>
|
||||||
<div class="auth-form__input-wrap">
|
|
||||||
<div class="auth-form__group">
|
<div class="auth-form__group">
|
||||||
<label class="auth-form__label" for="signup-username">Username</label>
|
<label class="auth-form__label" for="signup-username">Username</label>
|
||||||
<input
|
<input
|
||||||
@@ -58,6 +39,7 @@
|
|||||||
|
|
||||||
<div class="auth-form__group">
|
<div class="auth-form__group">
|
||||||
<label class="auth-form__label" for="signup-password">Password</label>
|
<label class="auth-form__label" for="signup-password">Password</label>
|
||||||
|
<div class="auth-form__input-wrap">
|
||||||
<input
|
<input
|
||||||
class="auth-form__input"
|
class="auth-form__input"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -99,6 +81,7 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
<span class="auth-form__hint">(at least 8 characters)</span>
|
<span class="auth-form__hint">(at least 8 characters)</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -113,7 +96,6 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="auth-form__submit" id="signup-submit">
|
<button type="submit" class="auth-form__submit" id="signup-submit">
|
||||||
<span class="auth-form__submit-text">Send verification code</span>
|
<span class="auth-form__submit-text">Send verification code</span>
|
||||||
@@ -194,15 +176,8 @@
|
|||||||
(function () {
|
(function () {
|
||||||
var BACKEND = 'https://backend.freedoms4.org/auth.php';
|
var BACKEND = 'https://backend.freedoms4.org/auth.php';
|
||||||
|
|
||||||
// ── State ──────────────────────────────────────────────────────
|
var state = { username: '', email: '', password: '' };
|
||||||
var state = {
|
|
||||||
username: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
otpToken: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── DOM refs ───────────────────────────────────────────────────
|
|
||||||
var msgBox = document.getElementById('auth-message');
|
var msgBox = document.getElementById('auth-message');
|
||||||
var signupForm = document.getElementById('signup-form');
|
var signupForm = document.getElementById('signup-form');
|
||||||
var signupSubmit = document.getElementById('signup-submit');
|
var signupSubmit = document.getElementById('signup-submit');
|
||||||
@@ -215,13 +190,6 @@
|
|||||||
var pwdInput = document.getElementById('signup-password');
|
var pwdInput = document.getElementById('signup-password');
|
||||||
var eyeBtn = signupForm.querySelector('.auth-form__eye');
|
var eyeBtn = signupForm.querySelector('.auth-form__eye');
|
||||||
|
|
||||||
var stepInds = [
|
|
||||||
document.getElementById('step-ind-1'),
|
|
||||||
document.getElementById('step-ind-2'),
|
|
||||||
document.getElementById('step-ind-3'),
|
|
||||||
];
|
|
||||||
|
|
||||||
// ── Helpers ────────────────────────────────────────────────────
|
|
||||||
function showMsg(text, type) {
|
function showMsg(text, type) {
|
||||||
msgBox.textContent = text;
|
msgBox.textContent = text;
|
||||||
msgBox.className = 'auth-message auth-message--' + type;
|
msgBox.className = 'auth-message auth-message--' + type;
|
||||||
@@ -231,22 +199,15 @@
|
|||||||
msgBox.style.display = 'none';
|
msgBox.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLoading(btn, loading) {
|
function setLoading(btn, on) {
|
||||||
btn.disabled = loading;
|
btn.disabled = on;
|
||||||
btn.querySelector('.auth-form__submit-text').style.display = loading ? 'none' : '';
|
btn.querySelector('.auth-form__submit-text').style.display = on ? 'none' : '';
|
||||||
btn.querySelector('.auth-form__submit-loader').style.display = loading
|
btn.querySelector('.auth-form__submit-loader').style.display = on
|
||||||
? 'inline-flex'
|
? 'inline-flex'
|
||||||
: 'none';
|
: 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStep(n) {
|
// Password eye toggle
|
||||||
stepInds.forEach(function (el, i) {
|
|
||||||
el.classList.toggle('otp-steps__item--active', i + 1 === n);
|
|
||||||
el.classList.toggle('otp-steps__item--done', i + 1 < n);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Password eye toggle ────────────────────────────────────────
|
|
||||||
eyeBtn.addEventListener('click', function () {
|
eyeBtn.addEventListener('click', function () {
|
||||||
var isText = pwdInput.type === 'text';
|
var isText = pwdInput.type === 'text';
|
||||||
pwdInput.type = isText ? 'password' : 'text';
|
pwdInput.type = isText ? 'password' : 'text';
|
||||||
@@ -254,26 +215,27 @@
|
|||||||
eyeBtn.querySelector('.eye-hide').style.display = isText ? 'none' : '';
|
eyeBtn.querySelector('.eye-hide').style.display = isText ? 'none' : '';
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Resend cooldown ────────────────────────────────────────────
|
// Resend cooldown
|
||||||
var resendTimeout = null;
|
var resendInterval = null;
|
||||||
function startResendCooldown(seconds) {
|
function startResendCooldown(seconds) {
|
||||||
otpResendBtn.style.display = 'none';
|
otpResendBtn.style.display = 'none';
|
||||||
otpResendTimer.style.display = '';
|
otpResendTimer.style.display = '';
|
||||||
var remaining = seconds;
|
var remaining = seconds;
|
||||||
otpResendTimer.textContent = 'Resend in ' + remaining + 's';
|
otpResendTimer.textContent = 'Resend in ' + remaining + 's';
|
||||||
resendTimeout = setInterval(function () {
|
resendInterval = setInterval(function () {
|
||||||
remaining--;
|
remaining--;
|
||||||
if (remaining <= 0) {
|
if (remaining <= 0) {
|
||||||
clearInterval(resendTimeout);
|
clearInterval(resendInterval);
|
||||||
otpResendTimer.style.display = 'none';
|
otpResendTimer.style.display = 'none';
|
||||||
otpResendBtn.style.display = '';
|
otpResendBtn.style.display = '';
|
||||||
|
otpResendBtn.disabled = false;
|
||||||
} else {
|
} else {
|
||||||
otpResendTimer.textContent = 'Resend in ' + remaining + 's';
|
otpResendTimer.textContent = 'Resend in ' + remaining + 's';
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── STEP 1 submit — validate + send_otp ───────────────────────
|
// STEP 1 — validate fields and send OTP
|
||||||
signupForm.addEventListener('submit', function (e) {
|
signupForm.addEventListener('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
hideMsg();
|
hideMsg();
|
||||||
@@ -320,13 +282,11 @@
|
|||||||
})
|
})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Switch to OTP panel
|
|
||||||
signupForm.style.display = 'none';
|
signupForm.style.display = 'none';
|
||||||
otpEmailDisp.textContent = email;
|
otpEmailDisp.textContent = email;
|
||||||
otpPanel.style.display = '';
|
otpPanel.style.display = '';
|
||||||
otpInput.value = '';
|
otpInput.value = '';
|
||||||
otpInput.focus();
|
otpInput.focus();
|
||||||
setStep(2);
|
|
||||||
startResendCooldown(60);
|
startResendCooldown(60);
|
||||||
showMsg('Code sent! Check your inbox (and spam folder).', 'success');
|
showMsg('Code sent! Check your inbox (and spam folder).', 'success');
|
||||||
} else {
|
} else {
|
||||||
@@ -341,7 +301,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Resend button ──────────────────────────────────────────────
|
// Resend
|
||||||
otpResendBtn.addEventListener('click', function () {
|
otpResendBtn.addEventListener('click', function () {
|
||||||
hideMsg();
|
hideMsg();
|
||||||
otpResendBtn.disabled = true;
|
otpResendBtn.disabled = true;
|
||||||
@@ -370,8 +330,8 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── STEP 2 submit — verify_otp then signup ────────────────────
|
// STEP 2 — verify OTP and create account in one request
|
||||||
otpSubmit.addEventListener('click', function () {
|
function doSignup() {
|
||||||
hideMsg();
|
hideMsg();
|
||||||
var otp = otpInput.value.trim();
|
var otp = otpInput.value.trim();
|
||||||
|
|
||||||
@@ -382,26 +342,7 @@
|
|||||||
|
|
||||||
setLoading(otpSubmit, true);
|
setLoading(otpSubmit, true);
|
||||||
|
|
||||||
// 2a. Verify OTP → get token
|
|
||||||
fetch(BACKEND, {
|
fetch(BACKEND, {
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify({ action: 'verify_otp', email: state.email, otp: otp }),
|
|
||||||
})
|
|
||||||
.then(function (r) {
|
|
||||||
return r.json();
|
|
||||||
})
|
|
||||||
.then(function (data) {
|
|
||||||
if (!data.success) {
|
|
||||||
showMsg(data.message || 'Invalid or expired code.', 'error');
|
|
||||||
setLoading(otpSubmit, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.otpToken = data.otp_token;
|
|
||||||
|
|
||||||
// 2b. Create account
|
|
||||||
return fetch(BACKEND, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -410,35 +351,32 @@
|
|||||||
username: state.username,
|
username: state.username,
|
||||||
email: state.email,
|
email: state.email,
|
||||||
password: state.password,
|
password: state.password,
|
||||||
otp_token: state.otpToken,
|
otp: otp,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(function (r) {
|
.then(function (r) {
|
||||||
return r.json();
|
return r.json();
|
||||||
})
|
})
|
||||||
.then(function (d) {
|
.then(function (data) {
|
||||||
if (d.success) {
|
if (data.success) {
|
||||||
setStep(3);
|
|
||||||
otpPanel.style.display = 'none';
|
|
||||||
showMsg('Account created! Redirecting to login\u2026', 'success');
|
showMsg('Account created! Redirecting to login\u2026', 'success');
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
window.location.href = '/login/';
|
window.location.href = '/login/';
|
||||||
}, 1800);
|
}, 1800);
|
||||||
} else {
|
} else {
|
||||||
showMsg(d.message || 'Sign-up failed. Please try again.', 'error');
|
showMsg(data.message || 'Sign-up failed. Please try again.', 'error');
|
||||||
setLoading(otpSubmit, false);
|
setLoading(otpSubmit, false);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
showMsg('Network error. Please try again.', 'error');
|
showMsg('Network error. Please try again.', 'error');
|
||||||
setLoading(otpSubmit, false);
|
setLoading(otpSubmit, false);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Allow pressing Enter in OTP box to submit
|
otpSubmit.addEventListener('click', doSignup);
|
||||||
otpInput.addEventListener('keydown', function (e) {
|
otpInput.addEventListener('keydown', function (e) {
|
||||||
if (e.key === 'Enter') otpSubmit.click();
|
if (e.key === 'Enter') doSignup();
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -199,75 +199,6 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── OTP step indicator ─────────────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.otp-steps {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__line {
|
|
||||||
flex: 1;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--background-color1, #ccc);
|
|
||||||
transition: background 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__num {
|
|
||||||
width: 1.8rem;
|
|
||||||
height: 1.8rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid var(--background-color1, #ccc);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 0.78rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--foreground-color3, #888);
|
|
||||||
background: var(--background-color);
|
|
||||||
transition:
|
|
||||||
border-color 0.25s ease,
|
|
||||||
color 0.25s ease,
|
|
||||||
background 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__label {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--foreground-color3, #888);
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: color 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Active step */
|
|
||||||
.otp-steps__item--active .otp-steps__num {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item--active .otp-steps__label {
|
|
||||||
color: var(--accent-color);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Completed step */
|
|
||||||
.otp-steps__item--done .otp-steps__num {
|
|
||||||
border-color: var(--accent-color);
|
|
||||||
background: var(--accent-color);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.otp-steps__item--done .otp-steps__label {
|
|
||||||
color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── OTP panel ───────────────────────────────────────────────────────────── */
|
/* ── OTP panel ───────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.otp-panel__hint {
|
.otp-panel__hint {
|
||||||
|
|||||||
Reference in New Issue
Block a user