Files
blackbird/static/public/forgot.html
T
2026-07-26 12:06:56 +08:00

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>