-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(packages): optimistic subpackage
@sa/alova
(#646)
- Loading branch information
Showing
11 changed files
with
331 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import adapterFetch from 'alova/fetch'; | ||
export default adapterFetch; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from '@alova/mock'; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { alova } from '../request'; | ||
|
||
/** | ||
* Login | ||
* | ||
* @param userName User name | ||
* @param password Password | ||
*/ | ||
export function fetchLogin(userName: string, password: string) { | ||
return alova.Post<Api.Auth.LoginToken>('/auth/login', { userName, password }); | ||
} | ||
|
||
/** Get user info */ | ||
export function fetchGetUserInfo() { | ||
return alova.Get<Api.Auth.UserInfo>('/auth/getUserInfo'); | ||
} | ||
|
||
/** Send captcha to target phone */ | ||
export function sendCaptcha(phone: string) { | ||
return alova.Post<null>('/auth/sendCaptcha', { phone }); | ||
} | ||
|
||
/** Verify captcha */ | ||
export function verifyCaptcha(phone: string, code: string) { | ||
return alova.Post<null>('/auth/verifyCaptcha', { phone, code }); | ||
} | ||
|
||
/** | ||
* Refresh token | ||
* | ||
* @param refreshToken Refresh token | ||
*/ | ||
export function fetchRefreshToken(refreshToken: string) { | ||
return alova.Post<Api.Auth.LoginToken>( | ||
'/auth/refreshToken', | ||
{ refreshToken }, | ||
{ | ||
meta: { | ||
authRole: 'refreshToken' | ||
} | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* return custom backend error | ||
* | ||
* @param code error code | ||
* @param msg error message | ||
*/ | ||
export function fetchCustomBackendError(code: string, msg: string) { | ||
return alova.Get('/auth/error', { | ||
params: { code, msg }, | ||
shareRequest: false | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './auth'; | ||
export * from './route'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { alova } from '../request'; | ||
|
||
/** get constant routes */ | ||
export function fetchGetConstantRoutes() { | ||
return alova.Get<Api.Route.MenuRoute[]>('/route/getConstantRoutes'); | ||
} | ||
|
||
/** get user routes */ | ||
export function fetchGetUserRoutes() { | ||
return alova.Get<Api.Route.UserRoute>('/route/getUserRoutes'); | ||
} | ||
|
||
/** | ||
* whether the route is exist | ||
* | ||
* @param routeName route name | ||
*/ | ||
export function fetchIsRouteExist(routeName: string) { | ||
return alova.Get<boolean>('/route/isRouteExist', { params: { routeName } }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { defineMock } from '@sa/alova/mock'; | ||
|
||
// you can separate the mock data into multiple files dependent on your project versions | ||
export default defineMock({ | ||
'[POST]/systemManage/addUser': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'[POST]/systemManage/updateUser': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'[DELETE]/systemManage/deleteUser': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'[DELETE]/systemManage/batchDeleteUser': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'[POST]/auth/sendCaptcha': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'[POST]/auth/verifyCaptcha': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: null | ||
}; | ||
}, | ||
'/mock/getLastTime': () => { | ||
return { | ||
code: '0000', | ||
msg: 'success', | ||
data: { | ||
time: new Date().toLocaleTimeString() | ||
} | ||
}; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { createAlovaRequest } from '@sa/alova'; | ||
import { createAlovaMockAdapter } from '@sa/alova/mock'; | ||
import adapterFetch from '@sa/alova/fetch'; | ||
import { useAuthStore } from '@/store/modules/auth'; | ||
import { $t } from '@/locales'; | ||
import { getServiceBaseURL } from '@/utils/service'; | ||
import featureUsers20241014 from '../mocks/feature-users-20241014'; | ||
import { getAuthorization, handleRefreshToken, showErrorMsg } from './shared'; | ||
import type { RequestInstanceState } from './type'; | ||
|
||
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; | ||
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); | ||
|
||
const state: RequestInstanceState = { | ||
errMsgStack: [] | ||
}; | ||
const mockAdapter = createAlovaMockAdapter([featureUsers20241014], { | ||
// using requestAdapter if not match mock request | ||
httpAdapter: adapterFetch(), | ||
|
||
// response delay time | ||
delay: 1000, | ||
|
||
// global mock toggle | ||
enable: true, | ||
matchMode: 'methodurl' | ||
}); | ||
export const alova = createAlovaRequest( | ||
{ | ||
baseURL, | ||
requestAdapter: import.meta.env.DEV ? mockAdapter : adapterFetch() | ||
}, | ||
{ | ||
onRequest({ config }) { | ||
const Authorization = getAuthorization(); | ||
config.headers.Authorization = Authorization; | ||
config.headers.apifoxToken = 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'; | ||
}, | ||
tokenRefresher: { | ||
async isExpired(response) { | ||
const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || []; | ||
const { code } = await response.clone().json(); | ||
return expiredTokenCodes.includes(String(code)); | ||
}, | ||
async handler() { | ||
await handleRefreshToken(); | ||
} | ||
}, | ||
async isBackendSuccess(response) { | ||
// when the backend response code is "0000"(default), it means the request is success | ||
// to change this logic by yourself, you can modify the `VITE_SERVICE_SUCCESS_CODE` in `.env` file | ||
const resp = response.clone(); | ||
const data = await resp.json(); | ||
return String(data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE; | ||
}, | ||
async transformBackendResponse(response) { | ||
return (await response.clone().json()).data; | ||
}, | ||
async onError(error, response) { | ||
const authStore = useAuthStore(); | ||
|
||
let message = error.message; | ||
let responseCode = ''; | ||
if (response) { | ||
const data = await response?.clone().json(); | ||
message = data.msg; | ||
responseCode = String(data.code); | ||
} | ||
|
||
function handleLogout() { | ||
showErrorMsg(state, message); | ||
authStore.resetStore(); | ||
} | ||
|
||
function logoutAndCleanup() { | ||
handleLogout(); | ||
window.removeEventListener('beforeunload', handleLogout); | ||
state.errMsgStack = state.errMsgStack.filter(msg => msg !== message); | ||
} | ||
|
||
// when the backend response code is in `logoutCodes`, it means the user will be logged out and redirected to login page | ||
const logoutCodes = import.meta.env.VITE_SERVICE_LOGOUT_CODES?.split(',') || []; | ||
if (logoutCodes.includes(responseCode)) { | ||
handleLogout(); | ||
throw error; | ||
} | ||
|
||
// when the backend response code is in `modalLogoutCodes`, it means the user will be logged out by displaying a modal | ||
const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || []; | ||
if (modalLogoutCodes.includes(responseCode) && !state.errMsgStack?.includes(message)) { | ||
state.errMsgStack = [...(state.errMsgStack || []), message]; | ||
|
||
// prevent the user from refreshing the page | ||
window.addEventListener('beforeunload', handleLogout); | ||
|
||
window.$dialog?.error({ | ||
title: $t('common.error'), | ||
content: message, | ||
positiveText: $t('common.confirm'), | ||
maskClosable: false, | ||
closeOnEsc: false, | ||
onPositiveClick() { | ||
logoutAndCleanup(); | ||
}, | ||
onClose() { | ||
logoutAndCleanup(); | ||
} | ||
}); | ||
throw error; | ||
} | ||
showErrorMsg(state, message); | ||
throw error; | ||
} | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { useAuthStore } from '@/store/modules/auth'; | ||
import { localStg } from '@/utils/storage'; | ||
import { fetchRefreshToken } from '../api'; | ||
import type { RequestInstanceState } from './type'; | ||
|
||
export function getAuthorization() { | ||
const token = localStg.get('token'); | ||
const Authorization = token ? `Bearer ${token}` : null; | ||
|
||
return Authorization; | ||
} | ||
|
||
/** refresh token */ | ||
export async function handleRefreshToken() { | ||
const { resetStore } = useAuthStore(); | ||
|
||
const rToken = localStg.get('refreshToken') || ''; | ||
const refreshTokenMethod = fetchRefreshToken(rToken); | ||
|
||
// set the refreshToken role, so that the request will not be intercepted | ||
refreshTokenMethod.meta.authRole = 'refreshToken'; | ||
|
||
try { | ||
const data = await refreshTokenMethod; | ||
localStg.set('token', data.token); | ||
localStg.set('refreshToken', data.refreshToken); | ||
} catch (error) { | ||
resetStore(); | ||
throw error; | ||
} | ||
} | ||
|
||
export function showErrorMsg(state: RequestInstanceState, message: string) { | ||
if (!state.errMsgStack?.length) { | ||
state.errMsgStack = []; | ||
} | ||
|
||
const isExist = state.errMsgStack.includes(message); | ||
|
||
if (!isExist) { | ||
state.errMsgStack.push(message); | ||
|
||
window.$message?.error(message, { | ||
onLeave: () => { | ||
state.errMsgStack = state.errMsgStack.filter(msg => msg !== message); | ||
|
||
setTimeout(() => { | ||
state.errMsgStack = []; | ||
}, 5000); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface RequestInstanceState { | ||
/** the request error message stack */ | ||
errMsgStack: string[]; | ||
} |