diff --git a/backend/routes/posts.js b/backend/routes/posts.js index 46dec41..66857f7 100644 --- a/backend/routes/posts.js +++ b/backend/routes/posts.js @@ -95,6 +95,34 @@ router.get('/', authenticate, async (req, res) => { } }); +// Получить один пост по ID +router.get('/:id', authenticate, async (req, res) => { + try { + const post = await Post.findById(req.params.id) + .populate('author', 'username firstName lastName photoUrl') + .populate('mentionedUsers', 'username firstName lastName') + .populate('comments.author', 'username firstName lastName photoUrl') + .exec(); + + if (!post) { + return res.status(404).json({ error: 'Пост не найден' }); + } + + // Проверить whitelist настройки пользователя + if (req.user.settings.whitelist.noNSFW && post.isNSFW) { + return res.status(403).json({ error: 'Пост скрыт настройками' }); + } + if (req.user.settings.whitelist.noHomo && post.isHomo) { + return res.status(403).json({ error: 'Пост скрыт настройками' }); + } + + res.json({ post }); + } catch (error) { + console.error('Ошибка получения поста:', error); + res.status(500).json({ error: 'Ошибка сервера' }); + } +}); + // Создать пост router.post('/', authenticate, strictPostLimiter, postCreationLimiter, fileUploadLimiter, uploadPostImages, async (req, res) => { try { diff --git a/backend/server.js b/backend/server.js index 065f177..c6c2f7b 100644 --- a/backend/server.js +++ b/backend/server.js @@ -7,7 +7,9 @@ const path = require('path'); const http = require('http'); // Загрузить переменные окружения ДО импорта config -dotenv.config({ path: path.join(__dirname, '.env') }); +// Загружаем из корня проекта (как в moderation/backend) +const rootEnvPath = path.resolve(__dirname, '../.env'); +dotenv.config({ path: rootEnvPath }); const { generalLimiter } = require('./middleware/rateLimiter'); const { initRedis } = require('./utils/redis'); diff --git a/backend/utils/telegram.js b/backend/utils/telegram.js index 12c4f88..d402fe0 100644 --- a/backend/utils/telegram.js +++ b/backend/utils/telegram.js @@ -43,20 +43,36 @@ function manualValidateInitData(initDataRaw, botToken) { } function validateAndParseInitData(initDataRaw, botToken = null) { + // Для основного приложения используем TELEGRAM_BOT_TOKEN + // Для модерации передается явно moderationBotToken const tokenToUse = botToken || config.telegramBotToken; + // Проверка что токен не пустая строка + const hasValidToken = tokenToUse && typeof tokenToUse === 'string' && tokenToUse.trim().length > 0; + + if (!hasValidToken) { + const errorMsg = botToken + ? 'Bot token модерации не настроен (MODERATION_BOT_TOKEN)' + : 'Bot token не настроен (TELEGRAM_BOT_TOKEN)'; + console.error('[Telegram] Token error:', { + botTokenProvided: !!botToken, + telegramBotTokenFromConfig: !!config.telegramBotToken, + telegramBotTokenValue: config.telegramBotToken ? `${config.telegramBotToken.substring(0, 10)}...` : 'undefined', + tokenToUseValue: tokenToUse ? `${tokenToUse.substring(0, 10)}...` : 'undefined', + errorMsg + }); + throw new Error(errorMsg); + } + console.log('[Telegram] validateAndParseInitData called:', { hasInitData: !!initDataRaw, type: typeof initDataRaw, length: initDataRaw?.length || 0, preview: initDataRaw?.substring(0, 100) + '...', - usingModerationToken: !!botToken + usingModerationToken: !!botToken, + hasTelegramBotToken: !!config.telegramBotToken }); - if (!tokenToUse) { - throw new Error('Bot token не настроен'); - } - if (!initDataRaw || typeof initDataRaw !== 'string') { throw new Error('initData не передан'); } diff --git a/frontend/src/pages/Feed.jsx b/frontend/src/pages/Feed.jsx index ee822de..235dbca 100644 --- a/frontend/src/pages/Feed.jsx +++ b/frontend/src/pages/Feed.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { useSearchParams, useNavigate } from 'react-router-dom' -import { getPosts } from '../utils/api' +import { getPosts, getPost } from '../utils/api' import PostCard from '../components/PostCard' import CreatePostModal from '../components/CreatePostModal' import { Plus, Settings } from 'lucide-react' @@ -33,18 +33,55 @@ export default function Feed({ user }) { const loadSpecificPost = async (postId) => { try { - // Загрузить посты и найти нужный - const data = await getPosts({}) - const foundPost = data.posts.find(p => p._id === postId) + // Сначала проверить, есть ли пост уже в загруженных + const existingPost = posts.find(p => p._id === postId) - if (foundPost) { - // Если пост найден, добавить его в начало списка + if (existingPost) { + // Если пост уже загружен, просто прокрутить к нему + setTimeout(() => { + const element = document.getElementById(`post-${postId}`) + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }) + } + }, 100) + return + } + + // Попытаться загрузить конкретный пост по ID + try { + const foundPost = await getPost(postId) + + if (foundPost) { + // Если пост найден, добавить его в начало списка + setPosts(prev => { + const filtered = prev.filter(p => p._id !== postId) + return [foundPost, ...filtered] + }) + + // Прокрутить к посту после загрузки + setTimeout(() => { + const element = document.getElementById(`post-${postId}`) + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }) + } + }, 100) + return + } + } catch (postError) { + console.error('Ошибка загрузки конкретного поста:', postError) + // Если не удалось загрузить конкретный пост, попробуем найти в ленте + } + + // Если пост не найден напрямую, загрузить ленту и попытаться найти там + const data = await getPosts({ filter, page: 1 }) + const foundInFeed = data.posts.find(p => p._id === postId) + + if (foundInFeed) { setPosts(prev => { const filtered = prev.filter(p => p._id !== postId) - return [foundPost, ...filtered] + return [foundInFeed, ...filtered] }) - // Прокрутить к посту после загрузки setTimeout(() => { const element = document.getElementById(`post-${postId}`) if (element) { diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 757234b..2df651e 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -117,6 +117,11 @@ export const getPosts = async (params = {}) => { return response.data } +export const getPost = async (postId) => { + const response = await api.get(`/posts/${postId}`) + return response.data.post +} + export const createPost = async (formData) => { const response = await api.post('/posts', formData, { headers: {