diff --git a/public/app.js b/public/app.js index 1ab2261..8643052 100644 --- a/public/app.js +++ b/public/app.js @@ -1,4 +1,5 @@ var signupEmail = ''; +var recoverEmail = ''; function showState(state) { ['signupInitial', 'signupLoading', 'signupVerify', 'signupResult'].forEach(function(id) { @@ -8,6 +9,14 @@ function showState(state) { document.getElementById(state).classList.add('active'); } +function showRecoverState(state) { + ['recoverInitial', 'recoverLoading', 'recoverVerify', 'recoverResult'].forEach(function(id) { + var el = document.getElementById(id); + if (el) el.classList.remove('active'); + }); + document.getElementById(state).classList.add('active'); +} + function openSignup() { document.getElementById('signupModal').classList.add('active'); showState('signupInitial'); @@ -22,6 +31,23 @@ function closeSignup() { document.getElementById('signupModal').classList.remove('active'); } +function openRecover() { + closeSignup(); + document.getElementById('recoverModal').classList.add('active'); + showRecoverState('recoverInitial'); + var errEl = document.getElementById('recoverError'); + if (errEl) errEl.style.display = 'none'; + var verifyErrEl = document.getElementById('recoverVerifyError'); + if (verifyErrEl) verifyErrEl.style.display = 'none'; + document.getElementById('recoverEmailInput').value = ''; + document.getElementById('recoverCode').value = ''; + recoverEmail = ''; +} + +function closeRecover() { + document.getElementById('recoverModal').classList.remove('active'); +} + async function submitSignup() { var errEl = document.getElementById('signupError'); var btn = document.getElementById('signupBtn'); @@ -106,17 +132,117 @@ async function submitVerify() { } } +async function submitRecover() { + var errEl = document.getElementById('recoverError'); + var btn = document.getElementById('recoverBtn'); + var emailInput = document.getElementById('recoverEmailInput'); + var email = emailInput.value.trim(); + + if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + errEl.textContent = 'Please enter a valid email address.'; + errEl.style.display = 'block'; + return; + } + + errEl.style.display = 'none'; + btn.disabled = true; + showRecoverState('recoverLoading'); + + try { + var res = await fetch('/v1/recover', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: email }) + }); + var data = await res.json(); + + if (!res.ok) { + showRecoverState('recoverInitial'); + errEl.textContent = data.error || 'Something went wrong.'; + errEl.style.display = 'block'; + btn.disabled = false; + return; + } + + recoverEmail = email; + document.getElementById('recoverEmailDisplay').textContent = email; + showRecoverState('recoverVerify'); + document.getElementById('recoverCode').focus(); + btn.disabled = false; + } catch (err) { + showRecoverState('recoverInitial'); + errEl.textContent = 'Network error. Please try again.'; + errEl.style.display = 'block'; + btn.disabled = false; + } +} + +async function submitRecoverVerify() { + var errEl = document.getElementById('recoverVerifyError'); + var btn = document.getElementById('recoverVerifyBtn'); + var codeInput = document.getElementById('recoverCode'); + var code = codeInput.value.trim(); + + if (!code || !/^\d{6}$/.test(code)) { + errEl.textContent = 'Please enter a 6-digit code.'; + errEl.style.display = 'block'; + return; + } + + errEl.style.display = 'none'; + btn.disabled = true; + + try { + var res = await fetch('/v1/recover/verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: recoverEmail, code: code }) + }); + var data = await res.json(); + + if (!res.ok) { + errEl.textContent = data.error || 'Verification failed.'; + errEl.style.display = 'block'; + btn.disabled = false; + return; + } + + if (data.apiKey) { + document.getElementById('recoveredKeyText').textContent = data.apiKey; + showRecoverState('recoverResult'); + } else { + errEl.textContent = data.message || 'No key found for this email.'; + errEl.style.display = 'block'; + btn.disabled = false; + } + } catch (err) { + errEl.textContent = 'Network error. Please try again.'; + errEl.style.display = 'block'; + btn.disabled = false; + } +} + function copyKey() { var key = document.getElementById('apiKeyText').textContent; var btn = document.getElementById('copyBtn'); + doCopy(key, btn); +} + +function copyRecoveredKey() { + var key = document.getElementById('recoveredKeyText').textContent; + var btn = document.getElementById('copyRecoveredBtn'); + doCopy(key, btn); +} + +function doCopy(text, btn) { function showCopied() { btn.textContent = '\u2713 Copied!'; setTimeout(function() { btn.textContent = 'Copy'; }, 2000); } try { - navigator.clipboard.writeText(key).then(showCopied).catch(function() { + navigator.clipboard.writeText(text).then(showCopied).catch(function() { var ta = document.createElement('textarea'); - ta.value = key; ta.style.position = 'fixed'; ta.style.opacity = '0'; + ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); @@ -146,6 +272,21 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById('signupBtn').addEventListener('click', submitSignup); document.getElementById('verifyBtn').addEventListener('click', submitVerify); document.getElementById('copyBtn').addEventListener('click', copyKey); + + // Recovery modal + document.getElementById('btn-close-recover').addEventListener('click', closeRecover); + document.getElementById('recoverBtn').addEventListener('click', submitRecover); + document.getElementById('recoverVerifyBtn').addEventListener('click', submitRecoverVerify); + document.getElementById('copyRecoveredBtn').addEventListener('click', copyRecoveredKey); + document.getElementById('recoverModal').addEventListener('click', function(e) { + if (e.target === this) closeRecover(); + }); + + // Open recovery from links + document.querySelectorAll('.open-recover').forEach(function(el) { + el.addEventListener('click', function(e) { e.preventDefault(); openRecover(); }); + }); + document.getElementById('signupModal').addEventListener('click', function(e) { if (e.target === this) closeSignup(); }); diff --git a/public/index.html b/public/index.html index 6c04a35..6335058 100644 --- a/public/index.html +++ b/public/index.html @@ -188,6 +188,13 @@ html, body { overflow-x: hidden !important; } } + +/* Recovery modal states */ +#recoverInitial, #recoverLoading, #recoverVerify, #recoverResult { display: none; } +#recoverInitial.active { display: block; } +#recoverLoading.active { display: flex; flex-direction: column; align-items: center; padding: 40px 0; text-align: center; } +#recoverResult.active { display: block; } +#recoverVerify.active { display: block; } @@ -349,7 +356,7 @@ html, body {
-100 free PDFs/month β’ All endpoints included
+100 free PDFs/month β’ All endpoints included
Lost your API key? Recover it β