@@ -1,36 +1,40 @@
|
||||
// server/api/auth/login.post.ts
|
||||
import type { UserLoginRequest, UserLoginResult } from "#shared/types";
|
||||
import { createUpstreamErrorResponse } from "~~/server/utils";
|
||||
import type { IUserLoginData, IUserLoginRequest } from "#shared/types";
|
||||
import {
|
||||
createErrorResponse,
|
||||
createSuccessResponse,
|
||||
createUpstreamErrorResponse,
|
||||
newApiFetch
|
||||
} from "~~/server/utils";
|
||||
|
||||
interface INewApiLoginResponse {
|
||||
/** 登录成功时上游返回的用户信息 */
|
||||
data?: IUserLoginData | null;
|
||||
/** 上游提示信息 */
|
||||
message?: string;
|
||||
/** 上游业务成功状态 */
|
||||
success?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许从请求体透传给上游的字段列表
|
||||
* 只有在这里声明过的字段才会被转发,其余字段一律丢弃,防止参数污染
|
||||
* 与 NewAPI 文档字段保持一致
|
||||
* 允许从请求体透传给上游的字段列表。
|
||||
* 只有在这里声明过的字段才会被转发,其余字段一律丢弃,防止参数污染。
|
||||
*/
|
||||
const LOGIN_FIELDS = ["username", "password"] as const satisfies ReadonlyArray<
|
||||
keyof UserLoginRequest
|
||||
keyof IUserLoginRequest
|
||||
>;
|
||||
|
||||
/**
|
||||
* POST /api/auth/login
|
||||
*
|
||||
* 登录流程:
|
||||
* 1. 解析并校验前端传来的 JSON 请求体
|
||||
* 2. 从请求体里只挑出文档允许的字段组装 payload
|
||||
* 3. 通过 newApiFetch 转发到上游 NewAPI 登录接口
|
||||
* 4. 上游响应体为空,成功时返回 { code: 0, data: null, msg: "登录成功" }
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 读取并反序列化请求体;如果前端没有传 body,readBody 会返回 null
|
||||
const requestBody = await readBody<Partial<UserLoginRequest> | null>(event);
|
||||
const requestBody = await readBody<Partial<IUserLoginRequest> | null>(event);
|
||||
|
||||
// 防御性校验:请求体只能是 JSON 对象或空,不接受字符串/数字等原始值
|
||||
if (requestBody !== null && typeof requestBody !== "object") {
|
||||
return createErrorResponse(400, "请求体必须是 JSON 对象");
|
||||
}
|
||||
|
||||
// 白名单过滤:只保留文档里声明的字段,且必须是字符串类型
|
||||
const payload: UserLoginRequest = {};
|
||||
const payload: IUserLoginRequest = {};
|
||||
for (const field of LOGIN_FIELDS) {
|
||||
const value = requestBody?.[field];
|
||||
if (typeof value === "string") {
|
||||
@@ -39,8 +43,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用上游 NewAPI 登录接口,baseURL 已在 newApiFetch 内部写死,无需再传
|
||||
await newApiFetch<UserLoginResult>("/api/user/login", {
|
||||
const result = await newApiFetch<INewApiLoginResponse>("/api/user/login", {
|
||||
method: "POST",
|
||||
body: payload,
|
||||
headers: {
|
||||
@@ -48,7 +51,14 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
return createSuccessResponse(null, "登录成功");
|
||||
const message =
|
||||
result.message || (result.success ? "登录成功" : "登录失败");
|
||||
|
||||
if (result.success !== true) {
|
||||
return createErrorResponse(1, message, result.data ?? null);
|
||||
}
|
||||
|
||||
return createSuccessResponse<IUserLoginData>(result.data ?? null, message);
|
||||
} catch (error) {
|
||||
return createUpstreamErrorResponse(error, "登录失败");
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
// server/api/auth/logout.get.ts
|
||||
import type { UserLogoutResult } from "#shared/types";
|
||||
import type { IUserLogoutData } from "#shared/types";
|
||||
import {
|
||||
createSuccessResponse,
|
||||
createUpstreamErrorResponse,
|
||||
newApiFetch
|
||||
} from "~~/server/utils";
|
||||
|
||||
/**
|
||||
* GET /api/auth/logout
|
||||
*
|
||||
* 登出流程:
|
||||
* 1. 服务端直接转发 GET 请求到上游 NewAPI
|
||||
* 2. 上游成功后统一返回 { code: 0, data: null, msg: "登出成功" }
|
||||
*/
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
await newApiFetch<UserLogoutResult>("/api/user/logout", {
|
||||
await newApiFetch<IUserLogoutData>("/api/user/logout", {
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
return createSuccessResponse(null, "登出成功");
|
||||
return createSuccessResponse<IUserLogoutData>(null, "登出成功");
|
||||
} catch (error) {
|
||||
return createUpstreamErrorResponse(error, "登出失败");
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
// server/api/auth/register.post.ts
|
||||
import type { UserRegisterRequest, UserRegisterResult } from "#shared/types";
|
||||
import type {
|
||||
IUserRegisterData,
|
||||
IUserRegisterRequest
|
||||
} from "#shared/types";
|
||||
import {
|
||||
createUpstreamErrorResponse,
|
||||
createErrorResponse,
|
||||
createSuccessResponse,
|
||||
createUpstreamErrorResponse,
|
||||
newApiFetch
|
||||
} from "../../utils";
|
||||
} from "~~/server/utils";
|
||||
|
||||
/**
|
||||
* 允许从请求体透传给上游的字段列表
|
||||
* 只有在这里声明过的字段才会被转发,其余字段一律丢弃,防止参数污染
|
||||
* 与 NewAPI 文档字段保持一致
|
||||
* 允许从请求体透传给上游的字段列表。
|
||||
* 只有在这里声明过的字段才会被转发,其余字段一律丢弃,防止参数污染。
|
||||
*/
|
||||
const REGISTER_FIELDS = [
|
||||
"username",
|
||||
@@ -18,30 +20,21 @@ const REGISTER_FIELDS = [
|
||||
"email",
|
||||
"verification_code",
|
||||
"aff_code"
|
||||
] as const satisfies ReadonlyArray<keyof UserRegisterRequest>;
|
||||
] as const satisfies ReadonlyArray<keyof IUserRegisterRequest>;
|
||||
|
||||
/**
|
||||
* POST /api/auth/register
|
||||
*
|
||||
* 注册流程:
|
||||
* 1. 解析并校验前端传来的 JSON 请求体
|
||||
* 2. 从请求体里只挑出文档允许的字段组装 payload
|
||||
* 3. 通过 newApiFetch 转发到上游 NewAPI 注册接口
|
||||
* 4. 将上游结果统一包装为 ApiResponse 格式返回给前端
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 读取并反序列化请求体;如果前端没有传 body,readBody 会返回 null
|
||||
const requestBody = await readBody<Partial<UserRegisterRequest> | null>(
|
||||
const requestBody = await readBody<Partial<IUserRegisterRequest> | null>(
|
||||
event
|
||||
);
|
||||
|
||||
// 防御性校验:请求体只能是 JSON 对象或空,不接受字符串/数字等原始值
|
||||
if (requestBody !== null && typeof requestBody !== "object") {
|
||||
return createErrorResponse(400, "请求体必须是 JSON 对象");
|
||||
}
|
||||
|
||||
// 白名单过滤:只保留文档里声明的字段,且必须是字符串类型
|
||||
const payload: UserRegisterRequest = {};
|
||||
const payload: IUserRegisterRequest = {};
|
||||
for (const field of REGISTER_FIELDS) {
|
||||
const value = requestBody?.[field];
|
||||
if (typeof value === "string") {
|
||||
@@ -50,7 +43,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await newApiFetch<UserRegisterResult>("/api/user/register", {
|
||||
const result = await newApiFetch<IUserRegisterData>("/api/user/register", {
|
||||
method: "POST",
|
||||
body: payload,
|
||||
headers: {
|
||||
@@ -58,7 +51,6 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 上游成功
|
||||
return createSuccessResponse(result, "注册成功");
|
||||
} catch (error) {
|
||||
return createUpstreamErrorResponse(error, "注册失败");
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { ApiResponse } from "#shared/types";
|
||||
import type { ICommonResponse } from "#shared/types";
|
||||
|
||||
/**
|
||||
* 构造统一成功响应
|
||||
* 构造统一成功响应。
|
||||
*
|
||||
* @param data 要返回给前端的数据,没有数据时传 null(默认)
|
||||
* @param msg 提示信息,默认 "请求成功"
|
||||
* @returns 符合 ApiResponse 格式的对象,code 固定为 0
|
||||
* @param data 要返回给前端的数据,没有数据时传 null
|
||||
* @param msg 提示信息,默认“请求成功”
|
||||
* @returns 符合 ICommonResponse 格式的对象,code 固定为 0
|
||||
*/
|
||||
export const createSuccessResponse = <T>(
|
||||
data: T | null = null,
|
||||
msg: string = "请求成功"
|
||||
): ApiResponse<T> => {
|
||||
): ICommonResponse<T> => {
|
||||
return {
|
||||
code: 0,
|
||||
data,
|
||||
@@ -19,18 +19,18 @@ export const createSuccessResponse = <T>(
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造统一错误响应
|
||||
* 构造统一错误响应。
|
||||
*
|
||||
* @param code 错误码,默认 400(请求参数错误);上游错误时传上游状态码
|
||||
* @param msg 错误描述,展示给前端的提示文字
|
||||
* @param data 附带的错误详情,通常是上游返回的原始响应体,默认 null
|
||||
* @returns 符合 ApiResponse 格式的对象,data 固定为 null 或错误详情
|
||||
* @param code 错误码,默认 400;上游错误时传上游状态码
|
||||
* @param msg 错误描述,展示给前端的提示文案
|
||||
* @param data 附带的错误详情,通常是上游返回的原始响应体
|
||||
* @returns 符合 ICommonResponse 格式的错误响应
|
||||
*/
|
||||
export const createErrorResponse = (
|
||||
code: number = 400,
|
||||
msg: string = "请求失败",
|
||||
data: any = null
|
||||
): ApiResponse => {
|
||||
data: unknown = null
|
||||
): ICommonResponse => {
|
||||
return {
|
||||
code,
|
||||
data,
|
||||
@@ -39,16 +39,16 @@ export const createErrorResponse = (
|
||||
};
|
||||
|
||||
/**
|
||||
* 将上游请求错误转换为统一响应
|
||||
* 将上游请求错误转换为统一响应。
|
||||
*
|
||||
* @param error ofetch 抛出的错误对象
|
||||
* @param fallbackMessage 当上游没有提供可用错误信息时的兜底提示
|
||||
* @returns 符合 ApiResponse 格式的错误响应
|
||||
* @returns 符合 ICommonResponse 格式的错误响应
|
||||
*/
|
||||
export const createUpstreamErrorResponse = (
|
||||
error: unknown,
|
||||
fallbackMessage: string = "请求失败"
|
||||
): ApiResponse => {
|
||||
): ICommonResponse => {
|
||||
const fetchError = error as {
|
||||
data?: unknown;
|
||||
message?: string;
|
||||
|
||||
Reference in New Issue
Block a user