refactor(portal): merge register + login into single auth entry
Some checks failed
CI / Build & Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Release (push) Has been cancelled

- Two separate cards (register + login) -> single unified card
- handleRegister + handleLogin -> single handleAuth that tries login first,
  falls back to register if login fails (new user detection)
- Single email/password input, single button, single status display
- Enter key submits on both fields
- File size 57873 -> 51329 chars (-11%)

Test: test_tksea_portal_assets.sh PASS, verify_frontend_smoke.sh PASS,
      verify_quality_gates.sh PASS (gofmt+vet+cov+integration)
This commit is contained in:
phamnazage-jpg
2026-06-04 13:52:18 +08:00
parent 2b5607285f
commit 4b743848bc

View File

@@ -161,35 +161,23 @@
<article class="panel">
<div class="section-head">
<div>
<h2>注册与登录</h2>
<p>刷新页面或稍后回来都可以直接登录恢复会话。首次注册成功后会自动登录,并回填邮箱</p>
<h2>登录</h2>
<p>刷新页面或稍后回来都可以直接恢复会话。系统自动判断新老用户,新邮箱自动创建账号并登录,已有账号则直接登录</p>
</div>
</div>
<div class="auth-grid">
<div class="auth-single">
<section class="card">
<h3>注册</h3>
<p class="hint">当前无需邮箱验证码、邀请码或 Turnstile。</p>
<label for="reg-email">邮箱</label>
<input id="reg-email" placeholder="you@example.com" />
<label for="reg-password">密码</label>
<input id="reg-password" type="password" placeholder="至少 6 位" />
<h3>登录或注册</h3>
<p class="hint">输入邮箱和密码。如果是新邮箱会自动创建账号并登录,已有账号则直接登录。无需验证码、邀请码或 Turnstile。</p>
<label for="auth-email">邮箱</label>
<input id="auth-email" placeholder="you@example.com" />
<label for="auth-password">密码</label>
<input id="auth-password" type="password" placeholder="至少 6 位" />
<div class="button-row">
<button id="register-btn">注册并登录</button>
<button id="auth-btn">登录 / 注册</button>
<span id="auth-hint" class="hint inline">新邮箱将自动创建</span>
</div>
<div id="register-status" class="status-box">未执行</div>
</section>
<section class="card">
<h3>登录</h3>
<p class="hint">如果你已经有账号,可以直接登录并同步当前用户状态。</p>
<label for="login-email">邮箱</label>
<input id="login-email" placeholder="you@example.com" />
<label for="login-password">密码</label>
<input id="login-password" type="password" placeholder="你的密码" />
<div class="button-row">
<button id="login-btn" class="alt">登录</button>
</div>
<div id="login-status" class="status-box">未执行</div>
<div id="auth-status" class="status-box">未执行</div>
</section>
</div>
</article>
@@ -461,7 +449,7 @@
} else {
localStorage.removeItem(STORAGE.token);
}
const email = $("login-email").value.trim() || $("reg-email").value.trim();
const email = $("auth-email").value.trim();
if (email) {
localStorage.setItem(STORAGE.email, email);
}
@@ -470,8 +458,7 @@
function restoreSession() {
state.accessToken = localStorage.getItem(STORAGE.token) || "";
const rememberedEmail = localStorage.getItem(STORAGE.email) || "";
$("login-email").value = rememberedEmail;
$("reg-email").value = rememberedEmail;
$("auth-email").value = rememberedEmail;
$("access-token").value = state.accessToken;
}
@@ -485,8 +472,7 @@
localStorage.removeItem(STORAGE.token);
$("access-token").value = "";
$("api-key").value = "";
setStatus("login-status", "", "未执行");
setStatus("register-status", "", "未执行");
setStatus("auth-status", "", "未执行");
setStatus("key-status", "", "未执行");
renderAll();
}
@@ -1127,7 +1113,7 @@
state.portalLogicalGroups = Array.isArray(payload.logical_groups) ? payload.logical_groups : [];
} catch (err) {
state.portalLogicalGroups = [];
setStatus("login-status", "bad", "逻辑分组目录拉取失败: " + err.message);
setStatus("auth-status", "bad", "逻辑分组目录拉取失败: " + err.message);
}
if (!state.accessToken) {
@@ -1154,7 +1140,7 @@
} catch (err) {
clearSession();
statusPill("bad", "登录失效");
setStatus("login-status", "bad", "会话已失效,请重新登录:" + err.message);
setStatus("auth-status", "bad", "会话已失效,请重新登录:" + err.message);
}
}
@@ -1164,53 +1150,40 @@
saveSession();
}
async function handleRegister() {
const email = $("reg-email").value.trim();
const password = $("reg-password").value;
setBusy("register-btn", true);
try {
const data = await requestJSON("/auth/register", "POST", {
email,
password,
verify_code: "",
turnstile_token: "",
promo_code: "",
invitation_code: "",
aff_code: ""
}, false);
rememberAuth(data);
$("login-email").value = email;
$("login-password").value = password;
setStatus("register-status", "ok", "注册成功,已自动登录。正在同步你的账号状态与申请资格。");
setStatus("login-status", "ok", "已自动登录。");
await refreshUserState();
} catch (err) {
setStatus("register-status", "bad", "注册失败: " + err.message);
} finally {
saveSession();
setBusy("register-btn", false);
async function handleAuth() {
const email = $("auth-email").value.trim();
const password = $("auth-password").value;
if (!email || !password) {
setStatus("auth-status", "bad", "邮箱和密码都不能为空。");
return;
}
}
async function handleLogin() {
const email = $("login-email").value.trim();
const password = $("login-password").value;
setBusy("login-btn", true);
setBusy("auth-btn", true);
setStatus("auth-status", "", "正在验证账号…");
try {
// 先尝试登录
const data = await requestJSON("/auth/login", "POST", {
email,
password,
turnstile_token: ""
email, password, turnstile_token: ""
}, false);
rememberAuth(data);
$("reg-email").value = email;
setStatus("login-status", "ok", "登录成功,正在同步你的账号状态与申请资格。");
setStatus("auth-status", "ok", "登录成功,正在同步你的账号状态与申请资格。");
await refreshUserState();
} catch (err) {
setStatus("login-status", "bad", "登录失败: " + err.message);
} catch (loginErr) {
// 登录失败,尝试注册(可能是新用户)
try {
const regData = await requestJSON("/auth/register", "POST", {
email, password,
verify_code: "", turnstile_token: "",
promo_code: "", invitation_code: "", aff_code: ""
}, false);
rememberAuth(regData);
setStatus("auth-status", "ok", "新账号注册成功,已自动登录。正在同步…");
await refreshUserState();
} catch (regErr) {
setStatus("auth-status", "bad", "登录或注册均失败:" + regErr.message);
}
} finally {
saveSession();
setBusy("login-btn", false);
setBusy("auth-btn", false);
}
}
@@ -1253,15 +1226,18 @@
await copyText(value, statusID, label, button);
}
$("register-btn").addEventListener("click", handleRegister);
$("login-btn").addEventListener("click", handleLogin);
$("create-key-btn").addEventListener("click", handleCreateKey);
$("refresh-session-btn").addEventListener("click", refreshUserState);
$("logout-btn").addEventListener("click", () => {
clearSession();
statusPill("warn", "已退出");
});
$("copy-token-btn").addEventListener("click", (event) => copyField("access-token", "login-status", "Access Token", event.currentTarget));
$("auth-btn").addEventListener("click", handleAuth);
$("auth-email").addEventListener("keydown", (e) => { if (e.key === "Enter") $("auth-btn").click(); });
$("auth-password").addEventListener("keydown", (e) => { if (e.key === "Enter") $("auth-btn").click(); });
$("copy-token-btn").addEventListener("click", (event) => copyField("access-token", "auth-status", "Access Token", event.currentTarget));
$("copy-key-btn").addEventListener("click", (event) => copyField("api-key", "key-status", "API Key", event.currentTarget));
$("keys-list").addEventListener("click", async (event) => {
const button = event.target.closest(".copy-existing-key-btn");