网友修x改、我修复之后
This commit is contained in:
1 parent
060b7fc7ad
commit
896cfc32ca
7 files changed
+702
-463
No files matched your search
@@ -0,0 +1,86 @@
|
||||
<!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>
|
||||
+38
-56
@@ -1,58 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<title>树状菜单 · 开源社区</title>
|
||||
<link rel="stylesheet" href="css/theme.css">
|
||||
<style>
|
||||
.hero { text-align: center; margin-bottom: 1.5rem; }
|
||||
.hero h1 { font-size: 1.6rem; margin-bottom: 0.4rem; }
|
||||
.hero p { font-size: 0.9rem; color: var(--fg-secondary); line-height: 1.6; }
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
justify-content: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.btn-row a {
|
||||
display: inline-block;
|
||||
padding: 0.6rem 1.8rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-sm);
|
||||
text-decoration: none;
|
||||
transition: opacity 0.15s, transform 0.1s;
|
||||
}
|
||||
.btn-row a:active { transform: scale(0.96); }
|
||||
.btn-primary { background: var(--accent); color: #fff; }
|
||||
.btn-primary:hover { opacity: 0.85; }
|
||||
.btn-ghost { background: transparent; color: var(--accent-dark); border: 1.5px solid var(--border); }
|
||||
.btn-ghost:hover { border-color: var(--accent); opacity: 0.85; }
|
||||
.tagline {
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border-light);
|
||||
font-size: 0.8rem;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="hero">
|
||||
<h1>树状菜单</h1>
|
||||
<p>一个轻量的开源社区,用树的结构组织知识与文件。<br>自由生长,自由分享。</p>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<a class="btn-primary" href="login.html">登录</a>
|
||||
<a class="btn-ghost" href="register.html">注册</a>
|
||||
</div>
|
||||
<div class="tagline">开源 · 轻量 · 自托管</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">蜀ICP备2024107064号-2</a>
|
||||
</footer>
|
||||
</body>
|
||||
</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>树状笔记 · 开源社区</title>
|
||||
<link rel="stylesheet" href="css/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="landing" style="display:none">
|
||||
<div class="card" style="max-width:400px;margin:60px auto;padding:40px;text-align:center">
|
||||
<h1 style="color:var(--accent)">树状笔记</h1>
|
||||
<p style="color:var(--fg-secondary);line-height:1.6">一个开源的、基于树状结构的知识组织文件管理工具</p>
|
||||
<div style="margin-top:1.5rem;display:flex;gap:0.75rem;justify-content:center">
|
||||
<a class="btn-primary" href="login.html" style="padding:0.6rem 1.8rem;font-size:0.9rem;font-weight:600;border-radius:var(--radius-sm);text-decoration:none;color:#fff;background:var(--accent)">登录</a>
|
||||
<a class="btn-ghost" href="register.html" style="padding:0.6rem 1.8rem;font-size:0.9rem;font-weight:600;border-radius:var(--radius-sm);text-decoration:none;color:var(--accent-dark);border:1.5px solid var(--border)">注册</a>
|
||||
</div>
|
||||
<p style="margin-top:1.5rem;font-size:0.8rem;color:var(--muted)">开源 · 免费 · 共享</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app" style="display:none">
|
||||
<iframe src="dnfs.html" style="width:100%;height:100vh;border:none"></iframe>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
var t = sessionStorage.getItem('q4_token') || localStorage.getItem('q4_token');
|
||||
if(t){
|
||||
document.getElementById('landing').style.display='none';
|
||||
document.getElementById('app').style.display='block';
|
||||
} else {
|
||||
document.getElementById('landing').style.display='block';
|
||||
document.getElementById('app').style.display='none';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<footer style="text-align:center;padding:1rem;font-size:0.75rem;color:var(--muted)">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:var(--muted)">蜀ICP备2024107064号-2</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +1,5 @@
|
||||
// Session restore from localStorage
|
||||
if(!sessionStorage.getItem('q4_token')){var t=localStorage.getItem('q4_token');if(t)sessionStorage.setItem('q4_token',t);}
|
||||
|
||||
/* by qing.
|
||||
*
|
||||
|
||||
+14
-86
@@ -1,162 +1,90 @@
|
||||
const msgTip = document.getElementById('msgTip');
|
||||
const pwdTip = document.getElementById('pwdTip');
|
||||
const emlTip = document.getElementById('emlTip');
|
||||
const agreeTip = document.getElementById('agreeTip');
|
||||
const form = document.getElementById('form');
|
||||
|
||||
const usernameInput = document.getElementById('loginUsername');
|
||||
const passwordInput = document.getElementById('loginPassword');
|
||||
const emailInput = document.getElementById('loginEmail');
|
||||
const agreeBox = document.getElementById('agreeBox');
|
||||
const protocol = document.getElementById('agreement');
|
||||
const resetUser = document.getElementById('resetUser');
|
||||
const registerBtn = document.getElementById('registerBtn');
|
||||
const loginButton = document.getElementById('loginBtn');
|
||||
|
||||
resetUser.addEventListener('click', function() {
|
||||
clearTipStyle();
|
||||
});
|
||||
resetUser.addEventListener('click', function() { clearTipStyle(); });
|
||||
|
||||
function clearTipStyle() {
|
||||
msgTip.innerHTML = '';
|
||||
pwdTip.innerHTML = '';
|
||||
agreeTip.innerHTML = '';
|
||||
emlTip.innerHTML = '';
|
||||
|
||||
msgTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
pwdTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
agreeTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
emlTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
protocol.classList.remove('tip-red');
|
||||
protocol.classList.remove('tip-green');
|
||||
agreeTip.classList.remove('tip-red');
|
||||
}
|
||||
clearTipStyle();
|
||||
|
||||
/**
|
||||
* @returns {string|false}
|
||||
*/
|
||||
function checkUsername() {
|
||||
const result = FormValidators.username(usernameInput.value);
|
||||
msgTip.innerHTML = '';
|
||||
msgTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
msgTip.textContent = result.message;
|
||||
msgTip.classList.add(result.className);
|
||||
msgTip.className = result.className;
|
||||
return result.valid ? result.value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string|false}
|
||||
*/
|
||||
function checkPwd() {
|
||||
const result = FormValidators.password(passwordInput.value);
|
||||
pwdTip.innerHTML = '';
|
||||
pwdTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
pwdTip.textContent = result.message;
|
||||
pwdTip.classList.add(result.className);
|
||||
pwdTip.className = result.className;
|
||||
return result.valid ? result.value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string|false}
|
||||
*/
|
||||
function checkEml() {
|
||||
const result = FormValidators.email(emailInput.value);
|
||||
emlTip.innerHTML = '';
|
||||
emlTip.classList.remove('tip-red', 'tip-yellow', 'tip-green');
|
||||
emlTip.textContent = result.message;
|
||||
emlTip.classList.add(result.className);
|
||||
return result.valid ? result.value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkAgree() {
|
||||
agreeTip.innerHTML = '';
|
||||
agreeTip.classList.remove('tip-red');
|
||||
protocol.classList.remove('tip-green', 'tip-red');
|
||||
if (!agreeBox.checked) {
|
||||
agreeTip.innerHTML = '请同意用户协议!';
|
||||
agreeTip.classList.add('tip-red');
|
||||
protocol.classList.add('tip-red');
|
||||
return false;
|
||||
}
|
||||
protocol.classList.add('tip-green');
|
||||
return true;
|
||||
}
|
||||
|
||||
usernameInput.addEventListener('focus', checkUsername);
|
||||
passwordInput.addEventListener('focus', checkPwd);
|
||||
|
||||
usernameInput.addEventListener('input', checkUsername);
|
||||
passwordInput.addEventListener('input', checkPwd);
|
||||
|
||||
usernameInput.addEventListener('blur', checkUsername);
|
||||
passwordInput.addEventListener('blur', checkPwd);
|
||||
|
||||
emailInput.addEventListener('focus', checkEml);
|
||||
emailInput.addEventListener('input', checkEml);
|
||||
emailInput.addEventListener('blur', checkEml);
|
||||
|
||||
agreeBox.addEventListener('change', checkAgree);
|
||||
|
||||
registerBtn.addEventListener('click', function() {
|
||||
location.href = 'register.html';
|
||||
});
|
||||
registerBtn.addEventListener('click', function() { location.href = 'register.html'; });
|
||||
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
clearTipStyle();
|
||||
|
||||
const userOk = checkUsername();
|
||||
const pwdOk = checkPwd();
|
||||
const emlOk = checkEml();
|
||||
const agreeOk = checkAgree();
|
||||
if (!userOk || !pwdOk || !agreeOk) return;
|
||||
|
||||
if (!(userOk && pwdOk && emlOk && agreeOk)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsonBody = JSON.stringify({
|
||||
username: userOk,
|
||||
password: pwdOk
|
||||
});
|
||||
|
||||
const jsonBody = JSON.stringify({ username: userOk, password: pwdOk });
|
||||
try {
|
||||
loginButton.disabled = true;
|
||||
const res = await fetch('/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json' // 告诉服务器内容为json
|
||||
},
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonBody
|
||||
});
|
||||
|
||||
const responseText = await res.text();
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = JSON.parse(responseText);
|
||||
} catch {
|
||||
console.error(`登录接口返回非 JSON 内容(HTTP ${res.status}):`, responseText);
|
||||
throw new Error('服务器响应格式异常,请稍后重试');
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(result.message || `登录失败(HTTP ${res.status})`);
|
||||
}
|
||||
|
||||
try { result = JSON.parse(responseText); } catch { throw new Error('服务器响应格式异常'); }
|
||||
if (!res.ok) throw new Error(result.message || '登录失败');
|
||||
if (result.success) {
|
||||
alert(`登陆成功`);
|
||||
localStorage.setItem('q4_token', result.data.token);
|
||||
sessionStorage.setItem('q4_token', result.data.token);
|
||||
location.href = 'dnfs.html';
|
||||
alert('登录成功');
|
||||
location.href = '/dnfs.html';
|
||||
} else {
|
||||
alert(result.message || '登录失败');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
alert(err.message || '接口请求失败,请检查后端服务是否启动');
|
||||
console.error('登录请求异常:', err);
|
||||
alert(err.message || '接口请求失败');
|
||||
} finally {
|
||||
loginButton.disabled = false;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ function checkCode() {
|
||||
codeTip.classList.add('tip-yellow');
|
||||
return false;
|
||||
}
|
||||
if (code !== sendCode) {
|
||||
if (sendCode === 'email_sent') { /* Server-verified, skip client check */ } else if (code !== sendCode) {
|
||||
codeTip.textContent = '验证码错误,请核对!';
|
||||
codeTip.classList.add('tip-red');
|
||||
return false;
|
||||
|
||||
+12
-17
@@ -2,7 +2,6 @@
|
||||
<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>登录</title>
|
||||
<link rel="stylesheet" href="css/theme.css">
|
||||
@@ -10,22 +9,14 @@
|
||||
<body>
|
||||
<div class="card">
|
||||
<h2>用户登录</h2>
|
||||
<form action="" id="form" method="post">
|
||||
<form id="form" novalidate>
|
||||
<div>
|
||||
<label>用户名/推荐邮箱:</label>
|
||||
<input type="text" id="loginUsername" name="username" placeholder="请输入用户名">
|
||||
<span id="msgTip" ></span>
|
||||
<label>密码:</label>
|
||||
<input type="password" id="loginPassword" name="password" placeholder="请输入密码">
|
||||
<span id="pwdTip"></span>
|
||||
</div>
|
||||
<div>
|
||||
<label>验证邮箱:</label>
|
||||
<input type="email" id="loginEmail" name="email" placeholder="请输入验证邮箱">
|
||||
<span id="emlSub">
|
||||
<input type="submit" value="发送验证码">
|
||||
</span>
|
||||
<span id="emlTip"></span>
|
||||
<label>用户名/推荐邮箱:</label>
|
||||
<input type="text" id="loginUsername" placeholder="请输入用户名">
|
||||
<span id="msgTip"></span>
|
||||
<label>密码:</label>
|
||||
<input type="password" id="loginPassword" placeholder="请输入密码">
|
||||
<span id="pwdTip"></span>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" id="registerBtn">注册</button>
|
||||
@@ -38,6 +29,10 @@
|
||||
<span id="agreeTip"></span>
|
||||
</div>
|
||||
</form>
|
||||
<a href="forgot.html" style="display:block;text-align:center;color:#999;font-size:13px;margin:8px 0">忘记密码?</a>
|
||||
<div style="text-align:center;margin:12px 0;position:relative"><div style="position:absolute;top:50%;left:0;right:0;height:1px;background:#e0d6cc"></div><span style="background:#fff;padding:0 12px;position:relative;color:#999;font-size:13px">或</span></div>
|
||||
<a href="https://yhdet.top/yhdet_auth?callback=https://qsont.me/yhdet_callback" class="yhdet-login-btn">开源社区 快捷登录</a>
|
||||
<style>.yhdet-login-btn{display:block;width:100%;padding:12px;border:1.5px solid #1a73e8;border-radius:8px;background:white;color:#1a73e8;font-size:15px;font-weight:600;text-decoration:none;text-align:center;box-sizing:border-box;margin-bottom:12px}.yhdet-login-btn:hover{background:#e8f0fe}</style>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">蜀ICP备2024107064号-2</a>
|
||||
@@ -45,4 +40,4 @@
|
||||
<script src="js/validation.js"></script>
|
||||
<script src="js/login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in new issue
Block a user