/** * 诊断预览图URL是否可访问 * GET /api/diagnose/preview-image?url={预览图URL} */ import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { HeadObjectCommand } from '@aws-sdk/client-s3'; import { s3Client, isS3PublicUrl } from '@/lib/s3'; import { rewriteStorageUrlForClient } from '@/lib/url-rewrite'; export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session) { return NextResponse.json( { error: '未登录' }, { status: 401 } ); } const url = request.nextUrl.searchParams.get('url'); if (!url) { return NextResponse.json( { error: '缺少url参数' }, { status: 400 } ); } const results: any = { originalUrl: url, checks: [], }; // 1. 检查URL格式 let parsedUrl: URL; try { parsedUrl = new URL(url); results.checks.push({ name: 'URL格式', status: 'success', message: 'URL格式正确', details: { protocol: parsedUrl.protocol, hostname: parsedUrl.hostname, pathname: parsedUrl.pathname, } }); } catch (error) { results.checks.push({ name: 'URL格式', status: 'error', message: 'URL格式错误', error: error instanceof Error ? error.message : String(error) }); return NextResponse.json(results); } // 2. 检查URL重写后的地址 const rewrittenUrl = rewriteStorageUrlForClient(url, request); results.rewrittenUrl = rewrittenUrl; results.checks.push({ name: 'URL重写', status: rewrittenUrl !== url ? 'success' : 'info', message: rewrittenUrl !== url ? 'URL已重写' : 'URL未重写', details: { original: url, rewritten: rewrittenUrl, } }); // 3. 检查是否为S3 URL const isS3 = isS3PublicUrl(url); results.isS3Url = isS3; results.checks.push({ name: 'S3 URL检测', status: isS3 ? 'success' : 'info', message: isS3 ? '是S3公网URL' : '不是S3公网URL', }); // 4. 如果是S3 URL,检查文件是否存在 if (isS3) { try { // 从URL提取key const urlObj = new URL(url); const key = urlObj.pathname.slice(1); // 移除开头的斜杠 const command = new HeadObjectCommand({ Bucket: process.env.S3_BUCKET!, Key: key, }); const response = await s3Client.send(command); results.checks.push({ name: 'S3文件存在性', status: 'success', message: '文件存在于S3', details: { key: key, contentLength: response.ContentLength, contentType: response.ContentType, lastModified: response.LastModified?.toISOString(), } }); } catch (error: any) { if (error.name === 'NotFound' || error.$metadata?.httpStatusCode === 404) { results.checks.push({ name: 'S3文件存在性', status: 'error', message: '文件不存在于S3', error: '404 Not Found' }); } else { results.checks.push({ name: 'S3文件存在性', status: 'error', message: '检查S3文件时出错', error: error.message || String(error) }); } } } // 5. 尝试通过HTTP访问(使用重写后的URL) if (rewrittenUrl) { try { const response = await fetch(rewrittenUrl, { method: 'HEAD', signal: AbortSignal.timeout(5000), // 5秒超时 }); results.checks.push({ name: 'HTTP访问', status: response.ok ? 'success' : 'error', message: response.ok ? `HTTP访问成功 (${response.status})` : `HTTP访问失败 (${response.status})`, details: { status: response.status, statusText: response.statusText, contentType: response.headers.get('content-type'), contentLength: response.headers.get('content-length'), } }); } catch (error: any) { results.checks.push({ name: 'HTTP访问', status: 'error', message: 'HTTP访问失败', error: error.message || String(error), details: { isTimeout: error.name === 'AbortError', isNetworkError: error.message?.includes('fetch'), } }); } } return NextResponse.json(results); } catch (error) { console.error('[Diagnose Preview Image] Error:', error); return NextResponse.json( { error: '诊断失败', message: error instanceof Error ? error.message : String(error) }, { status: 500 } ); } }