feat(#150): 登录页客户端 auth.js(login/me + 路由守卫 requireLoginElseRedirect,PRD 8.2)
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
/* iAOP 登录页客户端(issue #150 / PRD 8.2)。
|
||||
* 对接 core/auth/auth_api.py:
|
||||
* POST /auth/login {username,password} → {token,user}(后端 Set-Cookie iaop_session)
|
||||
* GET /auth/me 凭 cookie 校验当前登录态(路由守卫用)
|
||||
* 写操作守卫:未登录跳登录页;readonly 角色写按钮置灰(见 studio/cockpit 的 applyRbac)。
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
// 认证服务基址:独立运行 auth_api 时指向它;由统一网关接入时留空(同源)。
|
||||
var AUTH_BASE = (function () {
|
||||
try {
|
||||
if (localStorage.getItem("iaop_auth_base")) return localStorage.getItem("iaop_auth_base");
|
||||
} catch (e) {}
|
||||
return ""; // 生产同源,留空
|
||||
})();
|
||||
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
function showAlert(msg, kind) {
|
||||
var el = $("alert");
|
||||
el.textContent = msg || "";
|
||||
el.className = "alert show " + (kind === "ok" ? "alert-ok" : "alert-error");
|
||||
}
|
||||
function clearAlert() { $("alert").className = "alert"; }
|
||||
|
||||
// 检查当前登录态(路由守卫复用)。返回 Promise<user|null>。
|
||||
function currentUser() {
|
||||
return fetch(join(AUTH_BASE, "/auth/me"), { credentials: "include" })
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (d) { return (d && d.user) ? d.user : null; })
|
||||
.catch(function () { return null; });
|
||||
}
|
||||
|
||||
// 写操作守卫:未登录跳登录页(PRD 8.2)。
|
||||
function requireLoginElseRedirect(loginUrl) {
|
||||
return currentUser().then(function (u) {
|
||||
if (!u) {
|
||||
var next = encodeURIComponent(location.pathname + location.search);
|
||||
location.href = (loginUrl || "login.html") + "?next=" + next;
|
||||
return false;
|
||||
}
|
||||
return u;
|
||||
});
|
||||
}
|
||||
|
||||
function join(base, path) { return (base || "") + path; }
|
||||
|
||||
function login(username, password) {
|
||||
$("loginBtn").disabled = true;
|
||||
clearAlert();
|
||||
return fetch(join(AUTH_BASE, "/auth/login"), {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: username, password: password })
|
||||
}).then(function (resp) {
|
||||
return resp.json().then(function (d) {
|
||||
if (!resp.ok) throw new Error((d && d.error) || ("登录失败 HTTP " + resp.status));
|
||||
return d;
|
||||
});
|
||||
}).then(function (d) {
|
||||
// 后端已 Set-Cookie(HttpOnly),同时返回 token 供 Bearer 场景(如 SPA fetch 显式带)
|
||||
showAlert("登录成功,欢迎 " + (d.user && d.user.username) + "(" + (d.user && d.user.role) + ")", "ok");
|
||||
var next = new URLSearchParams(location.search).get("next") || "../cockpit/index.html";
|
||||
setTimeout(function () { location.href = next; }, 500);
|
||||
}).catch(function (e) {
|
||||
$("password").setAttribute("aria-invalid", "true");
|
||||
showAlert(e.message || "登录失败", "error");
|
||||
}).finally(function () {
|
||||
$("loginBtn").disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var form = $("loginForm");
|
||||
if (!form) return;
|
||||
|
||||
// 已登录直接跳转
|
||||
currentUser().then(function (u) {
|
||||
if (u) {
|
||||
var next = new URLSearchParams(location.search).get("next") || "../cockpit/index.html";
|
||||
location.href = next;
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
var u = $("username").value.trim();
|
||||
var p = $("password").value;
|
||||
$("username").removeAttribute("aria-invalid");
|
||||
$("password").removeAttribute("aria-invalid");
|
||||
if (!u || !p || p.length < 8) {
|
||||
$("password").setAttribute("aria-invalid", String(!p || p.length < 8));
|
||||
showAlert("用户名不能为空且密码不少于 8 位", "error");
|
||||
return;
|
||||
}
|
||||
login(u, p);
|
||||
});
|
||||
|
||||
var tog = $("togglePwd");
|
||||
if (tog) tog.addEventListener("click", function () {
|
||||
var pwd = $("password");
|
||||
pwd.type = pwd.type === "password" ? "text" : "password";
|
||||
});
|
||||
});
|
||||
|
||||
// 导出给其它页面复用(路由守卫)
|
||||
window.IAOP_AUTH = { currentUser: currentUser, requireLoginElseRedirect: requireLoginElseRedirect, AUTH_BASE: AUTH_BASE };
|
||||
Reference in New Issue
Block a user