357 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Request, Response } from 'express';
import axios from "axios";
import path from "path";
import { prisma } from '../../../lib/prisma';
import { AuthRequest } from '../../../middleware/auth';
import { checkPermission } from '../../../utils/permissions';
import { logger } from '../../../config/logger';
import { Article } from "../../../types";
import { uploadBufferToS3 } from "../../../services/s3Service";
const DEFAULT_COVER_IMAGE = '/images/cover-placeholder.webp';
export async function getArticle(req: Request, res: Response) : Promise<void> {
try {
const article = await prisma.article.findUnique({
where: { id: req.params.id },
include: {
author: {
select: {
id: true,
displayName: true,
avatarUrl: true,
email: true
}
},
gallery: {
orderBy: {order: "asc"}, // Сортировка изображений по порядку
select: {
id: true,
url: true,
caption: true,
alt: true,
width: true,
height: true,
size: true,
format: true,
order: true, // Добавляем порядок сортировки
},
}
}
});
if (!article) {
logger.warn(`Статья не найдена: ${req.params.id}`);
res.status(404).json({ error: 'Статья не найдена' });
return
}
res.json(article);
} catch (error) {
logger.error('Error fetching article:', error);
res.status(500).json({ error: 'Server error' });
}
}
export async function createArticle(req: AuthRequest, res: Response) : Promise<void> {
try {
const { title, excerpt, content, categoryId, cityId, coverImage, readTime } = req.body;
if (!req.user) {
logger.warn('Пользователь не вошел в систему');
res.status(401).json({ error: 'Пользователь не аутентифицирован' });
return
}
if (!categoryId || categoryId < 1 || categoryId > 8) {
logger.warn(`Недопустимый ID категории: ${categoryId}`);
res.status(400).json({ error: 'Недопустимая категория' });
return
}
if (!checkPermission(req.user, categoryId, 'create')) {
logger.warn(`Не разрешено для пользователя ${req.user.id} создавать статьи в категории ${categoryId}`);
res.status(403).json({ error: 'В разрешении отказано' });
return
}
const article = await prisma.article.create({
data: {
title,
excerpt,
content,
categoryId,
cityId,
coverImage,
readTime,
authorId: req.user.id
},
include: {
author: {
select: {
id: true,
displayName: true,
email: true
}
}
}
});
logger.info(`Создана статья: ${article.id} пользователем ${req.user.id}`);
res.status(201).json(article);
} catch (error) {
logger.error('Ошибка создания статьи:', error);
res.status(500).json({ error: 'Серверная ошибка' });
}
}
export async function updateArticle(req: AuthRequest, res: Response) : Promise<void> {
try {
const { title, excerpt, content, categoryId, cityId, coverImage, readTime } = req.body;
if (!req.user) {
res.status(401).json({ error: 'Пользователь не вошел в систему' });
return
}
const article = await prisma.article.findUnique({
where: { id: req.params.id }
});
if (!article) {
res.status(404).json({ error: 'Статья не найдена' });
return
}
if (!checkPermission(req.user, categoryId, 'edit')) {
res.status(403).json({ error: 'В разрешении отказано' });
return
}
const updatedArticle = await prisma.article.update({
where: { id: req.params.id },
data: {
title,
excerpt,
content,
categoryId: Number(categoryId),
cityId,
coverImage,
readTime
},
include: {
author: {
select: {
id: true,
displayName: true,
email: true
}
}
}
});
res.json(updatedArticle);
} catch (error) {
logger.error('Ошибка редактирования статьи:', error);
res.status(500).json({ error: 'Серверная ошибка' });
}
}
export async function activeArticle(req: AuthRequest, res: Response) : Promise<void> {
try {
const { isActive } = req.body;
if (!req.user) {
res.status(401).json({ error: 'Пользователь не вошел в систему' });
return
}
const article = await prisma.article.findUnique({
where: { id: req.params.id }
});
if (!article) {
res.status(404).json({ error: 'Статья не найдена' });
return
}
const updatedArticle = await prisma.article.update({
where: { id: req.params.id },
data: {
isActive: !isActive
},
include: {
author: {
select: {
id: true,
displayName: true,
email: true
}
}
}
});
res.json(updatedArticle);
} catch (error) {
logger.error('Ошибка активирования статьи:', error);
res.status(500).json({ error: 'Серверная ошибка' });
}
}
export async function reactArticle(req: AuthRequest, res: Response) : Promise<void> {
try {
const { reaction, likes, dislikes } = req.body;
let newLikes:number = Number(likes);
let newDisLikes:number = Number(dislikes);
if (reaction === 'like') newLikes++;
if (reaction === 'dislike') newDisLikes++;
const updatedArticle = await prisma.article.update({
where: { id: req.params.id },
data: {
likes: newLikes,
dislikes: newDisLikes
},
include: {
author: {
select: {
id: true,
displayName: true,
email: true
}
}
}
});
res.json(updatedArticle);
} catch (error) {
logger.error('Ошибка установки реакции на статью:', error);
res.status(500).json({ error: 'Серверная ошибка' });
}
}
export async function deleteArticle(req: AuthRequest, res: Response) : Promise<void> {
try {
if (!req.user) {
res.status(401).json({ error: 'Пользователь не вошел в систему' });
return
}
const article = await prisma.article.findUnique({
where: { id: req.params.id }
});
if (!article) {
res.status(404).json({ error: 'Статья не найдена' });
return
}
if (!checkPermission(req.user, article.categoryId.toString(), 'delete')) {
res.status(403).json({ error: 'В разрешении отказано' });
return
}
await prisma.article.delete({
where: { id: req.params.id }
});
res.json({ message: 'Статья успешно удалена' });
} catch (error) {
logger.error('Ошибка удаления статьи:', error);
res.status(500).json({ error: 'Серверная ошибка' });
}
}
export async function importArticles(req: AuthRequest, res: Response) : Promise<void> {
const articles: Article[] = req.body;
if (!req.user) {
res.status(401).json({ error: 'Пользователь не вошел в систему' });
return
}
if (!Array.isArray(articles) || articles.length === 0) {
res.status(400).json({ message: 'Ожидается непустой массив статей в теле запроса' });
return
}
let importedCount = 0;
try {
for (const article of articles) {
try {
// Шаг 1: Создание статьи
const newArticle = await prisma.article.create({
data: {
title: article.title,
excerpt: article.excerpt,
content: article.content,
categoryId: article.categoryId,
cityId: article.cityId,
coverImage: DEFAULT_COVER_IMAGE,
readTime: article.readTime,
publishedAt: new Date(article.publishedAt),
likes: article.likes || 0,
dislikes: article.dislikes || 0,
author: {
connect: { id: article.author.id },
},
},
});
// Шаг 2: Обработка coverImage
if (article.coverImage) {
const folder = `articles/${newArticle.id}`;
const imageUrl = article.coverImage;
try {
// Извлечение изображение в виде буфера массива
const imageResponse = await axios.get(imageUrl, { responseType: 'arraybuffer' });
// Создание буфера из данных ответа
const imageBuffer = Buffer.from(imageResponse.data, 'binary');
// Получить тип контента и расширение
const contentType = imageResponse.headers['content-type'];
const fileExtension = path.extname(new URL(imageUrl).pathname) ||
(contentType.includes('image/') ? `.${contentType.split('/')[1]}` : '');
// Генерация имени файла
const filename = `cover-${Date.now()}${fileExtension}`;
// Загрузка буфера в S3
const uploadedUrl = await uploadBufferToS3(
imageBuffer,
folder,
filename,
contentType,
'original',
80
);
} catch (imageError) {
console.error(`Ошибка загрузки coverImage для статьи ${article.id}:`, imageError);
}
}
importedCount++;
} catch (articleError) {
console.error(`Ошибка импорта статьи ${article.id}:`, articleError);
}
}
res.status(200).json({
message: `Импорт завершен. Успешно импортировано ${importedCount} из ${articles.length} статей.`,
});
} catch (err) {
console.error('Ошибка обработки запроса:', err);
res.status(500).json({ message: 'Ошибка сервера при импорте' });
} finally {
await prisma.$disconnect();
}
}