mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-29 06:36:41 +08:00
feat(packages): add @sa/alova
This commit is contained in:
parent
0ac95bdcf5
commit
494db95614
16
packages/alova/package.json
Normal file
16
packages/alova/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@sa/alova",
|
||||
"version": "0.1.0",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"alova": "^3.0.16"
|
||||
}
|
||||
}
|
5
packages/alova/src/constant.ts
Normal file
5
packages/alova/src/constant.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/** request id key */
|
||||
export const REQUEST_ID_KEY = 'X-Request-Id';
|
||||
|
||||
/** the backend error code key */
|
||||
export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';
|
83
packages/alova/src/index.ts
Normal file
83
packages/alova/src/index.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { createAlova } from 'alova';
|
||||
import VueHook from 'alova/vue';
|
||||
import adapterFetch from 'alova/fetch';
|
||||
import { nanoid } from '@sa/utils';
|
||||
import { createServerTokenAuthentication } from 'alova/client';
|
||||
import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
|
||||
import { isJSON } from './shared';
|
||||
|
||||
export interface CustomAlovaConfig {
|
||||
baseURL: string;
|
||||
headers?: Record<string, string>;
|
||||
timeout?: number;
|
||||
expiredTokenCodes: string[];
|
||||
}
|
||||
|
||||
export interface RequestOptions {
|
||||
refreshTokenHandler: () => Promise<boolean>;
|
||||
onBackendFail: (response: Response) => Promise<Response | null>;
|
||||
}
|
||||
|
||||
export const createAlovaRequest = (customConfig: CustomAlovaConfig, options: RequestOptions) => {
|
||||
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({
|
||||
refreshTokenOnSuccess: {
|
||||
isExpired: async response => {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (isJSON(contentType ?? '')) {
|
||||
const resp = response.clone();
|
||||
const data = await resp.json();
|
||||
const responseCode = String(data.code);
|
||||
if (customConfig.expiredTokenCodes.includes(responseCode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
handler: async () => {
|
||||
await options.refreshTokenHandler();
|
||||
}
|
||||
},
|
||||
// 附加token
|
||||
assignToken: method => {
|
||||
const token = localStorage.getItem('SOY_token');
|
||||
method.config.headers.Authorization = token ? `Bearer ${JSON.parse(token)}` : null;
|
||||
}
|
||||
});
|
||||
|
||||
const instance = createAlova({
|
||||
baseURL: customConfig.baseURL,
|
||||
timeout: customConfig.timeout ?? 10 * 1000,
|
||||
requestAdapter: adapterFetch(),
|
||||
statesHook: VueHook,
|
||||
beforeRequest: onAuthRequired(({ config }) => {
|
||||
// 添加配置headers
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
...customConfig.headers
|
||||
};
|
||||
|
||||
// set request id
|
||||
const requestId = nanoid();
|
||||
config.headers[REQUEST_ID_KEY] = requestId;
|
||||
}),
|
||||
responded: onResponseRefreshToken({
|
||||
onSuccess: async resp => {
|
||||
if (resp.ok || resp.status === 304) {
|
||||
if (isJSON(resp.headers.get('Content-Type') ?? '')) {
|
||||
const fail = await options.onBackendFail(resp);
|
||||
if (fail) {
|
||||
return fail;
|
||||
}
|
||||
return await resp.json();
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
return Promise.reject(resp);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
export { BACKEND_ERROR_CODE, REQUEST_ID_KEY };
|
3
packages/alova/src/shared.ts
Normal file
3
packages/alova/src/shared.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isJSON(contentType: string) {
|
||||
return contentType.includes('application/json');
|
||||
}
|
20
packages/alova/tsconfig.json
Normal file
20
packages/alova/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"types": ["node"],
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user