Работа с галереей и сохранением в S3.
This commit is contained in:
parent
ec10d76705
commit
584b2bc614
@ -336,7 +336,6 @@ export async function importArticles(req: AuthRequest, res: Response) : Promise<
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
importedCount++;
|
||||
} catch (articleError) {
|
||||
console.error(`Ошибка импорта статьи ${article.id}:`, articleError);
|
||||
|
@ -21,8 +21,8 @@ export async function createGalleryImage(req: AuthRequest, res: Response) {
|
||||
|
||||
res.status(201).json(image);
|
||||
} catch (error) {
|
||||
logger.error('Error creating gallery image:', error);
|
||||
res.status(500).json({ error: 'Failed to create gallery image' });
|
||||
logger.error('Ошибка создания изображения в галерее:', error);
|
||||
res.status(500).json({ error: 'Сбой создания изображения в галерее' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ export const handleFileUpload = async (req: Request, res: Response): Promise<voi
|
||||
const quality = Number(req.body.quality) || 80;
|
||||
|
||||
try {
|
||||
// Upload file to S3
|
||||
// Загрузка файлв в S3
|
||||
const fileUrl = await uploadFile(folder, file, resolutionId, quality);
|
||||
|
||||
res.json({ message: 'Файл успешно загружен', fileUrl });
|
||||
@ -28,7 +28,7 @@ export const handleFileUpload = async (req: Request, res: Response): Promise<voi
|
||||
}
|
||||
};
|
||||
|
||||
// Handle URL to image upload
|
||||
// Обработка загрузки файла в S3 по URL изображения
|
||||
export const handleUrlToImageUpload = async (req: Request, res: Response): Promise<void> => {
|
||||
const { imageUrl, folder, resolutionId, quality, filename } = req.body;
|
||||
|
||||
@ -38,11 +38,11 @@ export const handleUrlToImageUpload = async (req: Request, res: Response): Promi
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch the image
|
||||
// Получение изображения по URL в буфер
|
||||
const imageResponse = await axios.get(imageUrl, { responseType: 'arraybuffer' });
|
||||
const imageBuffer = Buffer.from(imageResponse.data, 'binary');
|
||||
|
||||
// Upload to S3
|
||||
// загрузка в S3
|
||||
const fileUrl = await uploadBufferToS3(
|
||||
imageBuffer,
|
||||
folder || 'images',
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import {User, UserPermissions} from '../types/auth';
|
||||
import { User, UserPermissions } from '../types/auth';
|
||||
import { logger } from '../config/logger';
|
||||
import {JsonValue} from "@prisma/client/runtime/library";
|
||||
import { JsonValue } from "@prisma/client/runtime/library";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export const authService = {
|
||||
login: async (email: string, password: string) => {
|
||||
try {
|
||||
logger.info(`Login attempt for user: ${email}`);
|
||||
logger.info(`Попытка входа пользователя: ${email}`);
|
||||
|
||||
const user : {
|
||||
email: string;
|
||||
@ -32,14 +32,14 @@ export const authService = {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
logger.warn(`Login failed: User not found - ${email}`);
|
||||
throw new Error('Invalid credentials');
|
||||
logger.warn(`Сбой входа: Пользователь не найден - ${email}`);
|
||||
throw new Error('Недопустивые данные пользователя');
|
||||
}
|
||||
|
||||
const isValidPassword = await bcrypt.compare(password, user.password);
|
||||
if (!isValidPassword) {
|
||||
logger.warn(`Login failed: Invalid password for user - ${email}`);
|
||||
throw new Error('Invalid credentials');
|
||||
logger.warn(`Сбой входа: Неправильный пароль пользователя - ${email}`);
|
||||
throw new Error('Недопустивые данные пользователя');
|
||||
}
|
||||
|
||||
const token = await authService.generateToken(user.id);
|
||||
@ -47,14 +47,14 @@ export const authService = {
|
||||
|
||||
const permissions = user.permissions as unknown;
|
||||
|
||||
logger.info(`User logged in successfully: ${email}`);
|
||||
logger.info(`Пользователь успешно зашел: ${email}`);
|
||||
return {
|
||||
user: {...userWithoutPassword,
|
||||
permissions: permissions as UserPermissions } as User,
|
||||
token
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Login error:', error);
|
||||
logger.error('Ошибка входа:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -66,10 +66,10 @@ export const authService = {
|
||||
process.env.JWT_SECRET || '',
|
||||
{ expiresIn: '24h' }
|
||||
);
|
||||
logger.debug(`Generated token for user: ${userId}`);
|
||||
logger.debug(`Создан токен для пользователя: ${userId}`);
|
||||
return token;
|
||||
} catch (error) {
|
||||
logger.error('Token generation error:', error);
|
||||
logger.error('Ошибка создания токена:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -82,7 +82,7 @@ export const authService = {
|
||||
permissions: any;
|
||||
}) => {
|
||||
try {
|
||||
logger.info(`Creating new user: ${userData.email}`);
|
||||
logger.info(`Создание нового пользователя: ${userData.email}`);
|
||||
|
||||
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
||||
const user : {
|
||||
@ -105,14 +105,14 @@ export const authService = {
|
||||
}
|
||||
});
|
||||
|
||||
logger.info(`User created successfully: ${userData.email}`);
|
||||
logger.info(`Пользователь успешно создан: ${userData.email}`);
|
||||
|
||||
const permissions = user.permissions as unknown;
|
||||
|
||||
return {...user,
|
||||
permissions: permissions as UserPermissions } as User
|
||||
} catch (error) {
|
||||
logger.error('User creation error:', error);
|
||||
logger.error('Ошибка создания пользователя:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import {PrismaClient} from '@prisma/client';
|
||||
import {logger} from '../config/logger';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { logger } from '../config/logger';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
|
||||
export const galleryService = {
|
||||
createImage: async (data: {
|
||||
url: string;
|
||||
@ -16,13 +17,25 @@ export const galleryService = {
|
||||
order?: number;
|
||||
}) => {
|
||||
try {
|
||||
const image = await prisma.galleryImage.create({
|
||||
data
|
||||
// Находим максимальное значение order для данного articleId
|
||||
const maxOrder = await prisma.galleryImage.aggregate({
|
||||
where: { articleId: data.articleId },
|
||||
_max: { order: true },
|
||||
});
|
||||
logger.info(`Created gallery image: ${image.id}`);
|
||||
|
||||
// Устанавливаем order как максимальное значение + 1, или 0 если записей еще нет
|
||||
const newOrder = maxOrder._max.order !== null ? maxOrder._max.order + 1 : 0;
|
||||
|
||||
const image = await prisma.galleryImage.create({
|
||||
data: {
|
||||
...data,
|
||||
order: newOrder
|
||||
}
|
||||
});
|
||||
logger.info(`Создано изображение в галерее: ${image.id}`);
|
||||
return image;
|
||||
} catch (error) {
|
||||
logger.error('Error creating gallery image:', error);
|
||||
logger.error('Сбой создания изображения в галерее:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -40,10 +53,10 @@ export const galleryService = {
|
||||
where: { id },
|
||||
data
|
||||
});
|
||||
logger.info(`Updated gallery image: ${id}`);
|
||||
logger.info(`Изменено изображение в галерее: ${id}`);
|
||||
return image;
|
||||
} catch (error) {
|
||||
logger.error(`Error updating gallery image ${id}:`, error);
|
||||
logger.error(`Сбой изменения изображения в галерее: ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -53,9 +66,9 @@ export const galleryService = {
|
||||
await prisma.galleryImage.delete({
|
||||
where: { id }
|
||||
});
|
||||
logger.info(`Deleted gallery image: ${id}`);
|
||||
logger.info(`Удалено изображение из галереи: ${id}`);
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting gallery image ${id}:`, error);
|
||||
logger.error(`Сбой удаления изображения из галереи ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -70,9 +83,9 @@ export const galleryService = {
|
||||
})
|
||||
)
|
||||
);
|
||||
logger.info(`Reordered gallery images for article: ${articleId}`);
|
||||
logger.info(`Изменен порядок расположения изображений в галерее для статьи: ${articleId}`);
|
||||
} catch (error) {
|
||||
logger.error(`Error reordering gallery images for article ${articleId}:`, error);
|
||||
logger.error(`Сбой изменения порядка расположения изображений в галерее для статьи ${articleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@ -84,7 +97,7 @@ export const galleryService = {
|
||||
orderBy: {order: 'asc'}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`Error fetching gallery for article ${articleId}:`, error);
|
||||
logger.error(`Ошибка получения изображений галереи для статьи: ${articleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export const s3Client = new S3Client({
|
||||
},
|
||||
});
|
||||
|
||||
// Export bucket name
|
||||
// Наименование бакета
|
||||
export const bucketName = BUCKET_NAME;
|
||||
|
||||
// Функция для изменения размеров и оптимизации изображения
|
||||
@ -52,7 +52,7 @@ const getMulterS3Config = (
|
||||
}).single('file'); // Обрабатываем один файл
|
||||
};
|
||||
|
||||
// Default multer upload middleware using the default S3 client
|
||||
// Middleware по умолчанию, использующее дефолтовый S3 клиент
|
||||
export const multerUpload = getMulterS3Config(s3Client, bucketName);
|
||||
|
||||
// Функция для загрузки файла в S3 после оптимизации
|
||||
@ -102,14 +102,14 @@ export const uploadToS3 = async (
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to upload a buffer to S3
|
||||
* @param buffer - The buffer to upload
|
||||
* @param folder - The folder path in S3
|
||||
* @param filename - Optional filename (if not provided, a UUID will be generated)
|
||||
* @param mimetype - The MIME type of the buffer content
|
||||
* @param resolutionId - Optional resolution identifier
|
||||
* @param quality - Optional quality setting for image optimization
|
||||
* @returns URL of the uploaded file
|
||||
* Функция для загрузки буфера в S3
|
||||
* @param buffer - Буфер для загрузки
|
||||
* @param folder - Путь к папке в S3
|
||||
* @param filename - Опциональное имя файла (если пусто, то будет сгенерирован UUID как имя)
|
||||
* @param mimetype - MIME тип контента буфера
|
||||
* @param resolutionId - Опциональный идентификатор разрешения изображения
|
||||
* @param quality - Отцтональное значение качества при оптимизации
|
||||
* @returns URL загрущенного файла
|
||||
*/
|
||||
export const uploadBufferToS3 = async (
|
||||
buffer: Buffer,
|
||||
@ -120,26 +120,36 @@ export const uploadBufferToS3 = async (
|
||||
quality: number = 80
|
||||
): Promise<string> => {
|
||||
try {
|
||||
// Generate filename if not provided
|
||||
// Генерация имени файла, если оно не задано
|
||||
const finalFilename = filename || `${uuidv4()}${mimetype ? getExtensionFromMimeType(mimetype) : '.bin'}`;
|
||||
|
||||
// Create the S3 key (path)
|
||||
// Создание ключа S3 (путь в бакете)
|
||||
const key = folder ? `${folder}/${finalFilename}` : finalFilename;
|
||||
|
||||
// Here you could add image optimization for the buffer if needed
|
||||
// For example, using Sharp library to resize/compress the image
|
||||
const selectedResolution = imageResolutions.find(r => r.id === resolutionId);
|
||||
if (!selectedResolution) {
|
||||
throw new Error('Недопустимое разрешение изображения');
|
||||
}
|
||||
|
||||
// Upload buffer to S3
|
||||
// Оптимизируем изображение
|
||||
const optimizedBuffer = await resizeAndOptimizeImage(
|
||||
buffer,
|
||||
selectedResolution.width,
|
||||
selectedResolution.height,
|
||||
quality
|
||||
);
|
||||
|
||||
// Загрузка буфера в S3
|
||||
await s3Client.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: key,
|
||||
Body: buffer,
|
||||
Body: optimizedBuffer,
|
||||
ContentType: mimetype,
|
||||
})
|
||||
);
|
||||
|
||||
// Return the file URL
|
||||
// Возвращаем URL файла
|
||||
const fileUrl = `https://${bucketName}.s3.regru.cloud/${finalFilename}`;
|
||||
logger.info(`Buffer successfully uploaded to S3: ${key}`);
|
||||
return fileUrl;
|
||||
@ -150,7 +160,7 @@ export const uploadBufferToS3 = async (
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to get file extension from MIME type
|
||||
// Функция получения пасширения файла из MIME типа
|
||||
function getExtensionFromMimeType(mimetype: string): string {
|
||||
const mimeToExt: Record<string, string> = {
|
||||
'image/jpeg': '.jpg',
|
||||
@ -167,7 +177,7 @@ function getExtensionFromMimeType(mimetype: string): string {
|
||||
return mimeToExt[mimetype] || '';
|
||||
}
|
||||
|
||||
// Simplified version of uploadToS3 that uses the default client and bucket
|
||||
// Упрощенная версия uploadToS3 которая использует клиента и бакет по умолчанию
|
||||
export const uploadFile = async (
|
||||
folder: string,
|
||||
file: Express.MulterS3.File,
|
||||
|
Loading…
x
Reference in New Issue
Block a user