fix password resets
This commit is contained in:
parent
4497863667
commit
d462542bbd
4 changed files with 194 additions and 6 deletions
105
index.html
105
index.html
|
|
@ -5930,6 +5930,111 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="page-reset-password" class="page">
|
||||||
|
<div
|
||||||
|
class="reset-password-container"
|
||||||
|
style="text-align: center; padding: var(--space-8); max-width: 400px; margin: 0 auto"
|
||||||
|
>
|
||||||
|
<h2 class="section-title" style="margin-bottom: var(--space-10)">Reset your Password</h2>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="reset-password-error"
|
||||||
|
class="message error-msg"
|
||||||
|
style="
|
||||||
|
display: none;
|
||||||
|
padding: var(--space-3) 0;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
background: transparent;
|
||||||
|
color: #ff6b6b;
|
||||||
|
text-align: left;
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
id="reset-password-success"
|
||||||
|
class="message success-msg"
|
||||||
|
style="
|
||||||
|
display: none;
|
||||||
|
padding: var(--space-3) 0;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
background: transparent;
|
||||||
|
color: var(--foreground);
|
||||||
|
text-align: left;
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<form id="reset-password-form">
|
||||||
|
<div style="margin-bottom: var(--space-4)">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="reset-password-input"
|
||||||
|
placeholder="New Password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.925rem;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: var(--space-6)">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="reset-password-confirm"
|
||||||
|
placeholder="Confirm New Password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.925rem;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
id="reset-password-submit-btn"
|
||||||
|
class="btn-primary"
|
||||||
|
style="width: 100%; justify-content: center"
|
||||||
|
>
|
||||||
|
<span id="reset-password-btn-text">Reset Password</span>
|
||||||
|
<div
|
||||||
|
id="reset-password-btn-spinner"
|
||||||
|
class="animate-spin"
|
||||||
|
style="display: none; width: 18px; height: 18px"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
style="width: 100%; height: 100%"
|
||||||
|
>
|
||||||
|
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="now-playing-bar">
|
<footer class="now-playing-bar">
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,14 @@ export class AuthManager {
|
||||||
const isOAuthRedirect = params.get('oauth') === '1';
|
const isOAuthRedirect = params.get('oauth') === '1';
|
||||||
|
|
||||||
if (userId && secret && userId !== 'null' && secret !== 'null') {
|
if (userId && secret && userId !== 'null' && secret !== 'null') {
|
||||||
try {
|
if (window.location.pathname !== '/reset-password') {
|
||||||
await auth.createSession(userId, secret);
|
try {
|
||||||
window.history.replaceState({}, '', window.location.pathname);
|
await auth.createSession(userId, secret);
|
||||||
} catch (error) {
|
window.history.replaceState({}, '', window.location.pathname);
|
||||||
console.warn('OAuth session handoff failed:', error.message);
|
} catch (error) {
|
||||||
window.history.replaceState({}, '', window.location.pathname);
|
console.warn('OAuth session handoff failed:', error.message);
|
||||||
|
window.history.replaceState({}, '', window.location.pathname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isOAuthRedirect) {
|
} else if (isOAuthRedirect) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
|
@ -137,6 +139,18 @@ export class AuthManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resetPassword(userId, secret, password, confirmPassword) {
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
throw new Error('Passwords do not match');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await auth.updateRecovery(userId, secret, password, password);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Password reset failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async signOut() {
|
async signOut() {
|
||||||
try {
|
try {
|
||||||
await auth.deleteSession('current');
|
await auth.deleteSession('current');
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,9 @@ export function createRouter(ui) {
|
||||||
case 'home':
|
case 'home':
|
||||||
await ui.renderHomePage();
|
await ui.renderHomePage();
|
||||||
break;
|
break;
|
||||||
|
case 'reset-password':
|
||||||
|
await ui.renderResetPasswordPage();
|
||||||
|
break;
|
||||||
case 'donate':
|
case 'donate':
|
||||||
ui.showPage('donate');
|
ui.showPage('donate');
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
66
js/ui.js
66
js/ui.js
|
|
@ -2392,6 +2392,72 @@ export class UIRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async renderResetPasswordPage() {
|
||||||
|
await this.showPage('reset-password');
|
||||||
|
const form = document.getElementById('reset-password-form');
|
||||||
|
const errorEl = document.getElementById('reset-password-error');
|
||||||
|
const successEl = document.getElementById('reset-password-success');
|
||||||
|
const btn = document.getElementById('reset-password-submit-btn');
|
||||||
|
const btnText = document.getElementById('reset-password-btn-text');
|
||||||
|
const spinner = document.getElementById('reset-password-btn-spinner');
|
||||||
|
const passwordInput = document.getElementById('reset-password-input');
|
||||||
|
const confirmInput = document.getElementById('reset-password-confirm');
|
||||||
|
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const userId = params.get('userId');
|
||||||
|
const secret = params.get('secret');
|
||||||
|
|
||||||
|
if (!userId || !secret) {
|
||||||
|
errorEl.textContent = 'Invalid or missing password reset link.';
|
||||||
|
errorEl.style.display = 'block';
|
||||||
|
form.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.onsubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
errorEl.style.display = 'none';
|
||||||
|
successEl.style.display = 'none';
|
||||||
|
|
||||||
|
const password = passwordInput.value;
|
||||||
|
const confirm = confirmInput.value;
|
||||||
|
|
||||||
|
if (password !== confirm) {
|
||||||
|
errorEl.textContent = 'Passwords do not match.';
|
||||||
|
errorEl.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
btn.disabled = true;
|
||||||
|
btnText.style.display = 'none';
|
||||||
|
spinner.style.display = 'block';
|
||||||
|
|
||||||
|
await authManager.resetPassword(userId, secret, password, confirm);
|
||||||
|
|
||||||
|
successEl.textContent = 'Password reset successfully. Opening login...';
|
||||||
|
successEl.style.display = 'block';
|
||||||
|
form.style.display = 'none';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const authModal = document.getElementById('email-auth-modal');
|
||||||
|
if (authModal) {
|
||||||
|
authModal.classList.add('active');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
} catch (error) {
|
||||||
|
errorEl.textContent = error.message || 'Failed to reset password. Please try again.';
|
||||||
|
errorEl.style.display = 'block';
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
btnText.style.display = 'inline';
|
||||||
|
spinner.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async renderPartyDetailPage(id) {
|
async renderPartyDetailPage(id) {
|
||||||
await this.showPage('party-detail');
|
await this.showPage('party-detail');
|
||||||
await partyManager.joinParty(id);
|
await partyManager.joinParty(id);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue