Skip to content
Closed
7 changes: 4 additions & 3 deletions apps/backend-mock/api/auth/refresh.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '~/utils/cookie-utils';
import { generateAccessToken, verifyRefreshToken } from '~/utils/jwt-utils';
import { MOCK_USERS } from '~/utils/mock-data';
import { forbiddenResponse } from '~/utils/response';
import { forbiddenResponse, useResponseSuccess } from '~/utils/response';

export default defineEventHandler(async (event) => {
const refreshToken = getRefreshTokenFromCookie(event);
Expand All @@ -30,6 +30,7 @@ export default defineEventHandler(async (event) => {
const accessToken = generateAccessToken(findUser);

setRefreshTokenCookie(event, refreshToken);

return accessToken;
return useResponseSuccess({
accessToken,
});
});
5 changes: 5 additions & 0 deletions packages/effects/plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"./motion": {
"types": "./src/motion/index.ts",
"default": "./src/motion/index.ts"
},
"./alova": {
"types": "./src/alova/index.ts",
"default": "./src/alova/index.ts"
}
},
"dependencies": {
Expand All @@ -41,6 +45,7 @@
"@vueuse/motion": "catalog:",
"echarts": "catalog:",
"vue": "catalog:",
"alova": "catalog:",

Check failure on line 48 in packages/effects/plugins/package.json

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest)

Expected object keys to be in ascending order. 'alova' should be before 'vue'
"vxe-pc-ui": "catalog:",
"vxe-table": "catalog:"
}
Expand Down
254 changes: 254 additions & 0 deletions packages/effects/plugins/src/alova/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import type {
Alova,
AlovaGenerics,
AlovaMethodCommonConfig,
AlovaMethodCreateConfig,
AlovaOptions,
Method,
RequestBody,
RespondedAlovaGenerics,
ResponseCompleteHandler,
ResponseErrorHandler,
ResponseSuccessHandler,
StatesHook,
} from 'alova';
import type {
AlovaRequestAdapterUnified,
TokenAuthenticationResult,
} from 'alova/client';

import { createAlova } from 'alova';
import adapterFetch from 'alova/fetch';
import VueHook from 'alova/vue';

/**
* 请求拦截器方法类型
*/
type RequestMethod<T extends AlovaGenerics = AlovaGenerics> = (
method: Method<T>,
) => Promise<void> | void;

/**
* Alova HTTP客户端封装类
* @template T 泛型参数,继承自AlovaGenerics
*/
class AlovaClient<T extends AlovaGenerics = AlovaGenerics> {
public readonly instance: Alova<T>;

/**
* 请求拦截器数组
*/
public requestInterceptor: RequestMethod<T>[] = [];

/**
* 请求完成拦截器数组
*/
public responseCompleteInterceptor: ResponseCompleteHandler<T>[] = [];

/**
* 请求错误拦截器数组
*/
public responseErrorInterceptor: ResponseErrorHandler<T>[] = [];

/**
* 请求成功拦截器数组
*/
public responseSuccessInterceptor: ResponseSuccessHandler<T>[] = [];
/**
* 构造函数
* @param options Alova配置选项
* @param authOptions 认证选项
*/
constructor(
options: AlovaOptions<T>,
authOptions?: TokenAuthenticationResult<
StatesHook<any>,
AlovaRequestAdapterUnified
>,
) {
const { onAuthRequired, onResponseRefreshToken } = authOptions || {};
const beforeRequest = async (method: Method<T>) => {
for (const interceptor of this.requestInterceptor) {
await interceptor(method);
}
};
const responded = {
// 请求成功的拦截器
// 当使用 `alova/fetch` 请求适配器时,第一个参数接收Response对象
// 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息
onSuccess: async (response: Response, method: Method<T>) => {
let result: any = null;
for (const interceptor of this.responseSuccessInterceptor) {
result = await interceptor(response, method);
if (result) {
return result;
}
}
},

onError: async (err: Error, method: Method<T>) => {
for (const interceptor of this.responseErrorInterceptor) {
await interceptor(err, method);
}
},

// 请求完成的拦截器
// 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。
// 接收当前请求的method实例
onComplete: async (method: Method<T>) => {
// 处理请求完成逻辑
for (const interceptor of this.responseCompleteInterceptor) {
await interceptor(method);
}
},
};

this.instance = createAlova({
requestAdapter: adapterFetch(),
statesHook: VueHook,
beforeRequest: onAuthRequired
? onAuthRequired(beforeRequest)
: beforeRequest,
// 使用 responded 对象分别指定请求成功的拦截器和请求失败的拦截器
responded: onResponseRefreshToken
? onResponseRefreshToken(responded)
: responded,
...options,
});
}
/**
* 添加请求拦截器
* @param method 拦截器方法
*/
public addRequestInterceptor(method: RequestMethod<T>) {
this.requestInterceptor.push(method);
}

/**
* 添加请求完成拦截器
* @param method 拦截器方法
*/
public addResponseCompleteInterceptor(method: ResponseCompleteHandler<T>) {
this.responseCompleteInterceptor.push(method);
}
/**
* 添加请求错误拦截器
* @param method 拦截器方法
*/
public addResponseErrorInterceptor(method: ResponseErrorHandler<T>) {
this.responseErrorInterceptor.push(method);
}
/**
* 添加请求成功拦截器
* @param method 拦截器方法
*/
public addResponseSuccessInterceptor(method: ResponseSuccessMethod) {
this.responseSuccessInterceptor.push(method);
}
/**
* 发送DELETE请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
* @returns Method实例
*/
public delete<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Delete(url, config, data);
}
/**
* 发送GET请求
* @param url 请求地址
* @param config 请求配置
* @returns Method实例
*/
public get<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Get(url, config);
}
/**
* 发送HEAD请求
* @param url 请求地址
* @param config 请求配置
* @returns Method实例
*/
public head<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Head(url, config);
}
/**
* 发送OPTIONS请求
* @param url 请求地址
* @param config 请求配置
* @returns Method实例
*/
public options<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Options(url, config);
}
/**
* 发送PATCH请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
* @returns Method实例
*/
public patch<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Patch(url, data, config);
}
/**
* 发送POST请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
* @returns Method实例
*/
public post<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Post(url, data, config);
}
/**
* 发送PUT请求
* @param url 请求地址
* @param data 请求数据
* @param config 请求配置
* @returns Method实例
*/
public put<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Put(url, data, config);
}
/**
* 发送自定义请求
* @param config 请求配置
* @returns Method实例
*/
public request<Responded = unknown, Transformed = unknown>(
config: AlovaMethodCommonConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Request(config);
}
}

