Files
Ninjaserver/public/reset-password.html
2025-09-03 12:04:54 +00:00

457 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Passwort zurücksetzen - NinjaCross</title>
<!-- Supabase -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<style>
/* Reset und Basis-Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: radial-gradient(ellipse at top, #1e293b 0%, #0f172a 50%, #020617 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
color: #e2e8f0;
line-height: 1.6;
}
.container {
background: rgba(30, 41, 59, 0.95);
backdrop-filter: blur(20px);
border: 1px solid rgba(51, 65, 85, 0.3);
border-radius: 20px;
padding: 40px;
max-width: 500px;
width: 90%;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
text-align: center;
}
.logo {
font-size: 2.5rem;
font-weight: 900;
background: linear-gradient(135deg, #00d4ff, #0891b2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 2px;
}
.tagline {
color: #94a3b8;
font-size: 0.9rem;
margin-bottom: 30px;
text-transform: uppercase;
letter-spacing: 1px;
}
.title {
font-size: 1.8rem;
font-weight: 700;
color: #ffffff;
margin-bottom: 20px;
}
.subtitle {
color: #cbd5e1;
font-size: 1rem;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
text-align: left;
}
.form-label {
display: block;
color: #e2e8f0;
font-weight: 600;
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: 0.9rem;
}
.form-input {
width: 100%;
padding: 15px 20px;
background: #1e293b;
border: 2px solid #334155;
border-radius: 12px;
color: #ffffff;
font-size: 1rem;
transition: all 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #00d4ff;
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
}
.form-input::placeholder {
color: #64748b;
}
.btn {
width: 100%;
padding: 15px 30px;
border: none;
border-radius: 12px;
font-size: 1rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 20px;
}
.btn-primary {
background: linear-gradient(135deg, #00d4ff, #0891b2);
color: #ffffff;
box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 212, 255, 0.4);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: transparent;
color: #00d4ff;
border: 2px solid #00d4ff;
}
.btn-secondary:hover {
background: #00d4ff;
color: #ffffff;
}
.message {
padding: 15px 20px;
border-radius: 12px;
margin-bottom: 20px;
font-weight: 600;
text-align: center;
}
.message.success {
background: rgba(34, 197, 94, 0.1);
border: 1px solid #22c55e;
color: #22c55e;
}
.message.error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid #ef4444;
color: #ef4444;
}
.message.info {
background: rgba(59, 130, 246, 0.1);
border: 1px solid #3b82f6;
color: #3b82f6;
}
.loading {
display: none;
text-align: center;
color: #94a3b8;
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #334155;
border-radius: 50%;
border-top-color: #00d4ff;
animation: spin 1s ease-in-out infinite;
margin-right: 10px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.back-link {
color: #00d4ff;
text-decoration: none;
font-size: 0.9rem;
margin-top: 20px;
display: inline-block;
}
.back-link:hover {
color: #0891b2;
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
margin: 20px;
padding: 30px 20px;
}
.logo {
font-size: 2rem;
}
.title {
font-size: 1.5rem;
}
.form-input, .btn {
padding: 12px 15px;
font-size: 0.9rem;
}
}
@media (max-width: 480px) {
.container {
margin: 10px;
padding: 20px 15px;
}
.logo {
font-size: 1.8rem;
}
.title {
font-size: 1.3rem;
}
}
</style>
</head>
<body>
<div class="container">
<div class="logo">🥷 NINJACROSS</div>
<div class="tagline">Die ultimative Timer-Rangliste</div>
<h1 class="title">Passwort zurücksetzen 🔐</h1>
<p class="subtitle">Gib dein neues Passwort ein, um dein Konto zu sichern</p>
<div id="messageContainer"></div>
<form id="resetForm">
<div class="form-group">
<label for="newPassword" class="form-label">Neues Passwort</label>
<input
type="password"
id="newPassword"
name="newPassword"
class="form-input"
placeholder="Mindestens 8 Zeichen"
required
minlength="8"
>
</div>
<div class="form-group">
<label for="confirmPassword" class="form-label">Passwort bestätigen</label>
<input
type="password"
id="confirmPassword"
name="confirmPassword"
class="form-input"
placeholder="Passwort wiederholen"
required
minlength="8"
>
</div>
<button type="submit" class="btn btn-primary" id="resetBtn">
🔄 Passwort zurücksetzen
</button>
</form>
<div class="loading" id="loading">
<div class="spinner"></div>
Passwort wird zurückgesetzt...
</div>
<a href="/" class="back-link">← Zurück zur Hauptseite</a>
</div>
<script>
// Supabase Konfiguration
const supabaseUrl = 'https://lfxlplnypzvjrhftaoog.supabase.co';
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxmeGxwbG55cHp2anJoZnRhb29nIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDkyMTQ3NzIsImV4cCI6MjA2NDc5MDc3Mn0.XR4preBqWAQ1rT4PFbpkmRdz57BTwIusBI89fIxDHM8';
const supabase = window.supabase.createClient(supabaseUrl, supabaseKey);
// DOM Elemente
const resetForm = document.getElementById('resetForm');
const newPasswordInput = document.getElementById('newPassword');
const confirmPasswordInput = document.getElementById('confirmPassword');
const resetBtn = document.getElementById('resetBtn');
const loading = document.getElementById('loading');
const messageContainer = document.getElementById('messageContainer');
// URL-Parameter extrahieren
const urlParams = new URLSearchParams(window.location.hash.substring(1));
const accessToken = urlParams.get('access_token');
const refreshToken = urlParams.get('refresh_token');
const tokenType = urlParams.get('token_type');
// Prüfen ob Reset-Token vorhanden ist
if (!accessToken) {
showMessage('error', 'Ungültiger oder fehlender Reset-Link. Bitte fordere einen neuen Reset-Link an.');
resetForm.style.display = 'none';
return;
}
// Session mit Token setzen
async function setSession() {
try {
const { data, error } = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken
});
if (error) {
throw error;
}
console.log('Session erfolgreich gesetzt:', data.user?.email);
} catch (error) {
console.error('Fehler beim Setzen der Session:', error);
showMessage('error', 'Fehler beim Laden des Reset-Links. Bitte versuche es erneut.');
resetForm.style.display = 'none';
}
}
// Passwort zurücksetzen
async function resetPassword(newPassword) {
try {
const { data, error } = await supabase.auth.updateUser({
password: newPassword
});
if (error) {
throw error;
}
return { success: true, data };
} catch (error) {
console.error('Fehler beim Zurücksetzen des Passworts:', error);
return { success: false, error: error.message };
}
}
// Nachricht anzeigen
function showMessage(type, message) {
messageContainer.innerHTML = `
<div class="message ${type}">
${message}
</div>
`;
}
// Formular-Validierung
function validateForm() {
const newPassword = newPasswordInput.value;
const confirmPassword = confirmPasswordInput.value;
if (newPassword.length < 8) {
showMessage('error', 'Das Passwort muss mindestens 8 Zeichen lang sein.');
return false;
}
if (newPassword !== confirmPassword) {
showMessage('error', 'Die Passwörter stimmen nicht überein.');
return false;
}
return true;
}
// Formular-Submit Handler
resetForm.addEventListener('submit', async (e) => {
e.preventDefault();
if (!validateForm()) {
return;
}
// UI-Status ändern
resetBtn.disabled = true;
loading.style.display = 'block';
resetForm.style.display = 'none';
try {
const result = await resetPassword(newPasswordInput.value);
if (result.success) {
showMessage('success', '✅ Passwort erfolgreich zurückgesetzt! Du wirst zur Hauptseite weitergeleitet...');
// Nach 3 Sekunden zur Hauptseite weiterleiten
setTimeout(() => {
window.location.href = '/';
}, 3000);
} else {
showMessage('error', `❌ Fehler beim Zurücksetzen: ${result.error}`);
resetForm.style.display = 'block';
}
} catch (error) {
showMessage('error', '❌ Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut.');
resetForm.style.display = 'block';
} finally {
resetBtn.disabled = false;
loading.style.display = 'none';
}
});
// Session beim Laden der Seite setzen
setSession();
// Passwort-Sicherheitshinweise
newPasswordInput.addEventListener('input', function() {
const password = this.value;
const hasLength = password.length >= 8;
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
if (password.length > 0) {
let hints = [];
if (!hasLength) hints.push('Mindestens 8 Zeichen');
if (!hasUpper) hints.push('Großbuchstaben');
if (!hasLower) hints.push('Kleinbuchstaben');
if (!hasNumber) hints.push('Zahlen');
if (!hasSpecial) hints.push('Sonderzeichen');
if (hints.length > 0) {
showMessage('info', `💡 Tipp: Verwende auch ${hints.join(', ')} für ein sicheres Passwort.`);
} else {
showMessage('success', '✅ Starkes Passwort!');
}
}
});
</script>
</body>
</html>