// server/api/auth/login.post.ts import type { UserLoginRequest, UserLoginResult } from "#shared/types"; import { createUpstreamErrorResponse } from "~~/server/utils"; /** * 允许从请求体透传给上游的字段列表 * 只有在这里声明过的字段才会被转发,其余字段一律丢弃,防止参数污染 * 与 NewAPI 文档字段保持一致 */ const LOGIN_FIELDS = ["username", "password"] as const satisfies ReadonlyArray< keyof UserLoginRequest >; /** * 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 | null>(event); // 防御性校验:请求体只能是 JSON 对象或空,不接受字符串/数字等原始值 if (requestBody !== null && typeof requestBody !== "object") { return createErrorResponse(400, "请求体必须是 JSON 对象"); } // 白名单过滤:只保留文档里声明的字段,且必须是字符串类型 const payload: UserLoginRequest = {}; for (const field of LOGIN_FIELDS) { const value = requestBody?.[field]; if (typeof value === "string") { payload[field] = value; } } try { // 调用上游 NewAPI 登录接口,baseURL 已在 newApiFetch 内部写死,无需再传 await newApiFetch("/api/user/login", { method: "POST", body: payload, headers: { "Content-Type": "application/json" } }); return createSuccessResponse(null, "登录成功"); } catch (error) { return createUpstreamErrorResponse(error, "登录失败"); } });