7d1cc2da1a
Co-authored-by: Copilot <copilot@github.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import type { H3Event } from "h3";
|
||
import { createError, getCookie } from "h3";
|
||
import {
|
||
NEWAPI_SESSION_COOKIE,
|
||
NEWAPI_USER_ID_COOKIE
|
||
} from "~~/server/utils/newApiAuthCookies";
|
||
|
||
/** 上游 NewAPI 服务的根地址,只在这里维护一次 */
|
||
const BASE_URL = "https://api.qflink.xyz";
|
||
|
||
type FetchOptions = Parameters<typeof $fetch>[1];
|
||
type FetchHeaders = HeadersInit | undefined;
|
||
|
||
interface INewApiRawResponse<T> {
|
||
/** 上游响应 body,ofetch raw response 使用 _data 保存解析后的 body */
|
||
_data?: T;
|
||
/** 上游响应头,登录时需要从 Set-Cookie 中提取 session */
|
||
headers: Headers;
|
||
}
|
||
|
||
/**
|
||
* 向上游 NewAPI 发起请求
|
||
*
|
||
* @param path 相对路径,如 "/api/user/register",会自动拼接到 BASE_URL 后面
|
||
* @param options 透传给 $fetch 的所有选项(method、body、headers 等)
|
||
* @returns Promise<T>,T 是上游返回的数据类型
|
||
*
|
||
* @example
|
||
* const result = await newApiFetch<string>("/api/user/register", {
|
||
* method: "POST",
|
||
* body: { username: "xxx" }
|
||
* })
|
||
*/
|
||
export const newApiFetch = <T>(
|
||
path: string,
|
||
options?: FetchOptions
|
||
): Promise<T> => {
|
||
return $fetch<T>(path, {
|
||
baseURL: BASE_URL,
|
||
...options
|
||
}) as Promise<T>;
|
||
};
|
||
|
||
/**
|
||
* 向上游 NewAPI 发起请求,并保留响应头。
|
||
*
|
||
* 仅在登录这类需要读取 Set-Cookie 的场景使用;普通业务请求继续用
|
||
* newApiFetch,保证上游请求入口仍然集中在本文件。
|
||
*/
|
||
export const newApiFetchRaw = async <T>(
|
||
path: string,
|
||
options?: FetchOptions
|
||
): Promise<INewApiRawResponse<T>> => {
|
||
const res = await $fetch.raw<T>(path, {
|
||
baseURL: BASE_URL,
|
||
...options
|
||
});
|
||
|
||
return {
|
||
_data: res._data as T | undefined,
|
||
headers: res.headers
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 向上游 NewAPI 发起已登录请求。
|
||
*
|
||
* 前端不读取 NewAPI session;这里只在 Nuxt server 内从本站 httpOnly
|
||
* cookie 读取 session/userId,并按 NewAPI 要求补齐 Cookie 与 New-Api-User。
|
||
*/
|
||
export const newApiAuthedFetch = <T>(
|
||
event: H3Event,
|
||
path: string,
|
||
options?: FetchOptions
|
||
): Promise<T> => {
|
||
const session = getCookie(event, NEWAPI_SESSION_COOKIE);
|
||
const userId = getCookie(event, NEWAPI_USER_ID_COOKIE);
|
||
|
||
if (!session || !userId) {
|
||
throw createError({
|
||
statusCode: 401,
|
||
statusMessage: "未登录"
|
||
});
|
||
}
|
||
|
||
return newApiFetch<T>(path, {
|
||
...options,
|
||
headers: {
|
||
...normalizeHeaders(options?.headers),
|
||
Cookie: `session=${session}`,
|
||
"New-Api-User": userId
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 归一化调用方 headers,追加鉴权头时不丢失已有 Content-Type */
|
||
const normalizeHeaders = (headers: FetchHeaders) => {
|
||
if (!headers) return {};
|
||
return Object.fromEntries(new Headers(headers).entries());
|
||
};
|