Files
iAOP/web/auth/auth.js

109 lines
3.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 };