Добавлены новые зависимости для работы с эмодзи и улучшена структура базы данных. Реализована модель сообщений и обновлены компоненты для поддержки новых функций мессенджера. Обновлены запросы и мутации для работы с сообщениями и чатом.
This commit is contained in:
167
src/app/api/upload-file/route.ts
Normal file
167
src/app/api/upload-file/route.ts
Normal file
@ -0,0 +1,167 @@
|
||||
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'
|
||||
]
|
||||
|
||||
const ALLOWED_FILE_TYPES = [
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'text/plain',
|
||||
'application/zip',
|
||||
'application/x-zip-compressed',
|
||||
'application/json'
|
||||
]
|
||||
|
||||
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 messageType = formData.get('messageType') as string // 'IMAGE' или 'FILE'
|
||||
|
||||
if (!file || !userId || !messageType) {
|
||||
return NextResponse.json(
|
||||
{ error: 'File, userId and messageType are required' },
|
||||
{ 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 }
|
||||
)
|
||||
}
|
||||
|
||||
// Проверяем тип файла в зависимости от типа сообщения
|
||||
const isImage = messageType === 'IMAGE'
|
||||
const allowedTypes = isImage ? ALLOWED_IMAGE_TYPES : [...ALLOWED_IMAGE_TYPES, ...ALLOWED_FILE_TYPES]
|
||||
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
return NextResponse.json(
|
||||
{ error: `File type ${file.type} is not allowed` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Ограничиваем размер файла
|
||||
const maxSize = isImage ? 10 * 1024 * 1024 : 50 * 1024 * 1024 // 10MB для изображений, 50MB для файлов
|
||||
if (file.size > maxSize) {
|
||||
const maxSizeMB = maxSize / (1024 * 1024)
|
||||
return NextResponse.json(
|
||||
{ error: `File size must be less than ${maxSizeMB}MB` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Генерируем уникальное имя файла
|
||||
const timestamp = Date.now()
|
||||
// Более безопасная очистка имени файла
|
||||
const safeFileName = file.name
|
||||
.replace(/[^\w\s.-]/g, '_') // Заменяем недопустимые символы
|
||||
.replace(/\s+/g, '_') // Заменяем пробелы на подчеркивания
|
||||
.replace(/_{2,}/g, '_') // Убираем множественные подчеркивания
|
||||
.toLowerCase() // Приводим к нижнему регистру
|
||||
|
||||
const folder = isImage ? 'images' : 'files'
|
||||
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 cleanMessageType = messageType.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,
|
||||
messagetype: cleanMessageType
|
||||
}
|
||||
})
|
||||
|
||||
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,
|
||||
messageType
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error uploading file:', error)
|
||||
|
||||
// Логируем детали ошибки
|
||||
if (error instanceof Error) {
|
||||
console.error('Error message:', error.message)
|
||||
console.error('Error stack:', error.stack)
|
||||
}
|
||||
|
||||
let errorMessage = 'Failed to upload file'
|
||||
if (error instanceof Error) {
|
||||
// Проверяем специфичные ошибки
|
||||
if (error.message.includes('Invalid character in header')) {
|
||||
errorMessage = 'Invalid characters in file name or metadata'
|
||||
} else if (error.message.includes('AccessDenied')) {
|
||||
errorMessage = 'Access denied to storage'
|
||||
} else if (error.message.includes('NoSuchBucket')) {
|
||||
errorMessage = 'Storage bucket not found'
|
||||
} else {
|
||||
errorMessage = error.message
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: errorMessage, success: false },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
82
src/app/api/upload-voice/route.ts
Normal file
82
src/app/api/upload-voice/route.ts
Normal file
@ -0,0 +1,82 @@
|
||||
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'
|
||||
|
||||
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
|
||||
|
||||
if (!file || !userId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'File and userId are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Проверяем тип файла (поддерживаем аудио форматы)
|
||||
if (!file.type.startsWith('audio/')) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Only audio files are allowed' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Ограничиваем размер файла (10MB для голосовых сообщений)
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
return NextResponse.json(
|
||||
{ error: 'File size must be less than 10MB' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Генерируем уникальное имя файла
|
||||
const timestamp = Date.now()
|
||||
const extension = file.name.split('.').pop() || 'wav'
|
||||
const key = `voice-messages/${userId}/${timestamp}.${extension}`
|
||||
|
||||
// Конвертируем файл в Buffer
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
|
||||
// Загружаем в S3
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: BUCKET_NAME,
|
||||
Key: key,
|
||||
Body: buffer,
|
||||
ContentType: file.type,
|
||||
ACL: 'public-read'
|
||||
})
|
||||
|
||||
await s3Client.send(command)
|
||||
|
||||
// Возвращаем URL файла и метаданные
|
||||
const url = `https://s3.twcstorage.ru/${BUCKET_NAME}/${key}`
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
url,
|
||||
key,
|
||||
duration: 0, // Длительность будет вычислена на фронтенде
|
||||
size: file.size
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error uploading voice message:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to upload voice message' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user