Files
labelmain-demo/app/api/transfer/[path]/route.ts
2026-04-23 14:47:14 +08:00

333 lines
9.0 KiB
TypeScript

import {
handleDeleteCookie,
handleLoginData,
handleRefreshToken,
} from "@/components/login/libs/cookie"
import { getSessionCacheTtlMs } from "@/components/login/libs/session-cache"
import { getClientIpFromRequest } from "@/components/login/libs/session-common"
import { genAccessToken } from "@/components/login/libs/session"
import {
// NextRequest,
NextResponse,
} from "next/server"
const isParamsValid = (params: {} | null) =>
params !== null && // 排除 null
typeof params === "object" && // 确保是对象类型
!Array.isArray(params) && // 排除数组(可选)
Object.keys(params).length > 0 // 检查是否有自身可枚举属性
const formatDurationMs = (durationMs: number) =>
Number.isFinite(durationMs) ? durationMs.toFixed(2) : "0.00"
const buildServerTimingHeader = (props: {
sessionLookupMs: number
redisLookupMs: number
sessionSource: "memory" | "shared" | "redis" | "skip"
upstreamFetchMs: number
totalMs: number
}) => {
const {
sessionLookupMs,
redisLookupMs,
sessionSource,
upstreamFetchMs,
totalMs,
} = props
const cacheTtlMs = getSessionCacheTtlMs()
return [
`session-lookup;dur=${formatDurationMs(sessionLookupMs)};desc="${sessionSource}"`,
`redis;dur=${formatDurationMs(redisLookupMs)};desc="${sessionSource === "redis" ? "get" : sessionSource}"`,
`upstream;dur=${formatDurationMs(upstreamFetchMs)}`,
`total;dur=${formatDurationMs(totalMs)}`,
`session-cache;dur=${sessionSource === "memory" ? "1" : "0"};desc="ttl=${cacheTtlMs}ms"`,
].join(", ")
}
const attachServerTiming = (
response: Response | NextResponse,
props: {
sessionLookupMs: number
redisLookupMs: number
sessionSource: "memory" | "shared" | "redis" | "skip"
upstreamFetchMs: number
totalMs: number
}
) => {
const nextResponse =
response instanceof NextResponse
? response
: new NextResponse(response.body, {
status: response.status,
headers: response.headers,
})
nextResponse.headers.set("Server-Timing", buildServerTimingHeader(props))
return nextResponse
}
export async function POST(req: any) {
const requestStart = performance.now()
const timing = {
sessionLookupMs: 0,
redisLookupMs: 0,
sessionSource: "skip" as "memory" | "shared" | "redis" | "skip",
upstreamFetchMs: 0,
}
try {
const body = await req.json()
let {
url,
data,
options = {},
method = "GET",
isDownload = false,
isLogin = false,
isRefresh = false,
isDeleteCookie = false,
notJson = false,
} = body
if (isDeleteCookie) {
await handleDeleteCookie()
return attachServerTiming(
new NextResponse(
JSON.stringify({
message: "Successfully delete cookie!",
code: 200,
status: true,
}),
{
headers: {
"Content-Type": "application/json",
},
}
),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
let userData: any = null
if (!isLogin) {
const sessionResult = await genAccessToken(req)
timing.sessionLookupMs = sessionResult.timing.sessionLookupMs
timing.redisLookupMs = sessionResult.timing.redisLookupMs
timing.sessionSource = sessionResult.timing.sessionSource
if (!sessionResult.ok) {
return attachServerTiming(
NextResponse.json(
{ message: sessionResult.message || "未获取到用户信息", code: 401 },
{ status: 401 }
),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
userData = sessionResult.userData
}
let fetchOptions: any = {}
if (isRefresh) {
if (!userData?.refresh_token) {
return attachServerTiming(
NextResponse.json(
{ message: "Refresh token is empty", code: 406 },
{ status: 200 }
),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
fetchOptions = {
method,
headers: {
"content-type": "application/json",
["Token"]: userData?.refresh_token,
["Refresh-Token"]: userData?.refresh_token,
},
// body: JSON.stringify({
// token: userData?.refresh_token,
// }),
}
} else {
const requestHeaders: Record<string, string> = {
"content-type": "application/json",
}
if (!isLogin) {
requestHeaders.Token = `${userData?.access_token || ""}`
requestHeaders["Refresh-Token"] = `${userData?.refresh_token || ""}`
}
fetchOptions = {
method,
headers: requestHeaders,
}
if (method === "POST" || method === "PUT" || method === "DELETE") {
fetchOptions.body = data
} else {
if (isParamsValid(data)) {
const queryString = new URLSearchParams(
Object.entries(data).map(([key, value]) => [key, String(value)])
).toString()
if (queryString) {
url += url.includes("?") ? `&${queryString}` : `?${queryString}`
}
}
}
}
const isPrefixUrl = url.startsWith("http://") || url.startsWith("https://")
const fetchUrl = isPrefixUrl ? url : `${process.env.NEXT_PUBLIC_URL}${url}`
const upstreamFetchStart = performance.now()
const res = await fetch(fetchUrl, fetchOptions)
timing.upstreamFetchMs = performance.now() - upstreamFetchStart
if (res?.status !== 200) {
let error = ""
if (res.headers.get("Content-Type")?.includes("application/json")) {
const data = await res.json()
error = data?.message || ""
} else {
error = await res.text()
}
return attachServerTiming(
NextResponse.json({ message: error }, { status: res?.status }),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
if (
options?.responseType === "blob" ||
options?.responseType === "arraybuffer" ||
isDownload
) {
return attachServerTiming(
new NextResponse(res.body, {
status: res.status,
headers: {
"Content-Type":
res.headers.get("Content-Type") || "application/octet-stream",
"x-real-name": res?.headers?.get("content-disposition") || "",
},
}),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
} else if (!notJson) {
if (!isLogin && !isRefresh) {
return attachServerTiming(
new NextResponse(res.body, {
status: res.status,
headers: {
"Content-Type":
res.headers.get("Content-Type") || "application/json",
},
}),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
try {
const data = await (res.headers
.get("Content-Type")
?.includes("application/json")
? res.json()
: res.text())
if (isLogin) {
const newData = await handleLoginData({
clientIP: getClientIpFromRequest(req),
fingerprint: body.fingerprint,
data,
})
return attachServerTiming(
new NextResponse(JSON.stringify(newData), {
headers: {
"Content-Type": "application/json",
},
}),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
if (isRefresh) {
const newData = await handleRefreshToken({
userData,
data,
})
return attachServerTiming(
new NextResponse(JSON.stringify(newData), {
headers: {
"Content-Type": "application/json",
},
}),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
return attachServerTiming(
new NextResponse(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
},
}),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
} catch (err) {
return attachServerTiming(
NextResponse.json({ error: err }, { status: 500 }),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
} else {
return attachServerTiming(res, {
...timing,
totalMs: performance.now() - requestStart,
})
}
} catch (err) {
return attachServerTiming(
NextResponse.json({ error: err }, { status: 500 }),
{
...timing,
totalMs: performance.now() - requestStart,
}
)
}
}