export async function GET(request: NextRequest) {
const corsResponse = handleCors(request)
if (corsResponse) return corsResponse
const blocked = await protectRoute(request)
if (blocked) return blocked
const auth = await requireAuth(request)
if ('error' in auth) return auth.error
try {
const avatarPath = await getUserAvatarPath(auth.userId)
if (!avatarPath) {
return errorResponse('Avatar not found', 404, request.headers.get('origin'))
}
if (avatarPath.startsWith('http://') || avatarPath.startsWith('https://')) {
return Response.redirect(avatarPath, 302)
}
const fileBuffer = await fs.readFile(avatarPath)
const ext = path.extname(avatarPath).toLowerCase()
const contentType =
ext === '.jpg' || ext === '.jpeg' ? 'image/jpeg' :
ext === '.png' ? 'image/png' :
ext === '.webp' ? 'image/webp' :
ext === '.gif' ? 'image/gif' :
'image/jpeg'
return new Response(fileBuffer, {
headers: {
...corsHeaders(request.headers.get('origin')),
'Content-Type': contentType,
'Cache-Control': 'public, max-age=3600',
},
})
} catch (error) {
logger.error('Get avatar error:', error)
return errorResponse('Internal server error', 500, request.headers.get('origin'))
}
}
export async function POST(request: NextRequest) {
const corsResponse = handleCors(request)
if (corsResponse) return corsResponse
const blocked = await protectRoute(request)
if (blocked) return blocked
const auth = await requireAuth(request)
if ('error' in auth) return auth.error
try {
const formData = await request.formData()
const file = formData.get('avatar') as File | null
if (!file) {
return errorResponse('No file provided', 400, request.headers.get('origin'))
}
if (file.size > MAX_FILE_SIZE) {
return errorResponse('File too large. Maximum size is 2MB', 400, request.headers.get('origin'))
}
if (!ALLOWED_MIME_TYPES.includes(file.type)) {
return errorResponse(
'Invalid file type. Allowed types: JPEG, PNG, WebP, GIF',
400,
request.headers.get('origin')
)
}
await deleteOldAvatar(auth.userId)
const userDir = await ensureUserAvatarDir(auth.userId)
const ext = file.type === 'image/jpeg' ? '.jpg' :
file.type === 'image/png' ? '.png' :
file.type === 'image/webp' ? '.webp' :
file.type === 'image/gif' ? '.gif' :
'.jpg'
const filename = `avatar_${Date.now()}${ext}`
const filePath = path.join(userDir, filename)
const arrayBuffer = await file.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
await fs.writeFile(filePath, buffer)
const relativePath = `${auth.userId}/${filename}`
await prisma.user.update({
where: { id: auth.userId },
data: { avatar: relativePath },
})
await createAuditLog(auth.userId, 'USER_UPDATE', {
fields: ['avatar'],
})
return jsonResponse(
{
avatar: relativePath,
message: 'Avatar uploaded successfully',
},
200,
request.headers.get('origin')
)
} catch (error: any) {
logger.error('Upload avatar error:', error)
return errorResponse('Internal server error', 500, request.headers.get('origin'))
}
}