b70557a077
- 单设备登录:登录成功即 last-login-wins 绑定 users.machine;非超管旧 token 在下一次受保护请求抛 4011 下线,超管豁免;仅认 token 内签名 deviceId,不用请求头。 前端两端接入踢下线跳转(?kicked=1 提示),V118 清空历史 machine - 站内通知:新增 notification 模块(任务失败扫描 / 密钥欠费 / 下游服务探测三类来源), 前后台铃铛组件 + 轮询;后台列表按主管数据范围(UserDataScopeSupport)过滤;V116 建表 均已于 2026-09-13 部署上线,此次补提交源码(此前仅存在于已部署 JAR/构建产物中)
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import axios from 'axios'
|
|
import {
|
|
isForbidden,
|
|
isKicked,
|
|
isLoginLocation,
|
|
isUnauthorized,
|
|
loginRedirectTarget,
|
|
shouldRedirectUnauthorized,
|
|
} from './envelope'
|
|
|
|
export { unwrap } from './envelope'
|
|
|
|
export const http = axios.create({
|
|
baseURL: '/',
|
|
withCredentials: true,
|
|
timeout: 30_000,
|
|
})
|
|
|
|
function redirectToLogin(requestUrl?: string): void {
|
|
if (typeof window === 'undefined') return
|
|
// 已在登录页或失败请求即登录端点时不再跳转,避免登录页循环与吞掉登录失败反馈。
|
|
if (!shouldRedirectUnauthorized(window.location.pathname, requestUrl)) return
|
|
const target = loginRedirectTarget(window.location)
|
|
window.location.assign('/admin-vue/login?redirect=' + target)
|
|
}
|
|
|
|
/** 单设备登录:被新设备顶下线,整页回登录页(整页 reload 顺带重置会话 store,避免守卫弹回)。 */
|
|
function redirectToLoginKicked(): void {
|
|
if (typeof window === 'undefined') return
|
|
if (isLoginLocation(window.location.pathname)) return
|
|
window.location.assign('/admin-vue/login?kicked=1')
|
|
}
|
|
|
|
http.interceptors.response.use(
|
|
(response) => {
|
|
// Java 未登录以 HTTP 200 + body.code=401 返回,需统一按未登录处理;
|
|
// 403(已登录但无后台权限,如用工具前端账号 token 访问后台)与 401 同样跳登录页。
|
|
// 4011(账号已在其他设备登录)单独提示,不按普通 401 处理。
|
|
if (isKicked(response.data)) {
|
|
redirectToLoginKicked()
|
|
} else if (isUnauthorized(response.data) || isForbidden(response.data)) {
|
|
redirectToLogin(response.config?.url)
|
|
}
|
|
return response
|
|
},
|
|
(error) => {
|
|
if (isKicked(error?.response?.data)) {
|
|
redirectToLoginKicked()
|
|
} else if (
|
|
error?.response?.status === 401 ||
|
|
error?.response?.status === 403 ||
|
|
isUnauthorized(error?.response?.data) ||
|
|
isForbidden(error?.response?.data)
|
|
) {
|
|
redirectToLogin(error?.config?.url)
|
|
}
|
|
return Promise.reject(error)
|
|
},
|
|
)
|