feat: email change UI, Swagger UI improvements, key recovery link on landing page
- Email change modal: API key + new email → verification code → confirmed - Swagger UI with proper OpenAPI spec (public/openapi.json + swagger-ui assets) - Key recovery link prominently on landing page hero section - Footer link for email change - Updated docs.html to use swagger-ui bundle
This commit is contained in:
parent
efa39661cf
commit
d859e9fa60
9 changed files with 741 additions and 383 deletions
140
public/app.js
140
public/app.js
|
|
@ -298,3 +298,143 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- Email Change ---
|
||||
var emailChangeApiKey = '';
|
||||
var emailChangeNewEmail = '';
|
||||
|
||||
function showEmailChangeState(state) {
|
||||
['emailChangeInitial', 'emailChangeLoading', 'emailChangeVerify', 'emailChangeResult'].forEach(function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.classList.remove('active');
|
||||
});
|
||||
document.getElementById(state).classList.add('active');
|
||||
}
|
||||
|
||||
function openEmailChange() {
|
||||
closeSignup();
|
||||
closeRecover();
|
||||
document.getElementById('emailChangeModal').classList.add('active');
|
||||
showEmailChangeState('emailChangeInitial');
|
||||
var errEl = document.getElementById('emailChangeError');
|
||||
if (errEl) errEl.style.display = 'none';
|
||||
var verifyErrEl = document.getElementById('emailChangeVerifyError');
|
||||
if (verifyErrEl) verifyErrEl.style.display = 'none';
|
||||
document.getElementById('emailChangeApiKey').value = '';
|
||||
document.getElementById('emailChangeNewEmail').value = '';
|
||||
document.getElementById('emailChangeCode').value = '';
|
||||
emailChangeApiKey = '';
|
||||
emailChangeNewEmail = '';
|
||||
}
|
||||
|
||||
function closeEmailChange() {
|
||||
document.getElementById('emailChangeModal').classList.remove('active');
|
||||
}
|
||||
|
||||
async function submitEmailChange() {
|
||||
var errEl = document.getElementById('emailChangeError');
|
||||
var btn = document.getElementById('emailChangeBtn');
|
||||
var apiKey = document.getElementById('emailChangeApiKey').value.trim();
|
||||
var newEmail = document.getElementById('emailChangeNewEmail').value.trim();
|
||||
|
||||
if (!apiKey) {
|
||||
errEl.textContent = 'Please enter your API key.';
|
||||
errEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (!newEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) {
|
||||
errEl.textContent = 'Please enter a valid email address.';
|
||||
errEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
errEl.style.display = 'none';
|
||||
btn.disabled = true;
|
||||
showEmailChangeState('emailChangeLoading');
|
||||
|
||||
try {
|
||||
var res = await fetch('/v1/email-change', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ apiKey: apiKey, newEmail: newEmail })
|
||||
});
|
||||
var data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
showEmailChangeState('emailChangeInitial');
|
||||
errEl.textContent = data.error || 'Something went wrong.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
emailChangeApiKey = apiKey;
|
||||
emailChangeNewEmail = newEmail;
|
||||
document.getElementById('emailChangeEmailDisplay').textContent = newEmail;
|
||||
showEmailChangeState('emailChangeVerify');
|
||||
document.getElementById('emailChangeCode').focus();
|
||||
btn.disabled = false;
|
||||
} catch (err) {
|
||||
showEmailChangeState('emailChangeInitial');
|
||||
errEl.textContent = 'Network error. Please try again.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function submitEmailChangeVerify() {
|
||||
var errEl = document.getElementById('emailChangeVerifyError');
|
||||
var btn = document.getElementById('emailChangeVerifyBtn');
|
||||
var code = document.getElementById('emailChangeCode').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/email-change/verify', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ apiKey: emailChangeApiKey, newEmail: emailChangeNewEmail, code: code })
|
||||
});
|
||||
var data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
errEl.textContent = data.error || 'Verification failed.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('emailChangeNewDisplay').textContent = data.newEmail || emailChangeNewEmail;
|
||||
showEmailChangeState('emailChangeResult');
|
||||
} catch (err) {
|
||||
errEl.textContent = 'Network error. Please try again.';
|
||||
errEl.style.display = 'block';
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners for email change (append to DOMContentLoaded)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var closeBtn = document.getElementById('btn-close-email-change');
|
||||
if (closeBtn) closeBtn.addEventListener('click', closeEmailChange);
|
||||
|
||||
var changeBtn = document.getElementById('emailChangeBtn');
|
||||
if (changeBtn) changeBtn.addEventListener('click', submitEmailChange);
|
||||
|
||||
var verifyBtn = document.getElementById('emailChangeVerifyBtn');
|
||||
if (verifyBtn) verifyBtn.addEventListener('click', submitEmailChangeVerify);
|
||||
|
||||
var modal = document.getElementById('emailChangeModal');
|
||||
if (modal) modal.addEventListener('click', function(e) { if (e.target === this) closeEmailChange(); });
|
||||
|
||||
document.querySelectorAll('.open-email-change').forEach(function(el) {
|
||||
el.addEventListener('click', function(e) { e.preventDefault(); openEmailChange(); });
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue