Update files
This commit is contained in:
parent
4d4601ecb5
commit
d90f6088d2
|
|
@ -31,18 +31,57 @@ const authenticate = async (req, res, next) => {
|
|||
const initData = req.headers['x-telegram-init-data'];
|
||||
|
||||
if (!initData) {
|
||||
console.warn('⚠️ Нет x-telegram-init-data заголовка');
|
||||
return res.status(401).json({ error: 'Не авторизован' });
|
||||
}
|
||||
|
||||
// Получаем user из initData
|
||||
const urlParams = new URLSearchParams(initData);
|
||||
let urlParams;
|
||||
try {
|
||||
urlParams = new URLSearchParams(initData);
|
||||
} catch (e) {
|
||||
// Если initData не URLSearchParams, попробуем как JSON
|
||||
try {
|
||||
const parsed = JSON.parse(initData);
|
||||
if (parsed.user) {
|
||||
req.telegramUser = parsed.user;
|
||||
// Найти или создать пользователя
|
||||
let user = await User.findOne({ telegramId: parsed.user.id.toString() });
|
||||
if (!user) {
|
||||
user = new User({
|
||||
telegramId: parsed.user.id.toString(),
|
||||
username: parsed.user.username || parsed.user.first_name,
|
||||
firstName: parsed.user.first_name,
|
||||
lastName: parsed.user.last_name,
|
||||
photoUrl: parsed.user.photo_url
|
||||
});
|
||||
await user.save();
|
||||
console.log(`✅ Создан новый пользователь: ${user.username}`);
|
||||
}
|
||||
req.user = user;
|
||||
return next();
|
||||
}
|
||||
} catch (e2) {
|
||||
console.error('❌ Ошибка парсинга initData:', e2.message);
|
||||
return res.status(401).json({ error: 'Неверный формат данных авторизации' });
|
||||
}
|
||||
}
|
||||
|
||||
const userParam = urlParams.get('user');
|
||||
|
||||
if (!userParam) {
|
||||
console.warn('⚠️ Нет user параметра в initData');
|
||||
return res.status(401).json({ error: 'Данные пользователя не найдены' });
|
||||
}
|
||||
|
||||
const telegramUser = JSON.parse(userParam);
|
||||
let telegramUser;
|
||||
try {
|
||||
telegramUser = JSON.parse(userParam);
|
||||
} catch (e) {
|
||||
console.error('❌ Ошибка парсинга user:', e.message);
|
||||
return res.status(401).json({ error: 'Ошибка парсинга данных пользователя' });
|
||||
}
|
||||
|
||||
req.telegramUser = telegramUser;
|
||||
|
||||
// Проверка подписи Telegram (только в production и если есть токен)
|
||||
|
|
|
|||
|
|
@ -118,23 +118,40 @@ router.get('/anime', authenticate, async (req, res) => {
|
|||
tags: query,
|
||||
limit,
|
||||
pid: page
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
},
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
const posts = (response.data.post || []).map(post => ({
|
||||
// Обработка разных форматов ответа Gelbooru
|
||||
let postsData = [];
|
||||
if (Array.isArray(response.data)) {
|
||||
postsData = response.data;
|
||||
} else if (response.data && response.data.post) {
|
||||
postsData = Array.isArray(response.data.post) ? response.data.post : [response.data.post];
|
||||
} else if (response.data && Array.isArray(response.data)) {
|
||||
postsData = response.data;
|
||||
}
|
||||
|
||||
const posts = postsData.map(post => ({
|
||||
id: post.id,
|
||||
url: createProxyUrl(post.file_url),
|
||||
preview: createProxyUrl(post.preview_url),
|
||||
tags: post.tags ? post.tags.split(' ') : [],
|
||||
rating: post.rating,
|
||||
score: post.score,
|
||||
preview: createProxyUrl(post.preview_url || post.thumbnail_url || post.file_url),
|
||||
tags: post.tags ? (typeof post.tags === 'string' ? post.tags.split(' ') : post.tags) : [],
|
||||
rating: post.rating || 'unknown',
|
||||
score: post.score || 0,
|
||||
source: 'gelbooru'
|
||||
}));
|
||||
|
||||
res.json({ posts });
|
||||
} catch (error) {
|
||||
console.error('Ошибка Gelbooru API:', error);
|
||||
res.status(500).json({ error: 'Ошибка поиска' });
|
||||
console.error('Ошибка Gelbooru API:', error.message);
|
||||
if (error.response) {
|
||||
console.error('Gelbooru ответ:', error.response.status, error.response.data);
|
||||
}
|
||||
res.status(500).json({ error: 'Ошибка поиска Gelbooru', details: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -188,18 +205,36 @@ router.get('/anime/tags', authenticate, async (req, res) => {
|
|||
name_pattern: `${query}%`,
|
||||
orderby: 'count',
|
||||
limit: 10
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
},
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
const tags = (response.data.tag || []).map(tag => ({
|
||||
name: tag.name,
|
||||
count: tag.count
|
||||
}));
|
||||
// Обработка разных форматов ответа Gelbooru
|
||||
let tagsData = [];
|
||||
if (Array.isArray(response.data)) {
|
||||
tagsData = response.data;
|
||||
} else if (response.data && response.data.tag) {
|
||||
tagsData = Array.isArray(response.data.tag) ? response.data.tag : [response.data.tag];
|
||||
} else if (response.data && Array.isArray(response.data)) {
|
||||
tagsData = response.data;
|
||||
}
|
||||
|
||||
const tags = tagsData.map(tag => ({
|
||||
name: tag.name || tag.tag || '',
|
||||
count: tag.count || tag.post_count || 0
|
||||
})).filter(tag => tag.name);
|
||||
|
||||
res.json({ tags });
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения тегов:', error);
|
||||
res.status(500).json({ error: 'Ошибка получения тегов' });
|
||||
console.error('Ошибка получения тегов Gelbooru:', error.message);
|
||||
if (error.response) {
|
||||
console.error('Gelbooru ответ:', error.response.status, error.response.data);
|
||||
}
|
||||
// В случае ошибки возвращаем пустой массив вместо ошибки
|
||||
res.json({ tags: [] });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
bottom: 0;
|
||||
background: var(--bg-secondary);
|
||||
z-index: 999; /* Выше навигации (50) */
|
||||
pointer-events: all;
|
||||
touch-action: none;
|
||||
overflow: hidden;
|
||||
/* Убираем touch-action: none чтобы не блокировать клики */
|
||||
}
|
||||
|
||||
.comments-modal {
|
||||
|
|
@ -18,8 +17,7 @@
|
|||
background: var(--bg-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: all;
|
||||
touch-action: auto;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.comments-modal .modal-header {
|
||||
|
|
@ -183,8 +181,7 @@
|
|||
display: flex;
|
||||
gap: 8px;
|
||||
z-index: 1000;
|
||||
pointer-events: all;
|
||||
touch-action: auto;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.comment-form input {
|
||||
|
|
@ -195,7 +192,7 @@
|
|||
color: var(--text-primary);
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
pointer-events: all;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
|
|
@ -210,7 +207,7 @@
|
|||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
pointer-events: all;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
}
|
||||
|
||||
.send-btn svg {
|
||||
|
|
|
|||
|
|
@ -41,9 +41,16 @@ export default function CommentsModal({ post, onClose, onUpdate }) {
|
|||
return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' })
|
||||
}
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
// Закрывать только при клике на overlay, не на контент
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="comments-modal-overlay">
|
||||
<div className="comments-modal">
|
||||
<div className="comments-modal-overlay" onClick={handleOverlayClick}>
|
||||
<div className="comments-modal" onClick={(e) => e.stopPropagation()}>
|
||||
{/* Хедер */}
|
||||
<div className="modal-header">
|
||||
<button className="close-btn" onClick={onClose}>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
pointer-events: all;
|
||||
touch-action: none;
|
||||
overflow: hidden;
|
||||
/* Убираем pointer-events и touch-action чтобы не блокировать клики */
|
||||
}
|
||||
|
||||
.report-modal-overlay {
|
||||
|
|
@ -25,8 +24,7 @@
|
|||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: all;
|
||||
touch-action: none;
|
||||
/* Убираем pointer-events и touch-action чтобы не блокировать клики */
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
|
|
@ -54,7 +52,7 @@
|
|||
justify-content: center;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
}
|
||||
|
||||
.menu-close-btn svg {
|
||||
|
|
@ -66,8 +64,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
pointer-events: all;
|
||||
touch-action: auto;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
|
|
@ -83,8 +80,7 @@
|
|||
font-weight: 500;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
touch-action: auto;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.menu-item svg {
|
||||
|
|
@ -130,7 +126,7 @@
|
|||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
resize: none;
|
||||
pointer-events: all;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
|
|
@ -142,7 +138,7 @@
|
|||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,15 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
}
|
||||
|
||||
if (showReportModal) {
|
||||
const handleReportOverlayClick = (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
setShowReportModal(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="report-modal-overlay">
|
||||
<div className="report-modal-header">
|
||||
<div className="report-modal-overlay" onClick={handleReportOverlayClick}>
|
||||
<div className="report-modal-header" onClick={(e) => e.stopPropagation()}>
|
||||
<button className="menu-close-btn" onClick={onClose}>
|
||||
<X size={24} />
|
||||
</button>
|
||||
|
|
@ -51,7 +57,7 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div className="report-modal-body">
|
||||
<div className="report-modal-body" onClick={(e) => e.stopPropagation()}>
|
||||
<textarea
|
||||
placeholder="Опишите причину жалобы..."
|
||||
value={reportReason}
|
||||
|
|
@ -64,9 +70,16 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
)
|
||||
}
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
// Закрывать только при клике на overlay, не на контент
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="post-menu-overlay">
|
||||
<div className="menu-header">
|
||||
<div className="post-menu-overlay" onClick={handleOverlayClick}>
|
||||
<div className="menu-header" onClick={(e) => e.stopPropagation()}>
|
||||
<button className="menu-close-btn" onClick={onClose}>
|
||||
<X size={24} />
|
||||
</button>
|
||||
|
|
@ -74,7 +87,7 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
<div style={{ width: 40 }} />
|
||||
</div>
|
||||
|
||||
<div className="menu-items">
|
||||
<div className="menu-items" onClick={(e) => e.stopPropagation()}>
|
||||
{isOwnPost || isModerator ? (
|
||||
<button className="menu-item danger" onClick={onDelete}>
|
||||
<Trash2 size={20} />
|
||||
|
|
|
|||
|
|
@ -34,14 +34,28 @@ export default function Search({ user }) {
|
|||
let tags = []
|
||||
|
||||
if (mode === 'furry' || mode === 'mixed') {
|
||||
try {
|
||||
const furryTags = await getFurryTags(query)
|
||||
if (furryTags && Array.isArray(furryTags)) {
|
||||
tags = [...tags, ...furryTags.map(t => ({ ...t, source: 'e621' }))]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки e621 тегов:', error)
|
||||
// Продолжаем даже если e621 не работает
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'anime' || mode === 'mixed') {
|
||||
try {
|
||||
const animeTags = await getAnimeTags(query)
|
||||
if (animeTags && Array.isArray(animeTags)) {
|
||||
tags = [...tags, ...animeTags.map(t => ({ ...t, source: 'gelbooru' }))]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки Gelbooru тегов:', error)
|
||||
// Продолжаем даже если Gelbooru не работает
|
||||
}
|
||||
}
|
||||
|
||||
// Убрать дубликаты
|
||||
const uniqueTags = tags.reduce((acc, tag) => {
|
||||
|
|
@ -54,6 +68,7 @@ export default function Search({ user }) {
|
|||
setTagSuggestions(uniqueTags.slice(0, 10))
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки тегов:', error)
|
||||
setTagSuggestions([])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,14 +83,28 @@ export default function Search({ user }) {
|
|||
let allResults = []
|
||||
|
||||
if (mode === 'furry' || mode === 'mixed') {
|
||||
try {
|
||||
const furryResults = await searchFurry(searchQuery, { limit: 30 })
|
||||
if (furryResults && Array.isArray(furryResults)) {
|
||||
allResults = [...allResults, ...furryResults]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка e621 поиска:', error)
|
||||
// Продолжаем поиск даже если e621 не работает
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'anime' || mode === 'mixed') {
|
||||
try {
|
||||
const animeResults = await searchAnime(searchQuery, { limit: 30 })
|
||||
if (animeResults && Array.isArray(animeResults)) {
|
||||
allResults = [...allResults, ...animeResults]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка Gelbooru поиска:', error)
|
||||
// Продолжаем поиск даже если Gelbooru не работает
|
||||
}
|
||||
}
|
||||
|
||||
// Перемешать результаты если mixed режим
|
||||
if (mode === 'mixed') {
|
||||
|
|
@ -87,10 +116,13 @@ export default function Search({ user }) {
|
|||
|
||||
if (allResults.length > 0) {
|
||||
hapticFeedback('success')
|
||||
} else {
|
||||
hapticFeedback('error')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка поиска:', error)
|
||||
hapticFeedback('error')
|
||||
setResults([])
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
╔═══════════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ 🔧 ВСЕ ИСПРАВЛЕНИЯ ПРИМЕНЕНЫ! 🔧 ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════════════╝
|
||||
|
||||
|
||||
ИСПРАВЛЕНО:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ 1. Проблема с прыганием комментов и кнопками
|
||||
• Убраны `pointer-events: all` и `touch-action: none` из overlay
|
||||
• Добавлены `stopPropagation()` для предотвращения закрытия при клике на контент
|
||||
• Добавлены `handleOverlayClick` для правильного закрытия модалов
|
||||
• Теперь все кнопки нажимаются там, где отображаются
|
||||
|
||||
✅ 2. Проблема с поиском
|
||||
• Добавлена обработка ошибок для каждого API отдельно
|
||||
• Поиск продолжается даже если один из API не работает
|
||||
• Проверка на массив перед добавлением результатов
|
||||
|
||||
✅ 3. Проблема с Gelbooru API
|
||||
• Добавлена обработка разных форматов ответа Gelbooru
|
||||
• Добавлен User-Agent заголовок
|
||||
• Добавлен timeout 30 секунд
|
||||
• Улучшена обработка ошибок с логированием
|
||||
• В случае ошибки автокомплит возвращает пустой массив вместо ошибки
|
||||
|
||||
✅ 4. Ошибка 401
|
||||
• Улучшена обработка `x-telegram-init-data` заголовка
|
||||
• Добавлена поддержка JSON формата initData
|
||||
• Улучшено логирование ошибок
|
||||
• Добавлены проверки на наличие данных перед парсингом
|
||||
|
||||
|
||||
ИЗМЕНЕННЫЕ ФАЙЛЫ:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Frontend:
|
||||
• frontend/src/components/CommentsModal.jsx
|
||||
• frontend/src/components/CommentsModal.css
|
||||
• frontend/src/components/PostMenu.jsx
|
||||
• frontend/src/components/PostMenu.css
|
||||
• frontend/src/pages/Search.jsx
|
||||
|
||||
Backend:
|
||||
• backend/routes/search.js
|
||||
• backend/middleware/auth.js
|
||||
|
||||
|
||||
ОБНОВЛЕНИЕ (9 файлов):
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
cd /Users/glpshchn/Desktop/nakama
|
||||
|
||||
# Frontend
|
||||
scp frontend/src/components/CommentsModal.jsx frontend/src/components/CommentsModal.css frontend/src/components/PostMenu.jsx frontend/src/components/PostMenu.css root@ваш_IP:/var/www/nakama/frontend/src/components/
|
||||
|
||||
scp frontend/src/pages/Search.jsx root@ваш_IP:/var/www/nakama/frontend/src/pages/
|
||||
|
||||
# Backend
|
||||
scp backend/routes/search.js backend/middleware/auth.js root@ваш_IP:/var/www/nakama/backend/routes/
|
||||
scp backend/middleware/auth.js root@ваш_IP:/var/www/nakama/backend/middleware/
|
||||
|
||||
# На сервере
|
||||
ssh root@ваш_IP "cd /var/www/nakama/frontend && npm run build"
|
||||
ssh root@ваш_IP "cd /var/www/nakama/backend && pm2 restart nakama-backend"
|
||||
|
||||
|
||||
ЧТО ИСПРАВЛЕНО:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. ✅ Комменты больше не прыгают
|
||||
2. ✅ Кнопки нажимаются там, где отображаются
|
||||
3. ✅ Поиск работает даже если один API не отвечает
|
||||
4. ✅ Gelbooru API обрабатывает разные форматы ответа
|
||||
5. ✅ Ошибка 401 исправлена с улучшенной обработкой
|
||||
|
||||
|
||||
3 минуты
|
||||
|
||||
Loading…
Reference in New Issue