完成: - Nginx /api/ 路由修复(3998→3090) - guada Agent引擎部署(BS-SG-001:8787) - Outline文件写入权限修复 - Dex数据库持久化+权限修复 - 邮箱注册系统(register.html + register-api:3997) - 注册服务改用Python写Dex BLOB(修复node-sqlite3格式问题) 待修复: - 注册后退出再登录间歇性OAuth state丢失(考虑独立子域名) 记录: cc-d135c.hdlp
164 lines
6.5 KiB
HTML
164 lines
6.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>光湖 · 注册</title>
|
||
<style>
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
:root{
|
||
--bg:#060a14;--card:rgba(14,20,48,0.8);
|
||
--blue:#60a5fa;--cyan:#22d3ee;--text:#eaf0ff;--dim:#94a7d0;
|
||
--border:rgba(96,165,250,0.15);--ok:#34d399;--err:#f87171;
|
||
}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden}
|
||
.bg{position:fixed;inset:0;background:radial-gradient(ellipse at 30% 20%,rgba(40,80,160,0.3),transparent 60%),radial-gradient(ellipse at 70% 80%,rgba(30,60,130,0.25),transparent 60%),#060a14;z-index:0}
|
||
.card{position:relative;z-index:1;width:100%;max-width:400px;padding:40px 32px;background:var(--card);border:1px solid var(--border);border-radius:16px;backdrop-filter:blur(20px)}
|
||
.logo{text-align:center;font-size:32px;margin-bottom:8px}
|
||
.title{text-align:center;font-size:22px;font-weight:500;color:var(--text);margin-bottom:4px}
|
||
.sub{text-align:center;font-size:13px;color:var(--dim);margin-bottom:28px}
|
||
.step{display:none}
|
||
.step.active{display:block}
|
||
label{display:block;font-size:13px;color:var(--dim);margin-bottom:6px}
|
||
input{width:100%;padding:12px;border-radius:10px;border:1px solid var(--border);background:rgba(12,20,40,0.6);color:var(--text);font-size:15px;outline:none;transition:border .3s;margin-bottom:16px}
|
||
input:focus{border-color:var(--blue)}
|
||
.btn{width:100%;padding:12px;border-radius:10px;border:none;background:linear-gradient(135deg,var(--blue),var(--cyan));color:#fff;font-size:15px;font-weight:500;cursor:pointer;transition:opacity .3s}
|
||
.btn:hover{opacity:.85}
|
||
.btn:disabled{opacity:.5;cursor:not-allowed}
|
||
.msg{text-align:center;font-size:13px;margin-top:12px;min-height:20px}
|
||
.msg.ok{color:var(--ok)}
|
||
.msg.err{color:var(--err)}
|
||
.back{display:block;text-align:center;margin-top:16px;font-size:13px;color:var(--dim);text-decoration:none}
|
||
.back:hover{color:var(--blue)}
|
||
.countdown{color:var(--dim)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="bg"></div>
|
||
<div class="card">
|
||
<div class="logo">🌊</div>
|
||
<div class="title">注册光湖协作中心</div>
|
||
<div class="sub">用邮箱注册,立即开始协作</div>
|
||
|
||
<div id="step1" class="step active">
|
||
<label>邮箱地址</label>
|
||
<input type="email" id="email" placeholder="your@email.com" autocomplete="email">
|
||
<button class="btn" id="sendBtn" onclick="sendCode()">发送验证码</button>
|
||
<div class="msg" id="msg1"></div>
|
||
</div>
|
||
|
||
<div id="step2" class="step">
|
||
<label>验证码(已发送至 <span id="emailDisplay"></span>)</label>
|
||
<input type="text" id="code" placeholder="6位验证码" maxlength="6" autocomplete="one-time-code">
|
||
<label>设置密码</label>
|
||
<input type="password" id="password" placeholder="至少6位">
|
||
<button class="btn" id="verifyBtn" onclick="verifyCode()">完成注册</button>
|
||
<div class="msg" id="msg2"></div>
|
||
<a href="#" class="back" onclick="backToStep1()">← 换一个邮箱</a>
|
||
</div>
|
||
|
||
<div class="msg" id="msg3" style="margin-top:16px"></div>
|
||
<a href="/outline/" class="back" style="margin-top:8px">已有账号?去登录 →</a>
|
||
</div>
|
||
|
||
<script>
|
||
let countdownTimer = null;
|
||
|
||
async function sendCode() {
|
||
const email = document.getElementById('email').value.trim();
|
||
const msg = document.getElementById('msg1');
|
||
const btn = document.getElementById('sendBtn');
|
||
|
||
if (!email || !email.includes('@')) {
|
||
msg.className = 'msg err'; msg.textContent = '请输入有效邮箱';
|
||
return;
|
||
}
|
||
|
||
btn.disabled = true; btn.textContent = '发送中...';
|
||
msg.className = 'msg'; msg.textContent = '';
|
||
|
||
try {
|
||
const res = await fetch('/register-api/send-code', {
|
||
method: 'POST', headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({email})
|
||
});
|
||
const data = await res.json();
|
||
if (data.ok) {
|
||
document.getElementById('emailDisplay').textContent = email;
|
||
document.getElementById('step1').classList.remove('active');
|
||
document.getElementById('step2').classList.add('active');
|
||
startCountdown(60);
|
||
} else {
|
||
msg.className = 'msg err'; msg.textContent = data.error;
|
||
btn.disabled = false; btn.textContent = '发送验证码';
|
||
}
|
||
} catch(e) {
|
||
msg.className = 'msg err'; msg.textContent = '网络错误,请重试';
|
||
btn.disabled = false; btn.textContent = '发送验证码';
|
||
}
|
||
}
|
||
|
||
function startCountdown(sec) {
|
||
if (countdownTimer) clearInterval(countdownTimer);
|
||
const btn = document.getElementById('sendBtn');
|
||
btn.disabled = true;
|
||
countdownTimer = setInterval(() => {
|
||
sec--;
|
||
if (sec <= 0) {
|
||
clearInterval(countdownTimer);
|
||
btn.disabled = false;
|
||
btn.textContent = '重新发送';
|
||
} else {
|
||
btn.textContent = `${sec}秒后重发`;
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
async function verifyCode() {
|
||
const email = document.getElementById('email').value.trim();
|
||
const code = document.getElementById('code').value.trim();
|
||
const password = document.getElementById('password').value;
|
||
const msg = document.getElementById('msg2');
|
||
const btn = document.getElementById('verifyBtn');
|
||
|
||
if (!code || code.length !== 6) {
|
||
msg.className = 'msg err'; msg.textContent = '请输入6位验证码';
|
||
return;
|
||
}
|
||
if (!password || password.length < 6) {
|
||
msg.className = 'msg err'; msg.textContent = '密码至少6位';
|
||
return;
|
||
}
|
||
|
||
btn.disabled = true; btn.textContent = '注册中...';
|
||
|
||
try {
|
||
const res = await fetch('/register-api/verify', {
|
||
method: 'POST', headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({email, code, password})
|
||
});
|
||
const data = await res.json();
|
||
if (data.ok) {
|
||
msg.className = 'msg ok'; msg.textContent = '注册成功!3秒后跳转登录...';
|
||
setTimeout(() => { window.location.href = '/outline/'; }, 3000);
|
||
} else {
|
||
msg.className = 'msg err'; msg.textContent = data.error;
|
||
btn.disabled = false; btn.textContent = '完成注册';
|
||
}
|
||
} catch(e) {
|
||
msg.className = 'msg err'; msg.textContent = '网络错误,请重试';
|
||
btn.disabled = false; btn.textContent = '完成注册';
|
||
}
|
||
}
|
||
|
||
function backToStep1() {
|
||
document.getElementById('step2').classList.remove('active');
|
||
document.getElementById('step1').classList.add('active');
|
||
if (countdownTimer) clearInterval(countdownTimer);
|
||
const btn = document.getElementById('sendBtn');
|
||
btn.disabled = false; btn.textContent = '发送验证码';
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|