feat: 增加开发模式 URL 免登录

- 新增 dev-only 且仅限本机访问的 admin 免登入口

- 管理端支持通过 ?devLogin=admin 自动换取登录态并清理 URL 参数

- 删除未受保护的临时 token 接口并补充关键单测
This commit is contained in:
2026-03-07 18:16:42 +08:00
parent 37e185e74a
commit a93f7ca216
14 changed files with 459 additions and 96 deletions

View File

@@ -10,7 +10,13 @@ import { resetAllStores, useAccessStore, useUserStore } from '@easyflow/stores';
import { ElNotification } from 'element-plus';
import { defineStore } from 'pinia';
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
import {
devLoginApi,
getAccessCodesApi,
getUserInfoApi,
loginApi,
logoutApi,
} from '#/api';
import { $t } from '#/locales';
export const useAuthStore = defineStore('auth', () => {
@@ -20,6 +26,50 @@ export const useAuthStore = defineStore('auth', () => {
const loginLoading = ref(false);
async function finalizeLogin(
accessToken: string,
options: {
notify?: boolean;
onSuccess?: () => Promise<void> | void;
skipRedirect?: boolean;
} = {},
) {
let userInfo: null | UserInfo = null;
accessStore.setAccessToken(accessToken);
const [fetchUserInfoResult, accessCodes] = await Promise.all([
fetchUserInfo(),
getAccessCodesApi(),
]);
userInfo = fetchUserInfoResult;
userStore.setUserInfo(userInfo);
accessStore.setAccessCodes(accessCodes);
if (accessStore.loginExpired) {
accessStore.setLoginExpired(false);
} else if (!options.skipRedirect) {
const homePath =
userInfo.homePath || preferences.app.defaultHomePath || '/';
options.onSuccess
? await options.onSuccess()
: await router.push(homePath);
}
if (options.notify !== false && userInfo?.nickname) {
ElNotification({
message: `${$t('authentication.loginSuccessDesc')}:${userInfo.nickname}`,
title: $t('authentication.loginSuccess'),
type: 'success',
});
}
return {
userInfo,
};
}
/**
* 异步处理登录操作
* Asynchronously handle the login process
@@ -29,53 +79,35 @@ export const useAuthStore = defineStore('auth', () => {
params: Recordable<any>,
onSuccess?: () => Promise<void> | void,
) {
// 异步处理用户登录操作并获取 accessToken
let userInfo: null | UserInfo = null;
try {
loginLoading.value = true;
const { token: accessToken } = await loginApi(params);
// 如果成功获取到 accessToken
if (accessToken) {
// 将 accessToken 存储到 accessStore 中
accessStore.setAccessToken(accessToken);
// 获取用户信息并存储到 accessStore 中
const [fetchUserInfoResult, accessCodes] = await Promise.all([
fetchUserInfo(),
getAccessCodesApi(),
]);
userInfo = fetchUserInfoResult;
userStore.setUserInfo(userInfo);
accessStore.setAccessCodes(accessCodes);
if (accessStore.loginExpired) {
accessStore.setLoginExpired(false);
} else {
const homePath =
userInfo.homePath || preferences.app.defaultHomePath || '/';
onSuccess ? await onSuccess?.() : await router.push(homePath);
}
if (userInfo?.nickname) {
ElNotification({
message: `${$t('authentication.loginSuccessDesc')}:${userInfo?.nickname}`,
title: $t('authentication.loginSuccess'),
type: 'success',
});
}
return await finalizeLogin(accessToken, { onSuccess });
}
} finally {
loginLoading.value = false;
}
return {
userInfo,
userInfo: null,
};
}
async function authDevLogin(account: string) {
const { token: accessToken } = await devLoginApi({ account });
if (!accessToken) {
return {
userInfo: null,
};
}
return finalizeLogin(accessToken, {
notify: false,
skipRedirect: true,
});
}
async function logout(redirect: boolean = true) {
try {
await logoutApi();
@@ -109,6 +141,7 @@ export const useAuthStore = defineStore('auth', () => {
return {
$reset,
authDevLogin,
authLogin,
fetchUserInfo,
loginLoading,