2026-05-07 16:37:22 +08:00

129 lines
3.1 KiB
TypeScript

import sharp from 'sharp'
import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
const ALLOWED_IMAGE_HOST_SUFFIXES = [
'alicdn.com',
'taobaocdn.com',
'tbcdn.cn',
]
function isAllowedImageHost(hostname: string): boolean {
return ALLOWED_IMAGE_HOST_SUFFIXES.some(
(suffix) => hostname === suffix || hostname.endsWith(`.${suffix}`),
)
}
export async function GET(request: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session) {
return NextResponse.json(
{
success: false,
error: {
code: 'UNAUTHORIZED',
message: '请先登录后再导出图片',
},
},
{ status: 401 },
)
}
const rawUrl = request.nextUrl.searchParams.get('url')
if (!rawUrl) {
return NextResponse.json(
{
success: false,
error: {
code: 'MISSING_URL',
message: '缺少图片地址',
},
},
{ status: 400 },
)
}
let targetUrl: URL
try {
targetUrl = new URL(rawUrl)
} catch {
return NextResponse.json(
{
success: false,
error: {
code: 'INVALID_URL',
message: '图片地址不正确',
},
},
{ status: 400 },
)
}
if (!['http:', 'https:'].includes(targetUrl.protocol) || !isAllowedImageHost(targetUrl.hostname)) {
return NextResponse.json(
{
success: false,
error: {
code: 'IMAGE_HOST_NOT_ALLOWED',
message: '图片来源不允许导出',
},
},
{ status: 400 },
)
}
const upstream = await fetch(targetUrl.toString(), {
method: 'GET',
cache: 'no-store',
headers: {
'User-Agent': 'Mozilla/5.0 Codex Taobao Image Proxy',
Referer: 'https://item.taobao.com/',
},
})
if (!upstream.ok) {
return NextResponse.json(
{
success: false,
error: {
code: 'UPSTREAM_IMAGE_FETCH_FAILED',
message: `图片下载失败,上游返回 ${upstream.status}`,
},
},
{ status: 502 },
)
}
const buffer = Buffer.from(await upstream.arrayBuffer())
const pngBuffer = await sharp(buffer, { failOn: 'none' })
.flatten({ background: '#ffffff' })
.png()
.toBuffer()
const metadata = await sharp(pngBuffer).metadata()
return new NextResponse(Uint8Array.from(pngBuffer), {
status: 200,
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'private, max-age=300',
'X-Image-Width': String(metadata.width || 0),
'X-Image-Height': String(metadata.height || 0),
},
})
} catch (error) {
console.error('[api/taobao-detail-plus/image-proxy] GET error:', error)
return NextResponse.json(
{
success: false,
error: {
code: 'IMAGE_PROXY_ERROR',
message: '图片处理失败,请稍后再试',
},
},
{ status: 500 },
)
}
}