diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/auth/DevLoginController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/auth/DevLoginController.java new file mode 100644 index 0000000..974f0d2 --- /dev/null +++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/auth/DevLoginController.java @@ -0,0 +1,37 @@ +package tech.easyflow.admin.controller.auth; + +import cn.dev33.satoken.annotation.SaIgnore; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Profile; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import tech.easyflow.auth.config.DevLoginGuard; +import tech.easyflow.auth.entity.LoginVO; +import tech.easyflow.auth.service.AuthService; +import tech.easyflow.common.domain.Result; +import tech.easyflow.common.web.jsonbody.JsonBody; + +@Profile("dev") +@RestController +@RequestMapping("/api/v1/auth/") +@ConditionalOnProperty(prefix = "easyflow.login.dev-bypass", name = "enabled", havingValue = "true") +public class DevLoginController { + + private final AuthService authService; + private final DevLoginGuard devLoginGuard; + + public DevLoginController(AuthService authService, DevLoginGuard devLoginGuard) { + this.authService = authService; + this.devLoginGuard = devLoginGuard; + } + + @SaIgnore + @PostMapping("dev-login") + public Result devLogin(HttpServletRequest request, + @JsonBody(value = "account", required = true) String account) { + devLoginGuard.checkAccess(request, account); + return Result.ok(authService.devLogin(account)); + } +} diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysTempTokenController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysTempTokenController.java deleted file mode 100644 index 3fc6975..0000000 --- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/system/SysTempTokenController.java +++ /dev/null @@ -1,33 +0,0 @@ -package tech.easyflow.admin.controller.system; - -import cn.dev33.satoken.annotation.SaIgnore; -import cn.dev33.satoken.stp.StpUtil; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import tech.easyflow.common.constant.Constants; -import tech.easyflow.common.domain.Result; -import tech.easyflow.common.entity.LoginAccount; - -import java.math.BigInteger; - -@RestController -@RequestMapping("/api/temp-token") -public class SysTempTokenController { - - @GetMapping("/create") - @SaIgnore - public Result createTempToken() { - - StpUtil.login(0); - String tokenValue = StpUtil.getTokenValue(); - LoginAccount loginAccount = new LoginAccount(); - loginAccount.setId(BigInteger.valueOf(0)); - loginAccount.setLoginName("匿名用户"); - loginAccount.setTenantId(BigInteger.ZERO); - loginAccount.setDeptId(BigInteger.ZERO); - StpUtil.getSession().set(Constants.LOGIN_USER_KEY, loginAccount); - - return Result.ok("", tokenValue); - } -} diff --git a/easyflow-modules/easyflow-module-auth/pom.xml b/easyflow-modules/easyflow-module-auth/pom.xml index 666c324..c0712cc 100644 --- a/easyflow-modules/easyflow-module-auth/pom.xml +++ b/easyflow-modules/easyflow-module-auth/pom.xml @@ -24,5 +24,11 @@ tech.easyflow easyflow-module-system + + junit + junit + ${junit.version} + test + diff --git a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/DevLoginGuard.java b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/DevLoginGuard.java new file mode 100644 index 0000000..6fe8514 --- /dev/null +++ b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/DevLoginGuard.java @@ -0,0 +1,48 @@ +package tech.easyflow.auth.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import tech.easyflow.common.web.exceptions.BusinessException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +@Component +public class DevLoginGuard { + + private final LoginProperties properties; + + public DevLoginGuard(LoginProperties properties) { + this.properties = properties; + } + + public void checkAccess(HttpServletRequest request, String account) { + if (!isAllowedAccount(account)) { + throw new BusinessException("仅允许使用 admin 账号进行开发免登"); + } + if (properties.getDevBypass().isLoopbackOnly() && !isLoopbackRequest(request)) { + throw new BusinessException("开发免登仅允许本机访问"); + } + } + + boolean isAllowedAccount(String account) { + return StringUtils.hasText(account) + && account.equals(properties.getDevBypass().getAccount()); + } + + boolean isLoopbackRequest(HttpServletRequest request) { + return request != null && isLoopbackAddress(request.getRemoteAddr()); + } + + boolean isLoopbackAddress(String remoteAddr) { + if (!StringUtils.hasText(remoteAddr)) { + return false; + } + try { + return InetAddress.getByName(remoteAddr).isLoopbackAddress(); + } catch (UnknownHostException ex) { + return false; + } + } +} diff --git a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/LoginProperties.java b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/LoginProperties.java index ea33f04..16c764a 100644 --- a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/LoginProperties.java +++ b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/config/LoginProperties.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration; public class LoginProperties { private String[] excludes; + private DevBypassProperties devBypass = new DevBypassProperties(); public String[] getExcludes() { return excludes; @@ -20,4 +21,43 @@ public class LoginProperties { public void setExcludes(String[] excludes) { this.excludes = excludes; } + + public DevBypassProperties getDevBypass() { + return devBypass; + } + + public void setDevBypass(DevBypassProperties devBypass) { + this.devBypass = devBypass; + } + + public static class DevBypassProperties { + + private String account = "admin"; + private boolean enabled = false; + private boolean loopbackOnly = true; + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isLoopbackOnly() { + return loopbackOnly; + } + + public void setLoopbackOnly(boolean loopbackOnly) { + this.loopbackOnly = loopbackOnly; + } + } } diff --git a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/AuthService.java b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/AuthService.java index 0fd5c7c..2734b0e 100644 --- a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/AuthService.java +++ b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/AuthService.java @@ -8,4 +8,9 @@ public interface AuthService { * 登录 */ LoginVO login(LoginDTO loginDTO); + + /** + * 开发模式免登录 + */ + LoginVO devLogin(String account); } diff --git a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/impl/AuthServiceImpl.java b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/impl/AuthServiceImpl.java index 3840418..7f7bb77 100644 --- a/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/impl/AuthServiceImpl.java +++ b/easyflow-modules/easyflow-module-auth/src/main/java/tech/easyflow/auth/service/impl/AuthServiceImpl.java @@ -38,35 +38,29 @@ public class AuthServiceImpl implements AuthService, StpInterface { @Override public LoginVO login(LoginDTO loginDTO) { - LoginVO res = new LoginVO(); try { TenantManager.ignoreTenantCondition(); String pwd = loginDTO.getPassword(); - QueryWrapper w = QueryWrapper.create(); - w.eq(SysAccount::getLoginName, loginDTO.getAccount()); - SysAccount record = sysAccountService.getOne(w); - if (record == null) { - throw new BusinessException("用户名/密码错误"); - } - if (EnumDataStatus.UNAVAILABLE.getCode().equals(record.getStatus())) { - throw new BusinessException("账号未启用,请联系管理员"); - } + SysAccount record = getAvailableAccount(loginDTO.getAccount(), "用户名/密码错误"); String pwdDb = record.getPassword(); if (!BCrypt.checkpw(pwd, pwdDb)) { throw new BusinessException("用户名/密码错误"); } - StpUtil.login(record.getId()); - LoginAccount loginAccount = new LoginAccount(); - BeanUtil.copyProperties(record, loginAccount); - StpUtil.getSession().set(Constants.LOGIN_USER_KEY, loginAccount); - String tokenValue = StpUtil.getTokenValue(); - res.setToken(tokenValue); - res.setNickname(record.getNickname()); - res.setAvatar(record.getAvatar()); + return createLoginVO(record); + } finally { + TenantManager.restoreTenantCondition(); + } + } + + @Override + public LoginVO devLogin(String account) { + try { + TenantManager.ignoreTenantCondition(); + SysAccount record = getAvailableAccount(account, "开发免登账号不存在"); + return createLoginVO(record); } finally { TenantManager.restoreTenantCondition(); } - return res; } @Override @@ -83,4 +77,30 @@ public class AuthServiceImpl implements AuthService, StpInterface { List roles = sysRoleService.getRolesByAccountId(BigInteger.valueOf(Long.parseLong(loginId.toString()))); return roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList()); } + + private LoginVO createLoginVO(SysAccount record) { + StpUtil.login(record.getId()); + LoginAccount loginAccount = new LoginAccount(); + BeanUtil.copyProperties(record, loginAccount); + StpUtil.getSession().set(Constants.LOGIN_USER_KEY, loginAccount); + + LoginVO res = new LoginVO(); + res.setToken(StpUtil.getTokenValue()); + res.setNickname(record.getNickname()); + res.setAvatar(record.getAvatar()); + return res; + } + + private SysAccount getAvailableAccount(String account, String accountNotFoundMessage) { + QueryWrapper w = QueryWrapper.create(); + w.eq(SysAccount::getLoginName, account); + SysAccount record = sysAccountService.getOne(w); + if (record == null) { + throw new BusinessException(accountNotFoundMessage); + } + if (EnumDataStatus.UNAVAILABLE.getCode().equals(record.getStatus())) { + throw new BusinessException("账号未启用,请联系管理员"); + } + return record; + } } diff --git a/easyflow-modules/easyflow-module-auth/src/test/java/tech/easyflow/auth/config/DevLoginGuardTest.java b/easyflow-modules/easyflow-module-auth/src/test/java/tech/easyflow/auth/config/DevLoginGuardTest.java new file mode 100644 index 0000000..24c8108 --- /dev/null +++ b/easyflow-modules/easyflow-module-auth/src/test/java/tech/easyflow/auth/config/DevLoginGuardTest.java @@ -0,0 +1,34 @@ +package tech.easyflow.auth.config; + +import org.junit.Assert; +import org.junit.Test; + +public class DevLoginGuardTest { + + @Test + public void shouldAcceptConfiguredAdminAccount() { + DevLoginGuard guard = new DevLoginGuard(createProperties()); + Assert.assertTrue(guard.isAllowedAccount("admin")); + Assert.assertFalse(guard.isAllowedAccount("guest")); + Assert.assertFalse(guard.isAllowedAccount(null)); + } + + @Test + public void shouldRecognizeLoopbackAddresses() { + DevLoginGuard guard = new DevLoginGuard(createProperties()); + Assert.assertTrue(guard.isLoopbackAddress("127.0.0.1")); + Assert.assertTrue(guard.isLoopbackAddress("::1")); + Assert.assertFalse(guard.isLoopbackAddress("192.168.1.10")); + Assert.assertFalse(guard.isLoopbackAddress("not-an-ip")); + } + + private LoginProperties createProperties() { + LoginProperties properties = new LoginProperties(); + LoginProperties.DevBypassProperties devBypass = new LoginProperties.DevBypassProperties(); + devBypass.setEnabled(true); + devBypass.setAccount("admin"); + devBypass.setLoopbackOnly(true); + properties.setDevBypass(devBypass); + return properties; + } +} diff --git a/easyflow-starter/easyflow-starter-all/src/main/resources/application.yml b/easyflow-starter/easyflow-starter-all/src/main/resources/application.yml index ef4c551..4ca406d 100644 --- a/easyflow-starter/easyflow-starter-all/src/main/resources/application.yml +++ b/easyflow-starter/easyflow-starter-all/src/main/resources/application.yml @@ -84,6 +84,10 @@ easyflow: login: # 放行接口路径 excludes: /api/v1/auth/**, /static/**, /userCenter/auth/**, /userCenter/public/** + dev-bypass: + enabled: true + account: admin + loopback-only: true storage: # local / xFileStorage type: xFileStorage diff --git a/easyflow-ui-admin/app/src/api/core/auth.ts b/easyflow-ui-admin/app/src/api/core/auth.ts index d0e8700..3741248 100644 --- a/easyflow-ui-admin/app/src/api/core/auth.ts +++ b/easyflow-ui-admin/app/src/api/core/auth.ts @@ -3,10 +3,15 @@ import { baseRequestClient, requestClient } from '#/api/request'; export namespace AuthApi { /** 登录接口参数 */ export interface LoginParams { + account?: string; password?: string; username?: string; } + export interface DevLoginParams { + account: string; + } + /** 登录接口返回值 */ export interface LoginResult { accessToken: string; @@ -26,6 +31,16 @@ export async function loginApi(data: AuthApi.LoginParams) { return requestClient.post('/api/v1/auth/login', data); } +/** + * 开发模式免登录 + */ +export async function devLoginApi(data: AuthApi.DevLoginParams) { + return requestClient.post( + '/api/v1/auth/dev-login', + data, + ); +} + /** * 刷新accessToken */ diff --git a/easyflow-ui-admin/app/src/router/__tests__/dev-login.test.ts b/easyflow-ui-admin/app/src/router/__tests__/dev-login.test.ts new file mode 100644 index 0000000..9dc4104 --- /dev/null +++ b/easyflow-ui-admin/app/src/router/__tests__/dev-login.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'vitest'; + +import { + getDevLoginAccount, + removeDevLoginQuery, + shouldAttemptDevLogin, +} from '../dev-login'; + +describe('dev-login route helpers', () => { + it('reads the admin account from the query string', () => { + expect(getDevLoginAccount({ devLogin: 'admin' })).toBe('admin'); + expect(getDevLoginAccount({ devLogin: ['admin', 'other'] })).toBe('admin'); + expect(getDevLoginAccount({ devLogin: ' ' })).toBeNull(); + }); + + it('removes only the devLogin query parameter', () => { + expect( + removeDevLoginQuery({ + devLogin: 'admin', + redirect: '/ai/workflow', + }), + ).toEqual({ + redirect: '/ai/workflow', + }); + }); + + it('attempts dev login only in dev mode and without an existing token', () => { + expect( + shouldAttemptDevLogin({ + account: 'admin', + hasAccessToken: false, + isDev: true, + }), + ).toBe(true); + expect( + shouldAttemptDevLogin({ + account: 'admin', + hasAccessToken: true, + isDev: true, + }), + ).toBe(false); + expect( + shouldAttemptDevLogin({ + account: 'guest', + hasAccessToken: false, + isDev: true, + }), + ).toBe(false); + expect( + shouldAttemptDevLogin({ + account: 'admin', + hasAccessToken: false, + isDev: false, + }), + ).toBe(false); + }); +}); diff --git a/easyflow-ui-admin/app/src/router/dev-login.ts b/easyflow-ui-admin/app/src/router/dev-login.ts new file mode 100644 index 0000000..d4d2661 --- /dev/null +++ b/easyflow-ui-admin/app/src/router/dev-login.ts @@ -0,0 +1,39 @@ +import type { LocationQuery, LocationQueryRaw } from 'vue-router'; + +const DEV_LOGIN_ACCOUNT = 'admin'; +const DEV_LOGIN_QUERY_KEY = 'devLogin'; + +function normalizeQueryValue( + value: LocationQuery[string] | LocationQueryRaw[string], +) { + if (Array.isArray(value)) { + return value[0] ?? null; + } + return value ?? null; +} + +export function getDevLoginAccount(query: LocationQuery | LocationQueryRaw) { + const value = normalizeQueryValue(query[DEV_LOGIN_QUERY_KEY]); + if (typeof value !== 'string') { + return null; + } + const account = value.trim(); + return account.length > 0 ? account : null; +} + +export function removeDevLoginQuery(query: LocationQuery | LocationQueryRaw) { + const { [DEV_LOGIN_QUERY_KEY]: _ignored, ...nextQuery } = query; + return nextQuery; +} + +export function shouldAttemptDevLogin(params: { + account: null | string; + hasAccessToken: boolean; + isDev: boolean; +}) { + return ( + params.isDev && + !params.hasAccessToken && + params.account === DEV_LOGIN_ACCOUNT + ); +} diff --git a/easyflow-ui-admin/app/src/router/guard.ts b/easyflow-ui-admin/app/src/router/guard.ts index 9db1514..6e20706 100644 --- a/easyflow-ui-admin/app/src/router/guard.ts +++ b/easyflow-ui-admin/app/src/router/guard.ts @@ -1,14 +1,19 @@ -import type {Router} from 'vue-router'; +import type { Router } from 'vue-router'; -import {LOGIN_PATH} from '@easyflow/constants'; -import {preferences} from '@easyflow/preferences'; -import {useAccessStore, useUserStore} from '@easyflow/stores'; -import {startProgress, stopProgress} from '@easyflow/utils'; +import { LOGIN_PATH } from '@easyflow/constants'; +import { preferences } from '@easyflow/preferences'; +import { useAccessStore, useUserStore } from '@easyflow/stores'; +import { startProgress, stopProgress } from '@easyflow/utils'; -import {accessRoutes, coreRouteNames} from '#/router/routes'; -import {useAuthStore} from '#/store'; +import { accessRoutes, coreRouteNames } from '#/router/routes'; +import { useAuthStore } from '#/store'; -import {generateAccess} from './access'; +import { generateAccess } from './access'; +import { + getDevLoginAccount, + removeDevLoginQuery, + shouldAttemptDevLogin, +} from './dev-login'; interface NetworkConnectionLike { effectiveType?: string; @@ -121,10 +126,59 @@ function setupCommonGuard(router: Router) { * @param router */ function setupAccessGuard(router: Router) { + let devLoginPromise: null | Promise = null; + router.beforeEach(async (to, from) => { const accessStore = useAccessStore(); const userStore = useUserStore(); const authStore = useAuthStore(); + const devLoginAccount = getDevLoginAccount(to.query); + + const resolveCleanFullPath = () => + router.resolve({ + hash: to.hash, + path: to.path, + query: removeDevLoginQuery(to.query), + }).fullPath; + + if (devLoginAccount && import.meta.env.DEV && accessStore.accessToken) { + return resolveCleanFullPath(); + } + + if ( + shouldAttemptDevLogin({ + account: devLoginAccount, + hasAccessToken: !!accessStore.accessToken, + isDev: import.meta.env.DEV, + }) + ) { + const account = devLoginAccount; + try { + devLoginPromise ??= authStore + .authDevLogin(account ?? '') + .then(() => undefined) + .finally(() => { + devLoginPromise = null; + }); + await devLoginPromise; + const cleanFullPath = resolveCleanFullPath(); + if (window.location.search.includes('devLogin=')) { + window.location.replace(cleanFullPath); + return false; + } + return cleanFullPath; + } catch { + const cleanFullPath = resolveCleanFullPath(); + return { + path: LOGIN_PATH, + query: + cleanFullPath === preferences.app.defaultHomePath + ? {} + : { redirect: encodeURIComponent(cleanFullPath) }, + replace: true, + }; + } + } // 基本路由,这些路由不需要进入权限拦截 if (coreRouteNames.includes(to.name as string)) { @@ -147,13 +201,17 @@ function setupAccessGuard(router: Router) { // 没有访问权限,跳转登录页面 if (to.fullPath !== LOGIN_PATH) { + const cleanFullPath = + devLoginAccount && import.meta.env.DEV + ? resolveCleanFullPath() + : to.fullPath; return { path: LOGIN_PATH, // 如不需要,直接删除 query query: - to.fullPath === preferences.app.defaultHomePath + cleanFullPath === preferences.app.defaultHomePath ? {} - : { redirect: encodeURIComponent(to.fullPath) }, + : { redirect: encodeURIComponent(cleanFullPath) }, // 携带当前跳转的页面,登录后重新跳转该页面 replace: true, }; diff --git a/easyflow-ui-admin/app/src/store/auth.ts b/easyflow-ui-admin/app/src/store/auth.ts index e9de50f..2782445 100644 --- a/easyflow-ui-admin/app/src/store/auth.ts +++ b/easyflow-ui-admin/app/src/store/auth.ts @@ -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; + 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, onSuccess?: () => Promise | 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,