export { AlovaClient, VueHook };
export * from 'alova';
export * from 'alova/client';
52 changes: 37 additions & 15 deletions playground/src/api/core/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { baseRequestClient, requestClient } from '#/api/request';
import { client, requestClient } from '#/api/request';

export namespace AuthApi {
/** 登录接口参数 */
Expand All @@ -21,32 +21,54 @@ export namespace AuthApi {
/**
* 登录
*/
export async function loginApi(data: AuthApi.LoginParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/login', data, {
withCredentials: true,
});
// export async function loginApi(data: AuthApi.LoginParams) {
// return requestClient.post<AuthApi.LoginResult>('/auth/login', data, {
// withCredentials: true,
// });
// }
export async function loginApi(params: AuthApi.LoginParams) {
const method = client.post<AuthApi.LoginResult>('/auth/login', params);
method.meta = {
authRole: 'login',
};
return method;
}

/**
* 刷新accessToken
*/
// export async function refreshTokenApi() {
// return baseRequestClient.post<AuthApi.RefreshTokenResult>(
// '/auth/refresh',
// null,
// {
// withCredentials: true,
// },
// );
// }
export async function refreshTokenApi() {
return baseRequestClient.post<AuthApi.RefreshTokenResult>(
'/auth/refresh',
null,
{
withCredentials: true,
},
);
const method = client.post<AuthApi.RefreshTokenResult>('/auth/refresh');
method.meta = {
authRole: 'refreshToken',
};
return method;
}

/**
* 退出登录
*/
// export async function logoutApi() {
// return baseRequestClient.post('/auth/logout', null, {
// withCredentials: true,
// });
// }

export async function logoutApi() {
return baseRequestClient.post('/auth/logout', null, {
withCredentials: true,
});
const method = client.post('/auth/logout');
method.meta = {
authRole: 'logout',
};
return method;
}

/**
Expand Down
7 changes: 5 additions & 2 deletions playground/src/api/core/user.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { UserInfo } from '@vben/types';

import { requestClient } from '#/api/request';
import { client } from '#/api/request';

/**
* 获取用户信息
*/
// export async function getUserInfoApi() {
// return requestClient.get<UserInfo>('/user/info');
// }
export async function getUserInfoApi() {
return requestClient.get<UserInfo>('/user/info');
return client.get<UserInfo>('/user/info');
}
Loading
Loading