import { NextRequest, NextResponse } from 'next/server' import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' const s3Client = new S3Client({ region: 'ru-1', endpoint: 'https://s3.twcstorage.ru', credentials: { accessKeyId: 'I6XD2OR7YO2ZN6L6Z629', secretAccessKey: '9xCOoafisG0aB9lJNvdLO1UuK73fBvMcpHMdijrJ' }, forcePathStyle: true }) const BUCKET_NAME = '617774af-sfera' // Разрешенные типы изображений const ALLOWED_IMAGE_TYPES = [ 'image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif' ] export async function POST(request: NextRequest) { try { const formData = await request.formData() const file = formData.get('file') as File const userId = formData.get('userId') as string const type = formData.get('type') as string // 'service' или 'supply' if (!file || !userId || !type) { return NextResponse.json( { error: 'File, userId and type are required' }, { status: 400 } ) } // Проверяем тип (services или supplies) if (!['service', 'supply'].includes(type)) { return NextResponse.json( { error: 'Type must be either "service" or "supply"' }, { status: 400 } ) } // Проверяем, что файл не пустой if (file.size === 0) { return NextResponse.json( { error: 'File is empty' }, { status: 400 } ) } // Проверяем имя файла if (!file.name || file.name.trim().length === 0) { return NextResponse.json( { error: 'Invalid file name' }, { status: 400 } ) } // Проверяем тип файла if (!ALLOWED_IMAGE_TYPES.includes(file.type)) { return NextResponse.json( { error: `File type ${file.type} is not allowed. Only images are supported.` }, { status: 400 } ) } // Ограничиваем размер файла (5MB для изображений) if (file.size > 5 * 1024 * 1024) { return NextResponse.json( { error: 'File size must be less than 5MB' }, { status: 400 } ) } // Генерируем уникальное имя файла const timestamp = Date.now() // Более безопасная очистка имени файла const safeFileName = file.name .replace(/[^\w\s.-]/g, '_') // Заменяем недопустимые символы .replace(/\s+/g, '_') // Заменяем пробелы на подчеркивания .replace(/_{2,}/g, '_') // Убираем множественные подчеркивания .toLowerCase() // Приводим к нижнему регистру const folder = type === 'service' ? 'services' : 'supplies' const key = `${folder}/${userId}/${timestamp}-${safeFileName}` // Конвертируем файл в Buffer const buffer = Buffer.from(await file.arrayBuffer()) // Очищаем метаданные от недопустимых символов const cleanOriginalName = file.name.replace(/[^\w\s.-]/g, '_') const cleanUserId = userId.replace(/[^\w-]/g, '') const cleanType = type.replace(/[^\w]/g, '') // Загружаем в S3 const command = new PutObjectCommand({ Bucket: BUCKET_NAME, Key: key, Body: buffer, ContentType: file.type, ACL: 'public-read', Metadata: { originalname: cleanOriginalName, uploadedby: cleanUserId, type: cleanType } }) await s3Client.send(command) // Возвращаем URL файла и метаданные const url = `https://s3.twcstorage.ru/${BUCKET_NAME}/${key}` return NextResponse.json({ success: true, url, key, originalName: file.name, size: file.size, type: file.type }) } catch (error) { console.error('Error uploading service image:', error) return NextResponse.json( { error: 'Failed to upload image' }, { status: 500 } ) } } export async function DELETE(request: NextRequest) { try { const { key } = await request.json() if (!key) { return NextResponse.json( { error: 'Key is required' }, { status: 400 } ) } // TODO: Добавить удаление из S3 // const command = new DeleteObjectCommand({ // Bucket: BUCKET_NAME, // Key: key // }) // await s3Client.send(command) return NextResponse.json({ success: true }) } catch (error) { console.error('Error deleting service image:', error) return NextResponse.json( { error: 'Failed to delete image' }, { status: 500 } ) } }