// server/api/auth/me.get.ts - 当前用户接口:用 httpOnly 登录态恢复用户信息并刷新用户快照。 import type { IUserMeData } from "#shared/types"; import { clearNewApiAuthCookies, createApiLogger, createErrorResponse, createSuccessResponse, newApiAuthedFetch, toSafeLogError, upsertUserSnapshot } from "~~/server/utils"; interface INewApiWrappedUserResponse { /** 获取成功时上游返回的当前用户信息 */ data?: INewApiSelfUserData | null; /** 上游提示信息,失败时优先返回给前端 */ message?: string; /** 上游业务成功状态 */ success?: boolean; } type NewApiUserResponse = INewApiSelfUserData | INewApiWrappedUserResponse; interface INewApiSelfUserData extends IUserMeData { /** NewAPI /api/user/self 可能返回配额、设置、第三方绑定等额外字段 */ [key: string]: unknown; } /** * GET /api/auth/me * * 流程: * 1. 从本站 httpOnly cookie 读取 NewAPI session/userId。 * 2. 带 Cookie 与 New-Api-User 请求 NewAPI /api/user/self。 * 3. 裁剪上游 self 响应,只返回前端需要的基础用户字段。 * 4. 异步更新数据库里的用户快照。 * 5. cookie 缺失、过期或上游鉴权失败时清理本地登录态并返回 401。 */ export default defineEventHandler(async (event) => { const logger = createApiLogger("auth.me"); logger.info("开始"); try { // 从本站 httpOnly cookie 读取登录态,并转发给 NewAPI 查询当前用户。 const result = await newApiAuthedFetch( event, "/api/user/self", { method: "GET" } ); const normalized = normalizeUserResponse(result); if (!normalized.user) { // 上游业务失败代表当前 cookie 不再可靠,清理后返回统一 401。 clearNewApiAuthCookies(event); logger.warn("登录状态无效", { upstreamMessage: normalized.message }); return createErrorResponse(401, "未登录"); } upsertUserSnapshot(normalized.user).catch((error) => { logger.error("保存用户快照失败", { userId: normalized.user?.id, error: toSafeLogError(error) }); }); logger.done("成功", { userId: normalized.user.id, role: normalized.user.role, status: normalized.user.status }); return createSuccessResponse( normalized.user, "获取用户信息成功" ); } catch (error) { // cookie 缺失或上游鉴权异常时统一视为未登录。 clearNewApiAuthCookies(event); logger.warn("未登录或登录态失效", { error: toSafeLogError(error) }); return createErrorResponse(401, "未登录"); } }); /** 兼容上游直接返回用户对象和 { success, message, data } 包装结构 */ const normalizeUserResponse = ( response: NewApiUserResponse ): { message: string; user: IUserMeData | null } => { if (isUser(response)) { return { message: "获取用户信息成功", user: pickUserBasicData(response) }; } if (!isRecord(response)) { return { message: "未登录", user: null }; } const wrapped = response as INewApiWrappedUserResponse; const message = wrapped.success ? "获取用户信息成功" : "未登录"; if (wrapped.success !== true || !isUser(wrapped.data)) { return { message, user: null }; } return { message, user: pickUserBasicData(wrapped.data) }; }; /** 只返回前端需要的基础用户字段,避免泄露上游完整 self 信息 */ const pickUserBasicData = (user: INewApiSelfUserData): IUserMeData => { return { id: user.id, username: user.username, display_name: user.display_name, group: user.group, role: user.role, status: user.status }; }; const isUser = (value: unknown): value is INewApiSelfUserData => { return ( isRecord(value) && typeof value.id === "number" && typeof value.username === "string" && typeof value.display_name === "string" && typeof value.group === "string" && typeof value.role === "number" && typeof value.status === "number" ); }; const isRecord = (value: unknown): value is Record => { return value !== null && typeof value === "object"; };