86 lines
5.4 KiB
HTML
86 lines
5.4 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
|
<title>Forgot Password</title>
|
|
<link rel="stylesheet" href="css/theme.css">
|
|
<style>
|
|
.auth-box{max-width:400px;margin:60px auto;padding:30px;background:#fff;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.06)}
|
|
.auth-box h2{text-align:center;margin-bottom:24px;color:#8b6914}
|
|
.auth-box label{display:block;margin:12px 0 4px;font-weight:600;color:#555}
|
|
.auth-box input[type="text"],.auth-box input[type="password"],.auth-box input[type="email"]{width:100%;padding:10px 12px;border:1.5px solid #ddd;border-radius:8px;font-size:15px;box-sizing:border-box}
|
|
.auth-box input:focus{border-color:#c9a96e;outline:none}
|
|
.code-row{display:flex;gap:8px;align-items:center}
|
|
.code-row input{flex:1}
|
|
.btn{width:100%;padding:12px;margin-top:16px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;color:#fff;background:linear-gradient(135deg,#c9a96e,#8b6914)}
|
|
.btn:hover{opacity:.9}.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
.tip{font-size:13px;margin-top:6px}
|
|
.tip-ok{color:#2d8a4e}.tip-err{color:#c0392b}
|
|
.back{display:block;text-align:center;margin-top:16px;color:#999;font-size:14px;text-decoration:none}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="auth-box">
|
|
<h2>Forgot Password</h2>
|
|
<div>
|
|
<label>Email</label>
|
|
<input type="email" id="email" placeholder="Enter your email">
|
|
<div class="code-row">
|
|
<input type="text" id="code" placeholder="6-digit code" maxlength="6" inputmode="numeric">
|
|
<button class="btn" id="sendBtn" style="width:auto;margin:0;padding:10px 16px">Send Code</button>
|
|
</div>
|
|
<p id="tip1" class="tip"></p>
|
|
<label>New Password</label>
|
|
<input type="password" id="newPwd" placeholder="Min 6 characters">
|
|
<label>Confirm Password</label>
|
|
<input type="password" id="confirmPwd" placeholder="Enter again">
|
|
<p id="tip2" class="tip"></p>
|
|
<button class="btn" id="resetBtn">Reset Password</button>
|
|
<a class="back" href="login.html">Back to Login</a>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
var PA="/api/proxy_send_code";
|
|
var PH={"Content-Type":"application/json"};
|
|
document.getElementById("sendBtn").onclick=function(){
|
|
var email=document.getElementById("email").value.trim();
|
|
if(!email||!email.includes("@")){document.getElementById("tip1").textContent="Invalid email";document.getElementById("tip1").className="tip tip-err";return}
|
|
var btn=document.getElementById("sendBtn");btn.disabled=true;btn.textContent="Sending...";
|
|
fetch(PA+"/send_code",{method:"POST",headers:PH,body:JSON.stringify({email:email,subject:"QSONT.me - Password Reset",body:"Your code: {code}\nValid for 10 min."})})
|
|
.then(function(r){return r.json()}).then(function(d){
|
|
var t1=document.getElementById("tip1");
|
|
if(d.success){t1.textContent="Code sent!";t1.className="tip tip-ok"}
|
|
else{t1.textContent=d.error||"Failed";t1.className="tip tip-err"}
|
|
btn.disabled=false;btn.textContent="Send Code"
|
|
}).catch(function(){document.getElementById("tip1").textContent="Network error";document.getElementById("tip1").className="tip tip-err";btn.disabled=false;btn.textContent="Send Code"})
|
|
};
|
|
document.getElementById("resetBtn").onclick=function(){
|
|
var email=document.getElementById("email").value.trim();
|
|
var code=document.getElementById("code").value.trim();
|
|
var pwd=document.getElementById("newPwd").value;
|
|
var cpwd=document.getElementById("confirmPwd").value;
|
|
var t2=document.getElementById("tip2");
|
|
if(!email||!code){t2.textContent="Enter email and code";t2.className="tip tip-err";return}
|
|
if(pwd.length<6){t2.textContent="Password min 6 chars";t2.className="tip tip-err";return}
|
|
if(pwd!==cpwd){t2.textContent="Passwords don't match";t2.className="tip tip-err";return}
|
|
var btn=document.getElementById("resetBtn");btn.disabled=true;btn.textContent="Verifying...";
|
|
fetch("/api/proxy_check_code",{method:"POST",headers:PH,body:JSON.stringify({email:email,code:code})})
|
|
.then(function(r){return r.json()}).then(function(d){
|
|
if(!d.success){t2.textContent=d.error||"Invalid code";t2.className="tip tip-err";btn.disabled=false;btn.textContent="Reset Password";return}
|
|
return fetch("/api/reset_token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:email})})
|
|
}).then(function(r){if(r)return r.json()}).then(function(d){
|
|
if(!d||!d.found){t2.textContent="Email not registered";t2.className="tip tip-err";btn.disabled=false;btn.textContent="Reset Password";return}
|
|
return fetch("/api/hash",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:pwd})})
|
|
}).then(function(r){if(r)return r.json()}).then(function(d){
|
|
if(!d||!d.hash){t2.textContent="Hash failed";t2.className="tip tip-err";btn.disabled=false;btn.textContent="Reset Password";return}
|
|
return fetch("/api/reset_token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:email})}).then(function(r){return r.json()}).then(function(tk){return fetch("/api/reset_password",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:email,password_hash:d.hash,verify_token:tk.token})})})
|
|
}).then(function(r){if(r)return r.json()}).then(function(d){
|
|
if(d&&d.success){t2.textContent="Reset success! Redirecting...";t2.className="tip tip-ok";setTimeout(function(){location.href="login.html"},3000)}
|
|
else{t2.textContent="Reset failed";t2.className="tip tip-err"}
|
|
btn.disabled=false;btn.textContent="Reset Password"
|
|
}).catch(function(){t2.textContent="Network error";t2.className="tip tip-err";btn.disabled=false;btn.textContent="Reset Password"})
|
|
};
|
|
</script>
|
|
</body>
|
|
</html> |