diff --git a/DELETE_MUSIC.md b/DELETE_MUSIC.md new file mode 100644 index 0000000..e809b39 --- /dev/null +++ b/DELETE_MUSIC.md @@ -0,0 +1,126 @@ +# Скрипт удаления всех альбомов и треков + +## Описание + +Скрипт `deleteAllMusic.js` безвозвратно удаляет все музыкальные данные из базы данных: +- Все треки (Tracks) +- Все альбомы (Albums) +- Все избранные треки (FavoriteTrack) +- Все ссылки на треки в постах (attachedTrack устанавливается в null) + +**⚠️ ВНИМАНИЕ: Это действие необратимо!** + +## Использование + +### Для Windows (CMD/PowerShell): + +```cmd +cd backend +node scripts\deleteAllMusic.js +``` + +### Для Linux/Mac: + +```bash +cd backend +node scripts/deleteAllMusic.js +``` + +### Или из корня проекта: + +```cmd +node backend/scripts/deleteAllMusic.js +``` + +## Что делает скрипт + +1. **Подключается к MongoDB** используя настройки из `.env` +2. **Показывает статистику** текущего количества записей +3. **Удаляет данные в следующем порядке:** + - Избранные треки (FavoriteTrack) + - Обнуляет attachedTrack в постах + - Удаляет все треки (Track) + - Удаляет все альбомы (Album) +4. **Показывает финальную статистику** + +## Пример вывода + +``` +🔌 Подключение к MongoDB... +✅ Подключено к MongoDB + +📊 Текущая статистика: + - Треков: 150 + - Альбомов: 25 + - Избранных треков: 500 + - Постов с прикрепленными треками: 80 + +🗑️ Начало удаления... + +1️⃣ Удаление избранных треков... + ✅ Удалено избранных треков: 500 + +2️⃣ Обнуление прикрепленных треков в постах... + ✅ Обновлено постов: 80 + +3️⃣ Удаление треков... + ✅ Удалено треков: 150 + +4️⃣ Удаление альбомов... + ✅ Удалено альбомов: 25 + +📊 Финальная статистика: + - Треков: 0 + - Альбомы: 0 + - Избранных треков: 0 + - Постов с прикрепленными треками: 0 + +✅ Все альбомы и треки успешно удалены! + +🔌 Отключено от MongoDB +``` + +## Важные замечания + +- ⚠️ **Физические файлы не удаляются** - скрипт удаляет только записи из базы данных. Файлы в `backend/uploads/music/` остаются на диске. Для полной очистки нужно вручную удалить файлы. + +- ⚠️ **Исполнители (Artists) не удаляются** - они остаются в базе, так как могут быть переиспользованы при загрузке новых треков. + +- ✅ **Посты не удаляются** - только обнуляется поле `attachedTrack`. + +## Дополнительная очистка файлов + +Если нужно также удалить физические файлы (аудио и обложки): + +### Windows (PowerShell): + +```powershell +# Удалить все файлы в папке музыки (сохранив структуру папок) +Remove-Item -Path "backend\uploads\music\*" -Include *.mp3,*.wav,*.ogg,*.m4a,*.flac,*.jpg,*.jpeg,*.png -Force + +# Или полностью удалить папку и создать заново +Remove-Item -Path "backend\uploads\music" -Recurse -Force +New-Item -Path "backend\uploads\music" -ItemType Directory +``` + +### Linux/Mac: + +```bash +# Удалить все файлы в папке музыки +rm -f backend/uploads/music/*.{mp3,wav,ogg,m4a,flac,jpg,jpeg,png} + +# Или полностью удалить папку и создать заново +rm -rf backend/uploads/music +mkdir -p backend/uploads/music +``` + +## Восстановление после удаления + +После выполнения скрипта невозможно восстановить удаленные данные из базы данных. Если у вас есть резервная копия (backup), вы можете восстановить данные из неё. + +Для создания резервной копии перед удалением используйте: + +```cmd +node backend/scripts/backup.js +``` + diff --git a/NGINX_CLIENT_MAX_BODY_SIZE.md b/NGINX_CLIENT_MAX_BODY_SIZE.md new file mode 100644 index 0000000..286fefa --- /dev/null +++ b/NGINX_CLIENT_MAX_BODY_SIZE.md @@ -0,0 +1,86 @@ +# Исправление ошибки 413 при загрузке альбомов + +## Проблема +При загрузке ZIP альбомов возникает ошибка 413 "Request Entity Too Large", даже после увеличения лимитов в Express и multer. + +## Решение + +Ошибка 413 возникает на уровне Nginx, который проксирует запросы к backend. Нужно увеличить `client_max_body_size` в конфигурации Nginx. + +### Вариант 1: Если используется Nginx для проксирования к backend + +Добавьте или увеличьте `client_max_body_size` в конфигурации Nginx: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # Увеличить лимит загрузки до 110MB (больше чем на backend для безопасности) + client_max_body_size 110M; + + location /api { + proxy_pass http://localhost:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Увеличить таймауты для больших загрузок + proxy_read_timeout 300s; + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + } +} +``` + +### Вариант 2: Если используется docker-compose с nginx + +Обновите `nginx-moderation-production.conf` или создайте отдельный конфиг для основного приложения: + +```nginx +# Увеличить лимит загрузки +client_max_body_size 110M; + +# Увеличить таймауты +client_body_timeout 300s; +client_header_timeout 300s; +``` + +### Вариант 3: Для production на сервере + +Если приложение развернуто на сервере, нужно обновить конфигурацию Nginx: + +1. Отредактируйте конфигурацию: +```bash +sudo nano /etc/nginx/sites-available/nakama +``` + +2. Добавьте или обновите: +```nginx +client_max_body_size 110M; +``` + +3. Проверьте конфигурацию: +```bash +sudo nginx -t +``` + +4. Перезапустите Nginx: +```bash +sudo systemctl reload nginx +``` + +## Проверка + +После применения изменений попробуйте загрузить ZIP альбом размером до 105MB. Ошибка 413 должна исчезнуть. + +## Примечание + +Лимиты должны быть установлены в следующем порядке (от большего к меньшему): +1. Nginx: `client_max_body_size 110M` (больше всех) +2. Express: `limit: '105mb'` +3. Multer: `fileSize: 105 * 1024 * 1024` + +Это обеспечивает, что запрос доходит до backend без ошибок. + diff --git a/backend/routes/posts.js b/backend/routes/posts.js index 2f72150..5fa4671 100644 --- a/backend/routes/posts.js +++ b/backend/routes/posts.js @@ -215,6 +215,12 @@ router.post('/', authenticate, strictPostLimiter, postCreationLimiter, fileUploa await post.save(); await post.populate('author', 'username firstName lastName photoUrl'); + if (post.attachedTrack) { + await post.populate({ + path: 'attachedTrack', + populate: { path: 'artist album' } + }); + } // Увеличить счетчики использования тегов if (parsedTags.length > 0) { diff --git a/backend/scripts/deleteAllMusic.js b/backend/scripts/deleteAllMusic.js new file mode 100644 index 0000000..cb19cb7 --- /dev/null +++ b/backend/scripts/deleteAllMusic.js @@ -0,0 +1,98 @@ +/** + * Скрипт для удаления всех альбомов и треков из базы данных + * + * ВНИМАНИЕ: Этот скрипт безвозвратно удалит: + * - Все треки (Tracks) + * - Все альбомы (Albums) + * - Все избранные треки (FavoriteTrack) + * - Все ссылки на треки в постах (attachedTrack будет установлен в null) + * + * Использование: + * node backend/scripts/deleteAllMusic.js + */ + +require('dotenv').config({ path: require('path').join(__dirname, '../.env') }); +const mongoose = require('mongoose'); +const Track = require('../models/Track'); +const Album = require('../models/Album'); +const FavoriteTrack = require('../models/FavoriteTrack'); +const Post = require('../models/Post'); + +const config = require('../config/index'); + +async function deleteAllMusic() { + try { + console.log('🔌 Подключение к MongoDB...'); + await mongoose.connect(config.mongoUri); + console.log('✅ Подключено к MongoDB\n'); + + // Подсчет перед удалением + const tracksCount = await Track.countDocuments(); + const albumsCount = await Album.countDocuments(); + const favoritesCount = await FavoriteTrack.countDocuments(); + const postsWithTracksCount = await Post.countDocuments({ attachedTrack: { $ne: null } }); + + console.log('📊 Текущая статистика:'); + console.log(` - Треков: ${tracksCount}`); + console.log(` - Альбомов: ${albumsCount}`); + console.log(` - Избранных треков: ${favoritesCount}`); + console.log(` - Постов с прикрепленными треками: ${postsWithTracksCount}\n`); + + if (tracksCount === 0 && albumsCount === 0) { + console.log('✅ В базе данных нет треков и альбомов для удаления'); + await mongoose.disconnect(); + return; + } + + console.log('🗑️ Начало удаления...\n'); + + // 1. Удалить все избранные треки + console.log('1️⃣ Удаление избранных треков...'); + const favoritesResult = await FavoriteTrack.deleteMany({}); + console.log(` ✅ Удалено избранных треков: ${favoritesResult.deletedCount}`); + + // 2. Обнулить attachedTrack во всех постах + console.log('\n2️⃣ Обнуление прикрепленных треков в постах...'); + const postsResult = await Post.updateMany( + { attachedTrack: { $ne: null } }, + { $set: { attachedTrack: null } } + ); + console.log(` ✅ Обновлено постов: ${postsResult.modifiedCount}`); + + // 3. Удалить все треки + console.log('\n3️⃣ Удаление треков...'); + const tracksResult = await Track.deleteMany({}); + console.log(` ✅ Удалено треков: ${tracksResult.deletedCount}`); + + // 4. Удалить все альбомы + console.log('\n4️⃣ Удаление альбомов...'); + const albumsResult = await Album.deleteMany({}); + console.log(` ✅ Удалено альбомов: ${albumsResult.deletedCount}`); + + // Финальная статистика + console.log('\n📊 Финальная статистика:'); + const finalTracksCount = await Track.countDocuments(); + const finalAlbumsCount = await Album.countDocuments(); + const finalFavoritesCount = await FavoriteTrack.countDocuments(); + const finalPostsWithTracksCount = await Post.countDocuments({ attachedTrack: { $ne: null } }); + + console.log(` - Треков: ${finalTracksCount}`); + console.log(` - Альбомов: ${finalAlbumsCount}`); + console.log(` - Избранных треков: ${finalFavoritesCount}`); + console.log(` - Постов с прикрепленными треками: ${finalPostsWithTracksCount}`); + + console.log('\n✅ Все альбомы и треки успешно удалены!'); + + } catch (error) { + console.error('❌ Ошибка при удалении:', error); + process.exit(1); + } finally { + await mongoose.disconnect(); + console.log('\n🔌 Отключено от MongoDB'); + process.exit(0); + } +} + +// Запуск скрипта +deleteAllMusic(); + diff --git a/frontend/src/components/FullPlayer.jsx b/frontend/src/components/FullPlayer.jsx index e1f6935..98e21c6 100644 --- a/frontend/src/components/FullPlayer.jsx +++ b/frontend/src/components/FullPlayer.jsx @@ -1,4 +1,5 @@ import { createPortal } from 'react-dom' +import { useLocation } from 'react-router-dom' import { X, Play, Pause, SkipBack, SkipForward, Heart, Download, Music, Volume2, VolumeX } from 'lucide-react' import { useMusicPlayer } from '../contexts/MusicPlayerContext' import { hapticFeedback, getTelegramUser } from '../utils/telegram' @@ -7,6 +8,7 @@ import api from '../utils/api' import './FullPlayer.css' export default function FullPlayer() { + const location = useLocation() const { currentTrack, isPlaying, @@ -23,11 +25,12 @@ export default function FullPlayer() { changeVolume, setIsExpanded } = useMusicPlayer() - + const [isFavorite, setIsFavorite] = useState(false) const [showVolume, setShowVolume] = useState(false) - if (!isExpanded || !currentTrack) return null + // Скрыть плеер на странице profile + if (location.pathname === '/profile' || !isExpanded || !currentTrack) return null const formatTime = (seconds) => { if (!seconds || isNaN(seconds)) return '0:00' diff --git a/frontend/src/components/LadderButton.css b/frontend/src/components/LadderButton.css index 098f153..349f461 100644 --- a/frontend/src/components/LadderButton.css +++ b/frontend/src/components/LadderButton.css @@ -1,6 +1,6 @@ .ladder-button { position: fixed; - bottom: 100px; + bottom: 140px; right: 16px; width: 56px; height: 56px; diff --git a/frontend/src/components/LadderButton.jsx b/frontend/src/components/LadderButton.jsx index aaff99d..2242a97 100644 --- a/frontend/src/components/LadderButton.jsx +++ b/frontend/src/components/LadderButton.jsx @@ -7,8 +7,8 @@ export default function LadderButton() { const navigate = useNavigate() const location = useLocation() - // Скрыть кнопку на странице ladder - if (location.pathname === '/ladder') { + // Скрыть кнопку на странице ladder и profile + if (location.pathname === '/ladder' || location.pathname === '/profile') { return null } diff --git a/frontend/src/components/MiniPlayer.jsx b/frontend/src/components/MiniPlayer.jsx index 74c2f32..148d617 100644 --- a/frontend/src/components/MiniPlayer.jsx +++ b/frontend/src/components/MiniPlayer.jsx @@ -1,9 +1,11 @@ +import { useLocation } from 'react-router-dom' import { Play, Pause, SkipForward, Music } from 'lucide-react' import { useMusicPlayer } from '../contexts/MusicPlayerContext' import { hapticFeedback } from '../utils/telegram' import './MiniPlayer.css' export default function MiniPlayer() { + const location = useLocation() const { currentTrack, isPlaying, @@ -14,7 +16,8 @@ export default function MiniPlayer() { toggleExpanded } = useMusicPlayer() - if (!currentTrack) return null + // Скрыть плеер на странице profile + if (location.pathname === '/profile' || !currentTrack) return null const progressPercent = duration > 0 ? (progress / duration) * 100 : 0 diff --git a/frontend/src/components/MusicAttachment.css b/frontend/src/components/MusicAttachment.css index 11add9f..bf08a42 100644 --- a/frontend/src/components/MusicAttachment.css +++ b/frontend/src/components/MusicAttachment.css @@ -1,11 +1,12 @@ .music-attachment { display: flex; align-items: center; - gap: 12px; + gap: 8px; background: var(--bg-secondary); border: 1px solid var(--divider-color); - border-radius: 12px; - padding: 12px; + border-radius: 8px; + padding: 8px 12px; + margin-top: 8px; transition: all 0.2s; } @@ -14,9 +15,9 @@ } .music-attachment-cover { - width: 48px; - height: 48px; - border-radius: 8px; + width: 32px; + height: 32px; + border-radius: 6px; background: var(--bg-primary); display: flex; align-items: center; @@ -36,12 +37,15 @@ } .music-attachment-info { + display: flex; + align-items: center; + gap: 6px; flex: 1; min-width: 0; } .music-attachment-title { - font-size: 14px; + font-size: 13px; font-weight: 500; color: var(--text-primary); white-space: nowrap; @@ -50,13 +54,24 @@ } .music-attachment-artist { - font-size: 12px; + font-size: 11px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.music-attachment-title::after { + content: ''; + color: var(--text-secondary); + margin: 0 4px; + font-weight: normal; +} + +.music-attachment-title:has(+ .music-attachment-artist)::after { + content: ' • '; +} + .music-attachment-play, .music-attachment-remove { background: none; diff --git a/frontend/src/components/MusicAttachment.jsx b/frontend/src/components/MusicAttachment.jsx index 9b70377..d8fd90a 100644 --- a/frontend/src/components/MusicAttachment.jsx +++ b/frontend/src/components/MusicAttachment.jsx @@ -42,8 +42,13 @@ export default function MusicAttachment({ track, onRemove, showRemove = false })
-
{track.title}
-
{track.artist?.name || 'Unknown'}
+
{track.title || 'Unknown Track'}
+ {track.artist?.name && ( + <> + +
{track.artist.name}
+ + )}