Update files

This commit is contained in:
glpshchn 2025-12-05 00:49:05 +03:00
parent fbeb53d96f
commit 97f20486a1
3 changed files with 64 additions and 87 deletions

View File

@ -187,6 +187,63 @@ router.get('/posts', authenticateModeration, requireModerationAccess, async (req
}); });
}); });
// Получить пост с комментариями
router.get('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
try {
const post = await Post.findById(req.params.id)
.populate('author', 'username firstName lastName photoUrl')
.populate('comments.author', 'username firstName lastName photoUrl')
.exec();
if (!post) {
return res.status(404).json({ error: 'Пост не найден' });
}
res.json({
post: {
id: post._id,
author: post.author ? serializeUser(post.author) : null,
content: post.content,
hashtags: post.hashtags,
tags: post.tags,
images: post.images || (post.imageUrl ? [post.imageUrl] : []),
comments: post.comments || [],
likesCount: post.likes?.length || 0,
isNSFW: post.isNSFW,
createdAt: post.createdAt
}
});
} catch (error) {
console.error('Ошибка получения поста:', error);
res.status(500).json({ error: 'Ошибка сервера' });
}
});
// Удалить комментарий (модераторский интерфейс)
router.delete('/posts/:postId/comments/:commentId', authenticateModeration, requireModerationAccess, async (req, res) => {
try {
const post = await Post.findById(req.params.postId);
if (!post) {
return res.status(404).json({ error: 'Пост не найден' });
}
const comment = post.comments.id(req.params.commentId);
if (!comment) {
return res.status(404).json({ error: 'Комментарий не найден' });
}
post.comments.pull(req.params.commentId);
await post.save();
await post.populate('comments.author', 'username firstName lastName photoUrl');
res.json({ comments: post.comments });
} catch (error) {
console.error('Ошибка удаления комментария:', error);
res.status(500).json({ error: 'Ошибка сервера' });
}
});
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => { router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
const { content, hashtags, tags, isNSFW } = req.body; const { content, hashtags, tags, isNSFW } = req.body;

View File

@ -1,10 +1,8 @@
import { useState } from 'react' import { useState } from 'react'
import { Settings, Heart, Edit2, Shield, Copy, Users } from 'lucide-react' import { Settings, Heart, Edit2, Shield } from 'lucide-react'
import { updateProfile } from '../utils/api' import { updateProfile } from '../utils/api'
import { hapticFeedback, showAlert } from '../utils/telegram' import { hapticFeedback } from '../utils/telegram'
import { decodeHtmlEntities } from '../utils/htmlEntities'
import ThemeToggle from '../components/ThemeToggle' import ThemeToggle from '../components/ThemeToggle'
import FollowListModal from '../components/FollowListModal'
import './Profile.css' import './Profile.css'
const DONATION_URL = 'https://donatepay.ru/don/1435720' const DONATION_URL = 'https://donatepay.ru/don/1435720'
@ -40,8 +38,6 @@ export default function Profile({ user, setUser }) {
const [showSettings, setShowSettings] = useState(false) const [showSettings, setShowSettings] = useState(false)
const [showEditBio, setShowEditBio] = useState(false) const [showEditBio, setShowEditBio] = useState(false)
const [bio, setBio] = useState(user.bio || '') const [bio, setBio] = useState(user.bio || '')
const [showFollowers, setShowFollowers] = useState(false)
const [showFollowing, setShowFollowing] = useState(false)
const [settings, setSettings] = useState(normalizeSettings(user.settings)) const [settings, setSettings] = useState(normalizeSettings(user.settings))
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
@ -144,7 +140,7 @@ export default function Profile({ user, setUser }) {
{user.bio ? ( {user.bio ? (
<div className="profile-bio"> <div className="profile-bio">
<p>{decodeHtmlEntities(user.bio)}</p> <p>{user.bio}</p>
<button className="edit-bio-btn" onClick={() => setShowEditBio(true)}> <button className="edit-bio-btn" onClick={() => setShowEditBio(true)}>
<Edit2 size={16} /> <Edit2 size={16} />
</button> </button>
@ -158,12 +154,12 @@ export default function Profile({ user, setUser }) {
</div> </div>
<div className="profile-stats"> <div className="profile-stats">
<div className="stat-item" onClick={() => setShowFollowers(true)} style={{ cursor: 'pointer' }}> <div className="stat-item">
<span className="stat-value">{user.followersCount || 0}</span> <span className="stat-value">{user.followersCount || 0}</span>
<span className="stat-label">Подписчики</span> <span className="stat-label">Подписчики</span>
</div> </div>
<div className="stat-divider" /> <div className="stat-divider" />
<div className="stat-item" onClick={() => setShowFollowing(true)} style={{ cursor: 'pointer' }}> <div className="stat-item">
<span className="stat-value">{user.followingCount || 0}</span> <span className="stat-value">{user.followingCount || 0}</span>
<span className="stat-label">Подписки</span> <span className="stat-label">Подписки</span>
</div> </div>
@ -185,62 +181,6 @@ export default function Profile({ user, setUser }) {
</button> </button>
</div> </div>
{/* Реферальная ссылка */}
{user.referralCode && (
<div className="referral-card card">
<div className="referral-content">
<div className="referral-icon">
<Users size={20} />
</div>
<div className="referral-text">
<h3>Пригласи друзей</h3>
<p>Получи +1 к счетчику, когда приглашенный создаст первый пост</p>
<div className="referral-stats">
Приглашено: <strong>{user.referralsCount || 0}</strong>
</div>
</div>
</div>
<div className="referral-link-section">
<div className="referral-link">
<code>{`https://t.me/${import.meta.env.VITE_TELEGRAM_BOT_NAME || 'NakamaSpaceBot'}?startapp=${user.referralCode}`}</code>
</div>
<button
className="referral-copy-btn"
onClick={async () => {
try {
hapticFeedback('light')
const botName = import.meta.env.VITE_TELEGRAM_BOT_NAME || 'NakamaSpaceBot'
const referralLink = `https://t.me/${botName}?startapp=${user.referralCode}`
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(referralLink)
hapticFeedback('success')
showAlert('✅ Ссылка скопирована!')
} else {
const textArea = document.createElement('textarea')
textArea.value = referralLink
textArea.style.position = 'fixed'
textArea.style.opacity = '0'
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy')
document.body.removeChild(textArea)
hapticFeedback('success')
showAlert('✅ Ссылка скопирована!')
}
} catch (error) {
console.error('Ошибка копирования:', error)
hapticFeedback('error')
}
}}
>
<Copy size={18} />
<span>Копировать</span>
</button>
</div>
</div>
)}
<div className="profile-powered"> <div className="profile-powered">
Powered by glpshcn \\ RBach \\ E621 \\ GelBooru Powered by glpshcn \\ RBach \\ E621 \\ GelBooru
</div> </div>
@ -394,26 +334,6 @@ export default function Profile({ user, setUser }) {
</div> </div>
</div> </div>
)} )}
{/* Модалка подписчиков */}
{showFollowers && user && (
<FollowListModal
users={user.followers || []}
title="Подписчики"
currentUser={user}
onClose={() => setShowFollowers(false)}
/>
)}
{/* Модалка подписок */}
{showFollowing && user && (
<FollowListModal
users={user.following || []}
title="Подписки"
currentUser={user}
onClose={() => setShowFollowing(false)}
/>
)}
</div> </div>
) )
} }

View File

@ -105,10 +105,10 @@ export const confirmRemoveAdmin = (adminId, code) =>
api.post('/mod-app/admins/confirm-remove', { adminId, code }).then((res) => res.data) api.post('/mod-app/admins/confirm-remove', { adminId, code }).then((res) => res.data)
export const getPostComments = (postId) => export const getPostComments = (postId) =>
api.get(`/posts/${postId}`).then((res) => res.data.post) api.get(`/mod-app/posts/${postId}`).then((res) => res.data.post)
export const deleteComment = (postId, commentId) => export const deleteComment = (postId, commentId) =>
api.delete(`/posts/${postId}/comments/${commentId}`).then((res) => res.data) api.delete(`/mod-app/posts/${postId}/comments/${commentId}`).then((res) => res.data)
export default api export default api