87 lines
3.1 KiB
TypeScript
87 lines
3.1 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import { getServerSession } from 'next-auth';
|
||
import { authOptions } from '@/lib/auth';
|
||
import { prisma } from '@/lib/prisma';
|
||
import { uploadToS3 } from '@/lib/s3';
|
||
import { normalizeUrl } from '@/lib/utils';
|
||
import { compressImage, sanitizeFileName } from '@/lib/image-processor';
|
||
import path from 'path';
|
||
|
||
// POST /api/user/avatar - 上传用户头像
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const session = await getServerSession(authOptions);
|
||
const userId = session?.user ? (session.user as any).id : undefined;
|
||
|
||
console.log('[UserAvatar] Session:', { userId, hasSession: !!session, hasUser: !!session?.user });
|
||
|
||
if (!userId) {
|
||
console.log('[UserAvatar] No user ID in session');
|
||
return NextResponse.json({ error: '未登录' }, { status: 401 });
|
||
}
|
||
|
||
const formData = await request.formData();
|
||
const avatar = formData.get('avatar') as File | null;
|
||
|
||
if (!avatar) {
|
||
console.log('[UserAvatar] No avatar file provided');
|
||
return NextResponse.json({ error: '请选择头像文件' }, { status: 400 });
|
||
}
|
||
|
||
console.log('[UserAvatar] File info:', { name: avatar.name, type: avatar.type, size: avatar.size });
|
||
|
||
// 验证文件类型
|
||
if (!['image/jpeg', 'image/jpg', 'image/png', 'image/webp'].includes(avatar.type)) {
|
||
return NextResponse.json(
|
||
{ error: '不支持的图片格式,请使用 JPG、PNG 或 WebP 格式' },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// 验证文件大小(5MB)
|
||
if (avatar.size > 5 * 1024 * 1024) {
|
||
return NextResponse.json(
|
||
{ error: `头像过大,请使用小于5MB的图片(当前:${(avatar.size / 1024 / 1024).toFixed(2)}MB)` },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// 压缩图片
|
||
const imageBuffer = Buffer.from(await avatar.arrayBuffer());
|
||
const compressedBuffer = await compressImage(imageBuffer, {
|
||
maxSizeKB: 100,
|
||
maxWidth: 200,
|
||
maxHeight: 200,
|
||
format: 'webp',
|
||
});
|
||
|
||
// 上传到S3
|
||
const sanitizedName = sanitizeFileName(avatar.name);
|
||
const ext = path.extname(sanitizedName) || '.webp';
|
||
const fileName = `avatar-${userId}-${Date.now()}${ext}`;
|
||
|
||
console.log('[UserAvatar] Uploading to S3:', fileName, `(${(compressedBuffer.length / 1024).toFixed(2)}KB)`);
|
||
|
||
const avatarUrl = await uploadToS3(compressedBuffer, fileName, 'image/webp');
|
||
|
||
console.log('[UserAvatar] S3 URL:', avatarUrl);
|
||
|
||
// 更新用户头像
|
||
console.log('[UserAvatar] Updating user:', userId);
|
||
const updatedUser = await prisma.user.update({
|
||
where: { id: userId },
|
||
data: { avatar: avatarUrl },
|
||
});
|
||
|
||
console.log('[UserAvatar] User updated:', { id: updatedUser.id, avatar: updatedUser.avatar });
|
||
|
||
// 规范化URL
|
||
const normalizedUrl = normalizeUrl(avatarUrl);
|
||
|
||
return NextResponse.json({ avatarUrl: normalizedUrl });
|
||
} catch (error) {
|
||
console.error('[UserAvatar] POST error:', error);
|
||
return NextResponse.json({ error: '上传头像失败' }, { status: 500 });
|
||
}
|
||
}
|