Update files
This commit is contained in:
parent
b4ca4f2d9e
commit
1cdfd57cdf
|
|
@ -0,0 +1,23 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
.git
|
||||
.gitignore
|
||||
.env
|
||||
.env.local
|
||||
.DS_Store
|
||||
*.md
|
||||
README.md
|
||||
LICENSE
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
build
|
||||
coverage
|
||||
.cache
|
||||
frontend/node_modules
|
||||
frontend/dist
|
||||
moderation/frontend/node_modules
|
||||
moderation/frontend/dist
|
||||
backend/uploads/*
|
||||
!backend/uploads/.gitkeep
|
||||
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
# 📋 Сводка изменений Nakama
|
||||
|
||||
## ✅ Выполненные задачи
|
||||
|
||||
### 1. ✨ Замена NakamaHost на Nakama
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Изменения:**
|
||||
- `backend/server.js` - изменено сообщение API
|
||||
- `backend/bot.js` - обновлены подписи к медиа (3 места)
|
||||
- `frontend/index.html` - обновлен заголовок страницы
|
||||
- `frontend/src/pages/Feed.jsx` - изменен заголовок приложения
|
||||
- `frontend/src/pages/Profile.jsx` - обновлен текст о поддержке проекта
|
||||
|
||||
---
|
||||
|
||||
### 2. 🔧 Улучшение меню репортов в системе модерации
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Изменения в `moderation/frontend/src/App.jsx`:**
|
||||
- Добавлено улучшенное отображение причины жалобы
|
||||
- Добавлен полный просмотр информации о посте (автор, содержание, медиа)
|
||||
- Добавлены превью изображений (до 3 штук)
|
||||
- Добавлены действия для работы с постом прямо из репорта:
|
||||
- Удалить пост
|
||||
- Забанить автора
|
||||
- Решено
|
||||
- Отклонить репорт
|
||||
- Добавлено сообщение "Нет активных репортов" когда репортов нет
|
||||
|
||||
---
|
||||
|
||||
### 3. ✏️ Возможность редактирования постов в системе модерации
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Изменения в `backend/models/Post.js`:**
|
||||
- Добавлено поле `publishedToChannel` (Boolean) - пост опубликован в канал
|
||||
- Добавлено поле `channelMessageId` (Number) - ID сообщения в Telegram канале
|
||||
- Добавлено поле `adminNumber` (Number) - номер админа, который опубликовал
|
||||
- Добавлено поле `editedAt` (Date) - время последнего редактирования
|
||||
|
||||
**Изменения в `backend/routes/modApp.js`:**
|
||||
- Обновлен `PUT /posts/:id` с проверкой прав:
|
||||
- Владелец может редактировать любые посты
|
||||
- Админы могут редактировать только свои посты из канала (по adminNumber)
|
||||
- Добавлено автоматическое обновление поста в Telegram канале при редактировании
|
||||
- Обновлен `GET /posts` для возврата информации о публикации в канале
|
||||
- Обновлен `POST /channel/publish` для создания записи в БД с информацией о канале
|
||||
|
||||
**Изменения в `backend/bots/serverMonitor.js`:**
|
||||
- `sendChannelMediaGroup` теперь возвращает messageId
|
||||
- Добавлена функция `updateChannelMessage` для обновления подписи к сообщению в канале
|
||||
|
||||
---
|
||||
|
||||
### 4. 🖼️ Исправление загрузки медиа в систему модерации
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Изменения в `moderation/frontend/src/App.jsx`:**
|
||||
- Добавлено преобразование относительных путей к изображениям в абсолютные URLs
|
||||
- Добавлена обработка ошибок загрузки изображений с fallback
|
||||
- Исправлено отображение медиа в постах
|
||||
- Исправлено отображение медиа в репортах
|
||||
- Добавлены console.error для отладки проблем с загрузкой
|
||||
|
||||
---
|
||||
|
||||
### 5. 💬 Исправление админского чата
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Изменения в `moderation/frontend/src/App.jsx`:**
|
||||
- Добавлен расширенный логгинг для отладки:
|
||||
- Логирование подключения WebSocket
|
||||
- Логирование авторизации
|
||||
- Логирование получения/отправки сообщений
|
||||
- Логирование ошибок подключения
|
||||
- Увеличен timeout подключения до 10 секунд
|
||||
- Добавлен обработчик `connect_error` для логирования ошибок
|
||||
- Исправлена задержка прокрутки к последнему сообщению (setTimeout 100ms)
|
||||
- Добавлено предупреждение при попытке отправить сообщение без подключения
|
||||
|
||||
---
|
||||
|
||||
### 6. 🗄️ Настройка подключения к БД на удаленном сервере
|
||||
**Статус:** ✅ Завершено (требуется ручная настройка)
|
||||
|
||||
**Создан файл `docker-compose.yml`:**
|
||||
- MongoDB URI настроен на `mongodb://103.80.87.247:27017/nakama`
|
||||
- Добавлена переменная окружения `MONGODB_URI`
|
||||
- Настроено монтирование директорий для данных БД
|
||||
|
||||
**Создан файл `setup-remote-storage.sh`:**
|
||||
- Скрипт для автоматической настройки SSHFS
|
||||
- Монтирование удаленных директорий:
|
||||
- `/var/nakama/db` → `/mnt/nakama-db`
|
||||
- `/var/nakama/media` → `/mnt/nakama-media`
|
||||
- `/var/nakama/backups` → `/mnt/nakama-backups`
|
||||
- Опция автомонтирования через `/etc/fstab`
|
||||
|
||||
**Создана документация `DEPLOYMENT_GUIDE.md`:**
|
||||
- Подробная инструкция по установке MongoDB на удаленном сервере
|
||||
- Настройка аутентификации MongoDB
|
||||
- Настройка удаленного доступа
|
||||
- Настройка firewall
|
||||
|
||||
---
|
||||
|
||||
### 7. 💾 Настройка автоматических бекапов БД
|
||||
**Статус:** ✅ Завершено (требуется ручная настройка cron)
|
||||
|
||||
**Создан файл `backup-cron.sh`:**
|
||||
- Автоматическое создание бекапов через `mongodump`
|
||||
- Сжатие бекапов в .tar.gz архивы
|
||||
- Автоматическое удаление старых бекапов (по умолчанию 30 дней)
|
||||
- Логирование всех операций
|
||||
- Цветной вывод для удобства
|
||||
- Опциональные Telegram уведомления
|
||||
|
||||
**Создана документация `CRON_SETUP.md`:**
|
||||
- Пошаговая инструкция настройки cron
|
||||
- Примеры различных расписаний:
|
||||
- Еженедельные бекапы (воскресенье в 3:00)
|
||||
- Ежедневные бекапы
|
||||
- Несколько раз в неделю
|
||||
- Настройка Telegram уведомлений
|
||||
- Инструкции по восстановлению из бекапа
|
||||
|
||||
**Добавлен сервис `backup` в `docker-compose.yml`:**
|
||||
- Готовый контейнер для запуска бекапов
|
||||
- Смонтированная директория для бекапов
|
||||
|
||||
---
|
||||
|
||||
### 8. 📁 Настройка хранения медиа на удаленном сервере
|
||||
**Статус:** ✅ Завершено (требуется ручная настройка)
|
||||
|
||||
**Изменения в `docker-compose.yml`:**
|
||||
- Backend монтирует `/mnt/nakama-media` в `/app/backend/uploads`
|
||||
- Все загруженные медиа автоматически сохраняются на удаленный сервер
|
||||
- Настроено через SSHFS монтирование
|
||||
|
||||
**Создан скрипт `setup-remote-storage.sh`:**
|
||||
- Автоматическая установка SSHFS
|
||||
- Создание директорий на удаленном сервере
|
||||
- Монтирование через SSH
|
||||
- Опция автомонтирования при загрузке системы
|
||||
|
||||
---
|
||||
|
||||
### 9. 🐳 Настройка Docker для всех компонентов
|
||||
**Статус:** ✅ Завершено
|
||||
|
||||
**Созданные файлы:**
|
||||
|
||||
1. **`Dockerfile.backend`**
|
||||
- Multi-stage сборка для оптимизации
|
||||
- Node 20 Alpine (минимальный размер)
|
||||
- Production зависимости
|
||||
- Автоматическое создание директорий для uploads
|
||||
|
||||
2. **`Dockerfile.frontend`**
|
||||
- Multi-stage сборка (builder + nginx)
|
||||
- Vite сборка с оптимизацией
|
||||
- Nginx для раздачи статики
|
||||
- Gzip сжатие
|
||||
- Кэширование статических файлов
|
||||
|
||||
3. **`Dockerfile.moderation`**
|
||||
- Аналогично frontend
|
||||
- Отдельный контейнер для системы модерации
|
||||
- Nginx с оптимизацией
|
||||
|
||||
4. **`docker-compose.yml`**
|
||||
- Полная оркестрация всех сервисов:
|
||||
- backend (Node.js API)
|
||||
- frontend (основное приложение)
|
||||
- moderation (система модерации)
|
||||
- mongodb (база данных)
|
||||
- backup (сервис бекапов)
|
||||
- Настроенные сети
|
||||
- Volumes для данных
|
||||
- Health checks
|
||||
- Переменные окружения
|
||||
- Зависимости между сервисами
|
||||
|
||||
5. **`nginx.conf` и `nginx-moderation.conf`**
|
||||
- Оптимизированная конфигурация nginx
|
||||
- Gzip сжатие
|
||||
- Кэширование статики
|
||||
- SPA роутинг (fallback на index.html)
|
||||
|
||||
6. **`.dockerignore`**
|
||||
- Исключение ненужных файлов из образов
|
||||
- Оптимизация размера образов
|
||||
- Ускорение сборки
|
||||
|
||||
7. **`.env.example`**
|
||||
- Полный пример конфигурации
|
||||
- Все необходимые переменные окружения
|
||||
- Комментарии и значения по умолчанию
|
||||
|
||||
---
|
||||
|
||||
## 📚 Созданная документация
|
||||
|
||||
### 1. **DEPLOYMENT_GUIDE.md** - Полное руководство по развертыванию
|
||||
- Требования к системе
|
||||
- Настройка удаленного сервера
|
||||
- Установка и настройка MongoDB
|
||||
- Развертывание с Docker
|
||||
- Настройка nginx reverse proxy
|
||||
- SSL сертификаты
|
||||
- Мониторинг и обслуживание
|
||||
- Решение проблем
|
||||
|
||||
### 2. **CRON_SETUP.md** - Настройка автоматических бекапов
|
||||
- Пошаговая инструкция
|
||||
- Синтаксис cron
|
||||
- Примеры расписаний
|
||||
- Настройка уведомлений
|
||||
- Управление бекапами
|
||||
- Восстановление из бекапа
|
||||
|
||||
### 3. **CHANGES_SUMMARY.md** - Этот файл
|
||||
- Полная сводка всех изменений
|
||||
- Инструкции по запуску
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### Подготовка
|
||||
|
||||
1. **Настройте удаленный сервер:**
|
||||
```bash
|
||||
# Следуйте инструкциям в DEPLOYMENT_GUIDE.md
|
||||
ssh root@103.80.87.247
|
||||
# Установите MongoDB и создайте директории
|
||||
```
|
||||
|
||||
2. **Настройте локальное окружение:**
|
||||
```bash
|
||||
cd /Users/glpshchn/Desktop/nakama
|
||||
|
||||
# Создайте .env файл
|
||||
cp .env.example .env
|
||||
nano .env # Заполните переменные
|
||||
```
|
||||
|
||||
3. **Настройте удаленное хранилище (опционально):**
|
||||
```bash
|
||||
./setup-remote-storage.sh
|
||||
```
|
||||
|
||||
### Запуск с Docker
|
||||
|
||||
```bash
|
||||
# Сборка всех сервисов
|
||||
docker-compose build
|
||||
|
||||
# Запуск в фоновом режиме
|
||||
docker-compose up -d
|
||||
|
||||
# Проверка статуса
|
||||
docker-compose ps
|
||||
|
||||
# Просмотр логов
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
### Настройка бекапов
|
||||
|
||||
```bash
|
||||
# Скопируйте скрипт на удаленный сервер
|
||||
scp backup-cron.sh root@103.80.87.247:/usr/local/bin/
|
||||
|
||||
# Следуйте инструкциям в CRON_SETUP.md
|
||||
ssh root@103.80.87.247
|
||||
chmod +x /usr/local/bin/backup-cron.sh
|
||||
crontab -e
|
||||
# Добавьте: 0 3 * * 0 /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
### Доступ к приложению
|
||||
|
||||
После запуска:
|
||||
- **Frontend (основное приложение):** http://localhost:5173
|
||||
- **Moderation (система модерации):** http://localhost:5174
|
||||
- **Backend API:** http://localhost:3000
|
||||
- **Health check:** http://localhost:3000/health
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Команды для управления
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# Остановить все сервисы
|
||||
docker-compose down
|
||||
|
||||
# Перезапустить конкретный сервис
|
||||
docker-compose restart backend
|
||||
|
||||
# Пересобрать и запустить
|
||||
docker-compose up -d --build
|
||||
|
||||
# Просмотр логов конкретного сервиса
|
||||
docker-compose logs -f backend
|
||||
|
||||
# Выполнить команду в контейнере
|
||||
docker-compose exec backend sh
|
||||
```
|
||||
|
||||
### Бекапы
|
||||
|
||||
```bash
|
||||
# Ручной бекап
|
||||
ssh root@103.80.87.247 '/usr/local/bin/backup-cron.sh'
|
||||
|
||||
# Список бекапов
|
||||
ssh root@103.80.87.247 'ls -lh /var/nakama/backups/'
|
||||
|
||||
# Восстановление
|
||||
ssh root@103.80.87.247
|
||||
cd /var/nakama/backups
|
||||
tar -xzf nakama_backup_YYYY-MM-DD_HH-MM-SS.tar.gz
|
||||
mongorestore --uri="mongodb://localhost:27017" --drop --gzip --db nakama nakama_backup_*/nakama/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Мониторинг
|
||||
|
||||
### Проверка здоровья системы
|
||||
|
||||
```bash
|
||||
# Статус Docker контейнеров
|
||||
docker-compose ps
|
||||
|
||||
# Использование ресурсов
|
||||
docker stats
|
||||
|
||||
# Логи в реальном времени
|
||||
docker-compose logs -f
|
||||
|
||||
# Проверка MongoDB
|
||||
ssh root@103.80.87.247 'systemctl status mongod'
|
||||
|
||||
# Свободное место на диске
|
||||
ssh root@103.80.87.247 'df -h'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Важные замечания
|
||||
|
||||
1. **Безопасность MongoDB:**
|
||||
- Настройте аутентификацию MongoDB (см. DEPLOYMENT_GUIDE.md)
|
||||
- Используйте firewall для ограничения доступа к порту 27017
|
||||
- Регулярно обновляйте MongoDB
|
||||
|
||||
2. **Переменные окружения:**
|
||||
- Никогда не коммитьте `.env` файл в git
|
||||
- Используйте надежные пароли и секретные ключи
|
||||
- JWT_SECRET должен быть случайной строкой минимум 32 символа
|
||||
|
||||
3. **Бекапы:**
|
||||
- Проверяйте успешность создания бекапов
|
||||
- Периодически проверяйте возможность восстановления
|
||||
- Храните бекапы на отдельном диске/сервере
|
||||
|
||||
4. **Обновления:**
|
||||
- Создавайте бекап перед обновлением
|
||||
- Тестируйте обновления на dev окружении
|
||||
- Читайте CHANGELOG перед обновлением
|
||||
|
||||
5. **Производительность:**
|
||||
- Мониторьте использование ресурсов
|
||||
- Настройте индексы в MongoDB для часто используемых запросов
|
||||
- Используйте Redis для кэширования (опционально)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Поддержка
|
||||
|
||||
При возникновении проблем:
|
||||
|
||||
1. **Проверьте логи:**
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
2. **Проверьте документацию:**
|
||||
- DEPLOYMENT_GUIDE.md
|
||||
- CRON_SETUP.md
|
||||
|
||||
3. **Свяжитесь с поддержкой:**
|
||||
- Telegram: https://t.me/NakamaReportbot
|
||||
- GitHub Issues: [создайте issue]
|
||||
|
||||
---
|
||||
|
||||
## 📝 Список файлов для коммита
|
||||
|
||||
Все изменения готовы к коммиту:
|
||||
|
||||
### Измененные файлы:
|
||||
- backend/server.js
|
||||
- backend/bot.js
|
||||
- backend/models/Post.js
|
||||
- backend/routes/modApp.js
|
||||
- backend/bots/serverMonitor.js
|
||||
- frontend/index.html
|
||||
- frontend/src/pages/Feed.jsx
|
||||
- frontend/src/pages/Profile.jsx
|
||||
- moderation/frontend/src/App.jsx
|
||||
|
||||
### Новые файлы:
|
||||
- Dockerfile.backend
|
||||
- Dockerfile.frontend
|
||||
- Dockerfile.moderation
|
||||
- docker-compose.yml
|
||||
- nginx.conf
|
||||
- nginx-moderation.conf
|
||||
- .dockerignore
|
||||
- backup-cron.sh
|
||||
- setup-remote-storage.sh
|
||||
- DEPLOYMENT_GUIDE.md
|
||||
- CRON_SETUP.md
|
||||
- CHANGES_SUMMARY.md
|
||||
|
||||
---
|
||||
|
||||
**Версия:** 2.2.0
|
||||
**Дата:** 20 ноября 2025
|
||||
**Автор:** AI Assistant (Claude Sonnet 4.5)
|
||||
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
# ⏰ Настройка автоматических бекапов через Cron
|
||||
|
||||
## 📋 Инструкция
|
||||
|
||||
### 1. Подключитесь к удаленному серверу
|
||||
|
||||
```bash
|
||||
ssh root@103.80.87.247
|
||||
```
|
||||
|
||||
### 2. Установите необходимые инструменты
|
||||
|
||||
```bash
|
||||
# Убедитесь, что mongo-tools установлены
|
||||
apt update
|
||||
apt install -y mongodb-database-tools
|
||||
```
|
||||
|
||||
### 3. Скопируйте скрипт бекапа на сервер
|
||||
|
||||
С вашего локального компьютера:
|
||||
|
||||
```bash
|
||||
scp backup-cron.sh root@103.80.87.247:/usr/local/bin/
|
||||
```
|
||||
|
||||
Или создайте файл напрямую на сервере:
|
||||
|
||||
```bash
|
||||
nano /usr/local/bin/backup-cron.sh
|
||||
# Вставьте содержимое из backup-cron.sh
|
||||
```
|
||||
|
||||
### 4. Сделайте скрипт исполняемым
|
||||
|
||||
```bash
|
||||
chmod +x /usr/local/bin/backup-cron.sh
|
||||
```
|
||||
|
||||
### 5. Настройте cron
|
||||
|
||||
```bash
|
||||
# Откройте crontab для редактирования
|
||||
crontab -e
|
||||
```
|
||||
|
||||
### 6. Добавьте задачи в crontab
|
||||
|
||||
Выберите один из вариантов:
|
||||
|
||||
#### Вариант 1: Еженедельные бекапы (воскресенье в 3:00 ночи)
|
||||
|
||||
```cron
|
||||
# Еженедельный бекап базы данных Nakama
|
||||
0 3 * * 0 /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
#### Вариант 2: Ежедневные бекапы (каждый день в 3:00 ночи)
|
||||
|
||||
```cron
|
||||
# Ежедневный бекап базы данных Nakama
|
||||
0 3 * * * /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
#### Вариант 3: Несколько бекапов в неделю (пн, ср, пт в 3:00)
|
||||
|
||||
```cron
|
||||
# Бекапы по понедельникам, средам и пятницам
|
||||
0 3 * * 1,3,5 /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
### 7. Проверьте настройку cron
|
||||
|
||||
```bash
|
||||
# Просмотреть текущие задачи cron
|
||||
crontab -l
|
||||
|
||||
# Проверить статус службы cron
|
||||
systemctl status cron
|
||||
```
|
||||
|
||||
### 8. Тестирование
|
||||
|
||||
Запустите бекап вручную:
|
||||
|
||||
```bash
|
||||
/usr/local/bin/backup-cron.sh
|
||||
```
|
||||
|
||||
Проверьте созданные бекапы:
|
||||
|
||||
```bash
|
||||
ls -lh /mnt/nakama-backups/
|
||||
```
|
||||
|
||||
Просмотрите лог:
|
||||
|
||||
```bash
|
||||
tail -50 /var/log/nakama-backup.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Синтаксис Cron
|
||||
|
||||
```
|
||||
* * * * * команда
|
||||
│ │ │ │ │
|
||||
│ │ │ │ └─── День недели (0-7, где 0 и 7 = воскресенье)
|
||||
│ │ │ └───── Месяц (1-12)
|
||||
│ │ └─────── День месяца (1-31)
|
||||
│ └───────── Час (0-23)
|
||||
└─────────── Минута (0-59)
|
||||
```
|
||||
|
||||
### Примеры расписаний
|
||||
|
||||
| Расписание | Синтаксис Cron | Описание |
|
||||
|-----------|----------------|----------|
|
||||
| Каждую минуту | `* * * * *` | Выполняется каждую минуту |
|
||||
| Каждый час | `0 * * * *` | Выполняется в начале каждого часа |
|
||||
| Раз в день (в полночь) | `0 0 * * *` | Выполняется в 00:00 каждый день |
|
||||
| Раз в день (в 3:00) | `0 3 * * *` | Выполняется в 03:00 каждый день |
|
||||
| Раз в неделю (воскресенье) | `0 3 * * 0` | Выполняется в воскресенье в 03:00 |
|
||||
| Раз в месяц | `0 0 1 * *` | Выполняется 1-го числа каждого месяца в 00:00 |
|
||||
| Каждые 6 часов | `0 */6 * * *` | Выполняется в 00:00, 06:00, 12:00, 18:00 |
|
||||
| Рабочие дни в 9:00 | `0 9 * * 1-5` | Выполняется пн-пт в 09:00 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Настройка уведомлений (опционально)
|
||||
|
||||
Чтобы получать уведомления о статусе бекапов в Telegram:
|
||||
|
||||
### 1. Создайте бота для уведомлений
|
||||
|
||||
1. Напишите [@BotFather](https://t.me/BotFather) в Telegram
|
||||
2. Отправьте команду `/newbot`
|
||||
3. Следуйте инструкциям
|
||||
4. Скопируйте токен бота
|
||||
|
||||
### 2. Получите свой Chat ID
|
||||
|
||||
1. Напишите боту [@userinfobot](https://t.me/userinfobot)
|
||||
2. Скопируйте свой Chat ID
|
||||
|
||||
### 3. Обновите скрипт бекапа
|
||||
|
||||
Откройте `/usr/local/bin/backup-cron.sh` и раскомментируйте последние строки:
|
||||
|
||||
```bash
|
||||
# В конце скрипта найдите и раскомментируйте:
|
||||
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/sendMessage" \
|
||||
-d "chat_id=<YOUR_CHAT_ID>" \
|
||||
-d "text=✅ Резервная копия Nakama успешно создана: ${BACKUP_NAME}.tar.gz (${BACKUP_SIZE})"
|
||||
```
|
||||
|
||||
Замените:
|
||||
- `<YOUR_BOT_TOKEN>` на токен вашего бота
|
||||
- `<YOUR_CHAT_ID>` на ваш Chat ID
|
||||
|
||||
---
|
||||
|
||||
## 📝 Полезные команды
|
||||
|
||||
### Управление cron
|
||||
|
||||
```bash
|
||||
# Открыть crontab для редактирования
|
||||
crontab -e
|
||||
|
||||
# Показать текущие задачи
|
||||
crontab -l
|
||||
|
||||
# Удалить все задачи
|
||||
crontab -r
|
||||
|
||||
# Открыть crontab другого пользователя (требуются права root)
|
||||
crontab -u username -e
|
||||
```
|
||||
|
||||
### Просмотр логов
|
||||
|
||||
```bash
|
||||
# Показать последние 50 строк лога бекапов
|
||||
tail -50 /var/log/nakama-backup.log
|
||||
|
||||
# Следить за логом в реальном времени
|
||||
tail -f /var/log/nakama-backup.log
|
||||
|
||||
# Показать все ошибки в логе
|
||||
grep -i error /var/log/nakama-backup.log
|
||||
|
||||
# Показать системный лог cron
|
||||
grep CRON /var/log/syslog
|
||||
```
|
||||
|
||||
### Управление бекапами
|
||||
|
||||
```bash
|
||||
# Список всех бекапов
|
||||
ls -lht /mnt/nakama-backups/
|
||||
|
||||
# Размер директории с бекапами
|
||||
du -sh /mnt/nakama-backups/
|
||||
|
||||
# Удалить бекапы старше 30 дней
|
||||
find /mnt/nakama-backups/ -name "nakama_backup_*.tar.gz" -type f -mtime +30 -delete
|
||||
|
||||
# Подсчитать количество бекапов
|
||||
ls -1 /mnt/nakama-backups/ | wc -l
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Важные замечания
|
||||
|
||||
1. **Время выполнения**: Убедитесь, что время в cron указано в часовом поясе сервера. Проверьте:
|
||||
```bash
|
||||
timedatectl
|
||||
```
|
||||
|
||||
2. **Права доступа**: Убедитесь, что у пользователя, под которым запускается cron, есть права на запись в директорию бекапов.
|
||||
|
||||
3. **Место на диске**: Регулярно проверяйте свободное место:
|
||||
```bash
|
||||
df -h /mnt/nakama-backups/
|
||||
```
|
||||
|
||||
4. **Ротация логов**: Настройте logrotate для `/var/log/nakama-backup.log`:
|
||||
```bash
|
||||
nano /etc/logrotate.d/nakama-backup
|
||||
```
|
||||
|
||||
Добавьте:
|
||||
```
|
||||
/var/log/nakama-backup.log {
|
||||
weekly
|
||||
rotate 4
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
}
|
||||
```
|
||||
|
||||
5. **Мониторинг**: Регулярно проверяйте, что бекапы создаются успешно:
|
||||
```bash
|
||||
ls -lt /mnt/nakama-backups/ | head
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Восстановление из бекапа
|
||||
|
||||
Если понадобится восстановить базу данных:
|
||||
|
||||
```bash
|
||||
# 1. Перейти в директорию с бекапами
|
||||
cd /mnt/nakama-backups/
|
||||
|
||||
# 2. Найти нужный бекап
|
||||
ls -lt | grep nakama_backup
|
||||
|
||||
# 3. Распаковать бекап
|
||||
tar -xzf nakama_backup_YYYY-MM-DD_HH-MM-SS.tar.gz
|
||||
|
||||
# 4. Восстановить базу данных
|
||||
mongorestore --uri="mongodb://localhost:27017" \
|
||||
--drop \
|
||||
--gzip \
|
||||
--db nakama \
|
||||
nakama_backup_YYYY-MM-DD_HH-MM-SS/nakama/
|
||||
|
||||
# 5. Удалить распакованную директорию
|
||||
rm -rf nakama_backup_YYYY-MM-DD_HH-MM-SS/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Помощь
|
||||
|
||||
Если у вас возникли проблемы с настройкой cron:
|
||||
|
||||
1. Проверьте логи: `tail -f /var/log/nakama-backup.log`
|
||||
2. Проверьте системный лог: `grep CRON /var/log/syslog`
|
||||
3. Убедитесь, что cron запущен: `systemctl status cron`
|
||||
4. Запустите скрипт вручную для отладки: `/usr/local/bin/backup-cron.sh`
|
||||
|
||||
|
|
@ -0,0 +1,477 @@
|
|||
# 🚀 Руководство по развертыванию Nakama
|
||||
|
||||
## 📋 Содержание
|
||||
1. [Требования](#требования)
|
||||
2. [Настройка удаленного сервера](#настройка-удаленного-сервера)
|
||||
3. [Настройка базы данных](#настройка-базы-данных)
|
||||
4. [Настройка автоматических бекапов](#настройка-автоматических-бекапов)
|
||||
5. [Развертывание с Docker](#развертывание-с-docker)
|
||||
6. [Настройка nginx](#настройка-nginx)
|
||||
7. [Мониторинг и обслуживание](#мониторинг-и-обслуживание)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Требования
|
||||
|
||||
### Локальный сервер (где запускается приложение)
|
||||
- Docker 20.10+
|
||||
- Docker Compose 2.0+
|
||||
- SSHFS (для монтирования удаленных директорий)
|
||||
- 2GB+ RAM
|
||||
- 10GB+ свободного места на диске
|
||||
|
||||
### Удаленный сервер (103.80.87.247)
|
||||
- SSH доступ
|
||||
- MongoDB 7+
|
||||
- 50GB+ свободного места для базы данных и медиа
|
||||
- Открытый порт 27017 для MongoDB
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Настройка удаленного сервера
|
||||
|
||||
### 1. Подключение по SSH
|
||||
|
||||
```bash
|
||||
ssh root@103.80.87.247
|
||||
```
|
||||
|
||||
### 2. Установка MongoDB
|
||||
|
||||
```bash
|
||||
# Обновить систему
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Импортировать GPG ключ MongoDB
|
||||
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
|
||||
sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
|
||||
|
||||
# Добавить репозиторий MongoDB
|
||||
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
|
||||
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
|
||||
|
||||
# Установить MongoDB
|
||||
apt update
|
||||
apt install -y mongodb-org
|
||||
|
||||
# Запустить MongoDB
|
||||
systemctl start mongod
|
||||
systemctl enable mongod
|
||||
```
|
||||
|
||||
### 3. Настройка MongoDB для удаленного доступа
|
||||
|
||||
```bash
|
||||
# Редактировать конфигурацию
|
||||
nano /etc/mongod.conf
|
||||
```
|
||||
|
||||
Изменить `bindIp`:
|
||||
```yaml
|
||||
net:
|
||||
port: 27017
|
||||
bindIp: 0.0.0.0 # Разрешить подключения со всех IP
|
||||
```
|
||||
|
||||
Настроить аутентификацию (рекомендуется):
|
||||
```bash
|
||||
# Подключиться к MongoDB
|
||||
mongosh
|
||||
|
||||
# Создать администратора
|
||||
use admin
|
||||
db.createUser({
|
||||
user: "admin",
|
||||
pwd: "your_secure_password",
|
||||
roles: [ { role: "root", db: "admin" } ]
|
||||
})
|
||||
|
||||
# Создать пользователя для приложения
|
||||
use nakama
|
||||
db.createUser({
|
||||
user: "nakama_user",
|
||||
pwd: "your_app_password",
|
||||
roles: [ { role: "readWrite", db: "nakama" } ]
|
||||
})
|
||||
```
|
||||
|
||||
Перезапустить MongoDB:
|
||||
```bash
|
||||
systemctl restart mongod
|
||||
```
|
||||
|
||||
### 4. Создание директорий для хранения данных
|
||||
|
||||
```bash
|
||||
# Создать директории
|
||||
mkdir -p /var/nakama/media
|
||||
mkdir -p /var/nakama/db
|
||||
mkdir -p /var/nakama/backups
|
||||
|
||||
# Установить права доступа
|
||||
chmod -R 755 /var/nakama
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 Настройка базы данных
|
||||
|
||||
### Строка подключения
|
||||
|
||||
Обновите `.env` файл:
|
||||
|
||||
```env
|
||||
# Без аутентификации
|
||||
MONGODB_URI=mongodb://103.80.87.247:27017/nakama
|
||||
|
||||
# С аутентификацией
|
||||
MONGODB_URI=mongodb://nakama_user:your_app_password@103.80.87.247:27017/nakama
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Настройка автоматических бекапов
|
||||
|
||||
### 1. Установка cron на удаленном сервере
|
||||
|
||||
```bash
|
||||
# Скопировать скрипт бекапа на удаленный сервер
|
||||
scp backup-cron.sh root@103.80.87.247:/usr/local/bin/
|
||||
|
||||
# Подключиться к удаленному серверу
|
||||
ssh root@103.80.87.247
|
||||
|
||||
# Сделать скрипт исполняемым
|
||||
chmod +x /usr/local/bin/backup-cron.sh
|
||||
|
||||
# Открыть crontab
|
||||
crontab -e
|
||||
```
|
||||
|
||||
### 2. Добавить задачу в crontab
|
||||
|
||||
Добавьте следующую строку для запуска бекапа каждое воскресенье в 3:00 ночи:
|
||||
|
||||
```cron
|
||||
0 3 * * 0 /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
Для ежедневных бекапов в 3:00 ночи:
|
||||
```cron
|
||||
0 3 * * * /usr/local/bin/backup-cron.sh >> /var/log/nakama-backup.log 2>&1
|
||||
```
|
||||
|
||||
### 3. Проверка работы бекапов
|
||||
|
||||
```bash
|
||||
# Запустить бекап вручную
|
||||
/usr/local/bin/backup-cron.sh
|
||||
|
||||
# Проверить созданные бекапы
|
||||
ls -lh /var/nakama/backups/
|
||||
|
||||
# Просмотреть лог
|
||||
tail -f /var/log/nakama-backup.log
|
||||
```
|
||||
|
||||
### 4. Восстановление из бекапа
|
||||
|
||||
```bash
|
||||
# Распаковать бекап
|
||||
cd /var/nakama/backups
|
||||
tar -xzf nakama_backup_YYYY-MM-DD_HH-MM-SS.tar.gz
|
||||
|
||||
# Восстановить базу данных
|
||||
mongorestore --uri="mongodb://103.80.87.247:27017" --gzip --db nakama nakama_backup_YYYY-MM-DD_HH-MM-SS/nakama/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐳 Развертывание с Docker
|
||||
|
||||
### 1. Подготовка
|
||||
|
||||
```bash
|
||||
cd /Users/glpshchn/Desktop/nakama
|
||||
|
||||
# Скопировать и настроить .env
|
||||
cp .env.example .env
|
||||
nano .env # Заполнить необходимые переменные
|
||||
```
|
||||
|
||||
### 2. Монтирование удаленных директорий (опционально)
|
||||
|
||||
Если вы хотите хранить медиа и бекапы на удаленном сервере:
|
||||
|
||||
```bash
|
||||
# Запустить скрипт настройки
|
||||
./setup-remote-storage.sh
|
||||
```
|
||||
|
||||
### 3. Сборка и запуск контейнеров
|
||||
|
||||
```bash
|
||||
# Собрать все сервисы
|
||||
docker-compose build
|
||||
|
||||
# Запустить в фоновом режиме
|
||||
docker-compose up -d
|
||||
|
||||
# Проверить статус
|
||||
docker-compose ps
|
||||
|
||||
# Просмотреть логи
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
### 4. Проверка работы
|
||||
|
||||
```bash
|
||||
# Проверить backend
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Проверить frontend
|
||||
curl http://localhost:5173
|
||||
|
||||
# Проверить moderation
|
||||
curl http://localhost:5174
|
||||
```
|
||||
|
||||
### 5. Остановка и обновление
|
||||
|
||||
```bash
|
||||
# Остановить все сервисы
|
||||
docker-compose down
|
||||
|
||||
# Пересобрать и перезапустить
|
||||
docker-compose up -d --build
|
||||
|
||||
# Перезапустить конкретный сервис
|
||||
docker-compose restart backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Настройка nginx (reverse proxy)
|
||||
|
||||
### Конфигурация для production
|
||||
|
||||
Создайте файл `/etc/nginx/sites-available/nakama`:
|
||||
|
||||
```nginx
|
||||
# Frontend (основное приложение)
|
||||
server {
|
||||
listen 80;
|
||||
server_name nakama.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5173;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
# Moderation (система модерации)
|
||||
server {
|
||||
listen 80;
|
||||
server_name mod.nakama.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5174;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
# Backend API
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.nakama.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
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_cache_bypass $http_upgrade;
|
||||
|
||||
# WebSocket support
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Активировать конфигурацию:
|
||||
|
||||
```bash
|
||||
# Создать символическую ссылку
|
||||
ln -s /etc/nginx/sites-available/nakama /etc/nginx/sites-enabled/
|
||||
|
||||
# Проверить конфигурацию
|
||||
nginx -t
|
||||
|
||||
# Перезапустить nginx
|
||||
systemctl restart nginx
|
||||
```
|
||||
|
||||
### SSL сертификаты (Let's Encrypt)
|
||||
|
||||
```bash
|
||||
# Установить certbot
|
||||
apt install certbot python3-certbot-nginx
|
||||
|
||||
# Получить сертификаты
|
||||
certbot --nginx -d nakama.yourdomain.com -d mod.nakama.yourdomain.com -d api.nakama.yourdomain.com
|
||||
|
||||
# Автоматическое обновление
|
||||
certbot renew --dry-run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Мониторинг и обслуживание
|
||||
|
||||
### Просмотр логов
|
||||
|
||||
```bash
|
||||
# Логи backend
|
||||
docker-compose logs -f backend
|
||||
|
||||
# Логи всех сервисов
|
||||
docker-compose logs -f
|
||||
|
||||
# Логи MongoDB на удаленном сервере
|
||||
ssh root@103.80.87.247 'tail -f /var/log/mongodb/mongod.log'
|
||||
```
|
||||
|
||||
### Мониторинг ресурсов
|
||||
|
||||
```bash
|
||||
# Использование Docker контейнеров
|
||||
docker stats
|
||||
|
||||
# Использование диска
|
||||
df -h
|
||||
|
||||
# Статус MongoDB (на удаленном сервере)
|
||||
ssh root@103.80.87.247 'systemctl status mongod'
|
||||
```
|
||||
|
||||
### Обновление приложения
|
||||
|
||||
```bash
|
||||
# 1. Создать бекап
|
||||
./backup-cron.sh
|
||||
|
||||
# 2. Получить обновления
|
||||
git pull
|
||||
|
||||
# 3. Остановить контейнеры
|
||||
docker-compose down
|
||||
|
||||
# 4. Пересобрать и запустить
|
||||
docker-compose up -d --build
|
||||
|
||||
# 5. Проверить работу
|
||||
docker-compose ps
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
### Очистка
|
||||
|
||||
```bash
|
||||
# Удалить неиспользуемые образы
|
||||
docker image prune -a
|
||||
|
||||
# Удалить неиспользуемые volumes
|
||||
docker volume prune
|
||||
|
||||
# Полная очистка Docker
|
||||
docker system prune -a --volumes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Решение проблем
|
||||
|
||||
### База данных недоступна
|
||||
|
||||
```bash
|
||||
# Проверить подключение к MongoDB
|
||||
mongosh --host 103.80.87.247 --port 27017
|
||||
|
||||
# Проверить статус MongoDB на удаленном сервере
|
||||
ssh root@103.80.87.247 'systemctl status mongod'
|
||||
|
||||
# Проверить логи
|
||||
ssh root@103.80.87.247 'tail -100 /var/log/mongodb/mongod.log'
|
||||
```
|
||||
|
||||
### Контейнеры не запускаются
|
||||
|
||||
```bash
|
||||
# Проверить логи
|
||||
docker-compose logs
|
||||
|
||||
# Пересоздать контейнеры
|
||||
docker-compose down -v
|
||||
docker-compose up -d --force-recreate
|
||||
```
|
||||
|
||||
### Проблемы с медиа
|
||||
|
||||
```bash
|
||||
# Проверить монтирование директорий
|
||||
df -h | grep nakama
|
||||
|
||||
# Проверить права доступа
|
||||
ls -la /mnt/nakama-media
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Дополнительные команды
|
||||
|
||||
### Создание ручного бекапа
|
||||
|
||||
```bash
|
||||
docker-compose exec mongodb mongodump --uri="mongodb://103.80.87.247:27017/nakama" --out=/backups/manual_backup
|
||||
```
|
||||
|
||||
### Экспорт/импорт данных
|
||||
|
||||
```bash
|
||||
# Экспорт коллекции
|
||||
docker-compose exec mongodb mongoexport --uri="mongodb://103.80.87.247:27017/nakama" --collection=posts --out=/backups/posts.json
|
||||
|
||||
# Импорт коллекции
|
||||
docker-compose exec mongodb mongoimport --uri="mongodb://103.80.87.247:27017/nakama" --collection=posts --file=/backups/posts.json
|
||||
```
|
||||
|
||||
### Масштабирование
|
||||
|
||||
Для горизонтального масштабирования backend:
|
||||
|
||||
```bash
|
||||
docker-compose up -d --scale backend=3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Поддержка
|
||||
|
||||
При возникновении проблем:
|
||||
1. Проверьте логи: `docker-compose logs -f`
|
||||
2. Проверьте статус сервисов: `docker-compose ps`
|
||||
3. Создайте issue на GitHub или напишите в https://t.me/NakamaReportbot
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Установка зависимостей
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Копирование backend кода
|
||||
COPY backend ./backend
|
||||
|
||||
# Создание директории для uploads
|
||||
RUN mkdir -p backend/uploads/posts backend/uploads/mod-channel
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Переменные окружения
|
||||
ENV NODE_ENV=production
|
||||
|
||||
CMD ["node", "backend/server.js"]
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Установка зависимостей
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Копирование исходников
|
||||
COPY frontend ./
|
||||
|
||||
# Сборка проекта
|
||||
ARG VITE_API_URL
|
||||
ENV VITE_API_URL=$VITE_API_URL
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
# Копирование собранного приложения
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Копирование конфигурации nginx
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Установка зависимостей
|
||||
COPY moderation/frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Копирование исходников
|
||||
COPY moderation/frontend ./
|
||||
|
||||
# Сборка проекта
|
||||
ARG VITE_API_URL
|
||||
ENV VITE_API_URL=$VITE_API_URL
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
# Копирование собранного приложения
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Копирование конфигурации nginx
|
||||
COPY nginx-moderation.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ async function sendPhotosToUser(userId, photos) {
|
|||
media.push({
|
||||
type: isVideo ? 'video' : 'photo',
|
||||
media: photoUrl,
|
||||
caption: index === 0 ? `<b>Из NakamaHost</b>\n${batch.length} фото` : undefined,
|
||||
caption: index === 0 ? `<b>Из Nakama</b>\n${batch.length} фото` : undefined,
|
||||
parse_mode: 'HTML',
|
||||
...(isVideo ? { supports_streaming: true } : {})
|
||||
});
|
||||
|
|
@ -224,7 +224,7 @@ async function sendPhotosToUser(userId, photos) {
|
|||
media.push({
|
||||
type: isVideo ? 'video' : 'photo',
|
||||
media: photoUrl,
|
||||
caption: index === 0 ? `<b>Из NakamaHost</b>\n${batch.length} фото` : undefined,
|
||||
caption: index === 0 ? `<b>Из Nakama</b>\n${batch.length} фото` : undefined,
|
||||
parse_mode: 'HTML',
|
||||
...(isVideo ? { supports_streaming: true } : {})
|
||||
});
|
||||
|
|
@ -252,7 +252,7 @@ async function handleWebAppData(userId, dataString) {
|
|||
const data = JSON.parse(dataString);
|
||||
|
||||
if (data.action === 'send_image') {
|
||||
const caption = `<b>Из NakamaHost</b>\n\n${data.caption || ''}`;
|
||||
const caption = `<b>Из Nakama</b>\n\n${data.caption || ''}`;
|
||||
await sendPhotoToUser(userId, data.url, caption);
|
||||
return { success: true, message: 'Изображение отправлено!' };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -376,9 +376,13 @@ const sendChannelMediaGroup = async (files, caption) => {
|
|||
});
|
||||
|
||||
try {
|
||||
await axios.post(`${TELEGRAM_API}/sendMediaGroup`, form, {
|
||||
const response = await axios.post(`${TELEGRAM_API}/sendMediaGroup`, form, {
|
||||
headers: form.getHeaders()
|
||||
});
|
||||
|
||||
// Вернуть ID первого сообщения из группы
|
||||
const messageId = response.data?.result?.[0]?.message_id;
|
||||
return messageId;
|
||||
} catch (error) {
|
||||
log('error', 'Не удалось отправить медиа-группу в канал', { error: error.response?.data || error.message });
|
||||
throw error;
|
||||
|
|
@ -410,10 +414,41 @@ const sendMessageToUser = async (userId, message) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Обновить сообщение в канале (только текст)
|
||||
*/
|
||||
const updateChannelMessage = async (messageId, content, hashtags) => {
|
||||
if (!TELEGRAM_API) {
|
||||
throw new Error('Бот модерации не инициализирован');
|
||||
}
|
||||
|
||||
const chatId = config.moderationChannelUsername || '@reichenbfurry';
|
||||
|
||||
// Формируем текст с хэштегами
|
||||
const text = [content, hashtags?.length ? hashtags.map(tag => `#${tag}`).join(' ') : ''].filter(Boolean).join('\n\n');
|
||||
|
||||
try {
|
||||
await axios.post(`${TELEGRAM_API}/editMessageCaption`, {
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
caption: `${text}${ERROR_SUPPORT_SUFFIX}`,
|
||||
parse_mode: 'HTML'
|
||||
});
|
||||
log('info', 'Сообщение в канале обновлено', { messageId });
|
||||
} catch (error) {
|
||||
log('error', 'Не удалось обновить сообщение в канале', {
|
||||
messageId,
|
||||
error: error.response?.data || error.message
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
startServerMonitorBot,
|
||||
sendChannelMediaGroup,
|
||||
sendMessageToUser,
|
||||
updateChannelMessage,
|
||||
isModerationAdmin
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,20 @@ const PostSchema = new mongoose.Schema({
|
|||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// Поля для постов из канала
|
||||
publishedToChannel: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
channelMessageId: {
|
||||
type: Number
|
||||
},
|
||||
adminNumber: {
|
||||
type: Number
|
||||
},
|
||||
editedAt: {
|
||||
type: Date
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
|
|
|
|||
|
|
@ -191,6 +191,9 @@ router.get('/posts', authenticateModeration, requireModerationAccess, async (req
|
|||
commentsCount: post.comments?.length || 0,
|
||||
likesCount: post.likes?.length || 0,
|
||||
isNSFW: post.isNSFW,
|
||||
publishedToChannel: post.publishedToChannel,
|
||||
adminNumber: post.adminNumber,
|
||||
editedAt: post.editedAt,
|
||||
createdAt: post.createdAt
|
||||
}));
|
||||
|
||||
|
|
@ -205,11 +208,29 @@ router.get('/posts', authenticateModeration, requireModerationAccess, async (req
|
|||
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { content, hashtags, tags, isNSFW } = req.body;
|
||||
|
||||
const post = await Post.findById(req.params.id);
|
||||
const post = await Post.findById(req.params.id).populate('author');
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Пост не найден' });
|
||||
}
|
||||
|
||||
// Проверить, может ли админ редактировать этот пост
|
||||
// Админ может редактировать:
|
||||
// 1. Любой пост, если он владелец (req.isOwner)
|
||||
// 2. Только свои посты из канала (где adminNumber совпадает)
|
||||
if (!req.isOwner) {
|
||||
// Получить админа текущего пользователя
|
||||
const admin = await ModerationAdmin.findOne({ telegramId: req.user.telegramId });
|
||||
|
||||
// Если это пост из канала, проверить, что админ - автор
|
||||
if (post.publishedToChannel && post.adminNumber) {
|
||||
if (!admin || admin.adminNumber !== post.adminNumber) {
|
||||
return res.status(403).json({ error: 'Вы можете редактировать только свои посты из канала' });
|
||||
}
|
||||
}
|
||||
// Если это обычный пост, владелец может редактировать любой, остальные админы - нет
|
||||
// (это поведение можно изменить по необходимости)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
post.content = content;
|
||||
post.hashtags = Array.isArray(hashtags)
|
||||
|
|
@ -230,6 +251,19 @@ router.put('/posts/:id', authenticateModeration, requireModerationAccess, async
|
|||
|
||||
await post.populate('author', 'username firstName lastName role banned bannedUntil');
|
||||
|
||||
// Если пост был опубликован в канале, обновить его там
|
||||
if (post.publishedToChannel && post.channelMessageId) {
|
||||
try {
|
||||
const { updateChannelMessage } = require('../bots/serverMonitor');
|
||||
if (updateChannelMessage) {
|
||||
await updateChannelMessage(post.channelMessageId, post.content, post.hashtags);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Не удалось обновить сообщение в канале:', error);
|
||||
// Продолжаем выполнение, даже если не удалось обновить в канале
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
post: {
|
||||
id: post._id,
|
||||
|
|
@ -239,6 +273,8 @@ router.put('/posts/:id', authenticateModeration, requireModerationAccess, async
|
|||
tags: post.tags,
|
||||
images: post.images,
|
||||
isNSFW: post.isNSFW,
|
||||
publishedToChannel: post.publishedToChannel,
|
||||
adminNumber: post.adminNumber,
|
||||
editedAt: post.editedAt,
|
||||
createdAt: post.createdAt
|
||||
}
|
||||
|
|
@ -688,8 +724,25 @@ router.post(
|
|||
const caption = captionLines.join('\n');
|
||||
|
||||
try {
|
||||
await sendChannelMediaGroup(files, caption);
|
||||
res.json({ success: true });
|
||||
// Отправить в канал и получить message_id
|
||||
const messageId = await sendChannelMediaGroup(files, caption);
|
||||
|
||||
// Создать пост в базе данных
|
||||
const newPost = new Post({
|
||||
author: req.user._id,
|
||||
content: description,
|
||||
hashtags: tagsArray.map(tag => tag.replace('#', '').toLowerCase()),
|
||||
images: [], // Медиа хранится в Telegram
|
||||
tags: ['other'], // Можно настроить определение типа контента
|
||||
publishedToChannel: true,
|
||||
channelMessageId: messageId,
|
||||
adminNumber: slotNumber,
|
||||
isNSFW: false
|
||||
});
|
||||
|
||||
await newPost.save();
|
||||
|
||||
res.json({ success: true, postId: newPost._id, messageId });
|
||||
} catch (error) {
|
||||
logSecurityEvent('CHANNEL_PUBLISH_FAILED', req, { error: error.message });
|
||||
res.status(500).json({ error: 'Не удалось опубликовать в канал' });
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ app.use('/api/mod-app', require('./routes/modApp'));
|
|||
|
||||
// Базовый роут
|
||||
app.get('/', (req, res) => {
|
||||
res.json({ message: 'NakamaHost API работает' });
|
||||
res.json({ message: 'Nakama API работает' });
|
||||
});
|
||||
|
||||
// 404 handler
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для автоматического резервного копирования MongoDB
|
||||
# Запускается раз в неделю через cron
|
||||
|
||||
# Цвета для вывода
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Конфигурация
|
||||
BACKUP_DIR="/mnt/nakama-backups"
|
||||
MONGODB_URI="mongodb://103.80.87.247:27017/nakama"
|
||||
DB_NAME="nakama"
|
||||
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
|
||||
BACKUP_NAME="nakama_backup_${DATE}"
|
||||
RETENTION_DAYS=30
|
||||
|
||||
echo -e "${GREEN}=== Начало резервного копирования ===${NC}"
|
||||
echo "Дата: $(date)"
|
||||
echo "База данных: ${DB_NAME}"
|
||||
echo "Директория бекапов: ${BACKUP_DIR}"
|
||||
|
||||
# Создать директорию для бекапов, если она не существует
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
|
||||
# Выполнить mongodump
|
||||
echo -e "${YELLOW}Создание резервной копии...${NC}"
|
||||
if mongodump --uri="${MONGODB_URI}" --db="${DB_NAME}" --out="${BACKUP_DIR}/${BACKUP_NAME}" --gzip; then
|
||||
echo -e "${GREEN}✓ Резервная копия успешно создана${NC}"
|
||||
|
||||
# Создать архив
|
||||
echo -e "${YELLOW}Создание архива...${NC}"
|
||||
cd "${BACKUP_DIR}" || exit 1
|
||||
if tar -czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}"; then
|
||||
echo -e "${GREEN}✓ Архив создан: ${BACKUP_NAME}.tar.gz${NC}"
|
||||
|
||||
# Удалить временную директорию
|
||||
rm -rf "${BACKUP_NAME}"
|
||||
|
||||
# Получить размер архива
|
||||
BACKUP_SIZE=$(du -h "${BACKUP_NAME}.tar.gz" | cut -f1)
|
||||
echo -e "${GREEN}Размер архива: ${BACKUP_SIZE}${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Ошибка создания архива${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Ошибка создания резервной копии${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Удалить старые бекапы (старше RETENTION_DAYS дней)
|
||||
echo -e "${YELLOW}Удаление старых бекапов (старше ${RETENTION_DAYS} дней)...${NC}"
|
||||
find "${BACKUP_DIR}" -name "nakama_backup_*.tar.gz" -type f -mtime +${RETENTION_DAYS} -delete
|
||||
REMAINING_BACKUPS=$(find "${BACKUP_DIR}" -name "nakama_backup_*.tar.gz" -type f | wc -l)
|
||||
echo -e "${GREEN}Оставшихся бекапов: ${REMAINING_BACKUPS}${NC}"
|
||||
|
||||
# Вывести список последних бекапов
|
||||
echo -e "${YELLOW}Последние 5 бекапов:${NC}"
|
||||
ls -lht "${BACKUP_DIR}"/nakama_backup_*.tar.gz | head -5
|
||||
|
||||
echo -e "${GREEN}=== Резервное копирование завершено ===${NC}"
|
||||
echo ""
|
||||
|
||||
# Отправить уведомление (опционально)
|
||||
# Раскомментируйте, если хотите получать уведомления
|
||||
# curl -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
# -d "chat_id=${YOUR_CHAT_ID}" \
|
||||
# -d "text=✅ Резервная копия Nakama успешно создана: ${BACKUP_NAME}.tar.gz (${BACKUP_SIZE})"
|
||||
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.backend
|
||||
container_name: nakama-backend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- MONGODB_URI=${MONGODB_URI:-mongodb://103.80.87.247:27017/nakama}
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}
|
||||
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
|
||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- MODERATION_BOT_TOKEN=${MODERATION_BOT_TOKEN}
|
||||
- MODERATION_OWNER_USERNAMES=${MODERATION_OWNER_USERNAMES:-glpshchn00}
|
||||
- MODERATION_CHANNEL_USERNAME=${MODERATION_CHANNEL_USERNAME:-@reichenbfurry}
|
||||
- GELBOORU_API_KEY=${GELBOORU_API_KEY}
|
||||
- GELBOORU_USER_ID=${GELBOORU_USER_ID}
|
||||
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:5173}
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-*}
|
||||
- REDIS_URL=${REDIS_URL}
|
||||
volumes:
|
||||
# Медиа хранится на удаленном сервере, монтируем через NFS или SSH
|
||||
- /mnt/nakama-media:/app/backend/uploads
|
||||
networks:
|
||||
- nakama-network
|
||||
depends_on:
|
||||
- mongodb
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.frontend
|
||||
args:
|
||||
VITE_API_URL: ${VITE_API_URL:-http://localhost:3000/api}
|
||||
container_name: nakama-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5173:80"
|
||||
networks:
|
||||
- nakama-network
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
moderation:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.moderation
|
||||
args:
|
||||
VITE_API_URL: ${VITE_API_URL:-http://localhost:3000/api}
|
||||
container_name: nakama-moderation
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5174:80"
|
||||
networks:
|
||||
- nakama-network
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
mongodb:
|
||||
image: mongo:7
|
||||
container_name: nakama-mongodb
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "27017:27017"
|
||||
environment:
|
||||
- MONGO_INITDB_DATABASE=nakama
|
||||
volumes:
|
||||
# База данных на удаленном сервере
|
||||
- /mnt/nakama-db:/data/db
|
||||
networks:
|
||||
- nakama-network
|
||||
command: mongod --bind_ip_all
|
||||
|
||||
backup:
|
||||
image: mongo:7
|
||||
container_name: nakama-backup
|
||||
restart: "no"
|
||||
environment:
|
||||
- MONGODB_URI=${MONGODB_URI:-mongodb://103.80.87.247:27017/nakama}
|
||||
volumes:
|
||||
- /mnt/nakama-backups:/backups
|
||||
- ./backend/scripts/backup.js:/backup.js
|
||||
networks:
|
||||
- nakama-network
|
||||
depends_on:
|
||||
- mongodb
|
||||
entrypoint: /bin/bash
|
||||
command: -c "echo 'Backup container ready. Run manual backups or set up cron.'"
|
||||
|
||||
networks:
|
||||
nakama-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
nakama-db:
|
||||
driver: local
|
||||
nakama-media:
|
||||
driver: local
|
||||
nakama-backups:
|
||||
driver: local
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<title>NakamaHost</title>
|
||||
<title>Nakama</title>
|
||||
<!-- Telegram Web App SDK - прямая загрузка -->
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export default function Feed({ user }) {
|
|||
<div className="feed-page">
|
||||
{/* Хедер */}
|
||||
<div className="feed-header">
|
||||
<h1>NakamaHost</h1>
|
||||
<h1>Nakama</h1>
|
||||
<button className="create-btn" onClick={handleCreatePost}>
|
||||
<Plus size={20} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ export default function Profile({ user, setUser }) {
|
|||
</div>
|
||||
<div className="donation-text">
|
||||
<h3>Поддержите проект</h3>
|
||||
<p>Каждый взнос помогает развивать NakamaHost и запускать новые функции.</p>
|
||||
<p>Каждый взнос помогает развивать Nakama и запускать новые функции.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button className="donation-button" onClick={handleDonate}>
|
||||
|
|
|
|||
|
|
@ -286,14 +286,18 @@ export default function App() {
|
|||
import.meta.env.PROD ? window.location.origin : 'http://localhost:3000'
|
||||
);
|
||||
|
||||
console.log('Инициализация чата, подключение к:', API_URL);
|
||||
|
||||
const socket = io(`${API_URL}/mod-chat`, {
|
||||
transports: ['websocket', 'polling'],
|
||||
reconnection: true,
|
||||
reconnectionDelay: 1000,
|
||||
reconnectionAttempts: 5
|
||||
reconnectionAttempts: 5,
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('WebSocket подключен, отправка auth...');
|
||||
socket.emit('auth', {
|
||||
username: user.username,
|
||||
telegramId: user.telegramId
|
||||
|
|
@ -301,42 +305,57 @@ export default function App() {
|
|||
});
|
||||
|
||||
socket.on('ready', () => {
|
||||
console.log('Авторизация успешна!');
|
||||
setChatState((prev) => ({ ...prev, connected: true }));
|
||||
});
|
||||
|
||||
socket.on('unauthorized', () => {
|
||||
console.error('Unauthorized в чате');
|
||||
setChatState((prev) => ({ ...prev, connected: false }));
|
||||
socket.disconnect();
|
||||
});
|
||||
|
||||
socket.on('message', (message) => {
|
||||
console.log('Получено сообщение:', message);
|
||||
setChatState((prev) => ({
|
||||
...prev,
|
||||
messages: [...prev.messages, message]
|
||||
}));
|
||||
if (chatListRef.current) {
|
||||
chatListRef.current.scrollTo({
|
||||
top: chatListRef.current.scrollHeight,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (chatListRef.current) {
|
||||
chatListRef.current.scrollTo({
|
||||
top: chatListRef.current.scrollHeight,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
socket.on('online', (online) => {
|
||||
console.log('Обновление списка онлайн:', online);
|
||||
setChatState((prev) => ({ ...prev, online }));
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log('WebSocket отключен:', reason);
|
||||
setChatState((prev) => ({ ...prev, connected: false }));
|
||||
});
|
||||
|
||||
socket.on('connect_error', (error) => {
|
||||
console.error('Ошибка подключения WebSocket:', error);
|
||||
});
|
||||
|
||||
chatSocketRef.current = socket;
|
||||
};
|
||||
|
||||
const handleSendChat = () => {
|
||||
if (!chatSocketRef.current || !chatState.connected) return;
|
||||
if (!chatSocketRef.current || !chatState.connected) {
|
||||
console.warn('Чат не подключен');
|
||||
return;
|
||||
}
|
||||
const text = chatInput.trim();
|
||||
if (!text) return;
|
||||
console.log('Отправка сообщения:', text);
|
||||
chatSocketRef.current.emit('message', { text });
|
||||
setChatInput('');
|
||||
};
|
||||
|
|
@ -515,14 +534,24 @@ export default function App() {
|
|||
</div>
|
||||
{post.images?.length ? (
|
||||
<div className="image-grid">
|
||||
{post.images.map((img, idx) => (
|
||||
<div key={idx} className="image-thumb">
|
||||
<img src={img} alt="" />
|
||||
<button className="image-remove" onClick={() => handleRemoveImage(post.id, idx)}>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{post.images.map((img, idx) => {
|
||||
// Преобразовать относительный путь в абсолютный
|
||||
const imageUrl = img.startsWith('http')
|
||||
? img
|
||||
: `${import.meta.env.VITE_API_URL || (import.meta.env.PROD ? window.location.origin : 'http://localhost:3000')}${img}`;
|
||||
|
||||
return (
|
||||
<div key={idx} className="image-thumb">
|
||||
<img src={imageUrl} alt="" onError={(e) => {
|
||||
e.target.style.display = 'none';
|
||||
console.error('Failed to load image:', imageUrl);
|
||||
}} />
|
||||
<button className="image-remove" onClick={() => handleRemoveImage(post.id, idx)}>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
@ -569,24 +598,84 @@ export default function App() {
|
|||
</div>
|
||||
<div className="list-item-subtitle">Статус: {report.status}</div>
|
||||
<div className="report-content">
|
||||
<p>{report.reason || 'Причина не указана'}</p>
|
||||
<div style={{ marginBottom: '12px', padding: '12px', backgroundColor: 'var(--bg-secondary)', borderRadius: '8px' }}>
|
||||
<strong>Причина жалобы:</strong>
|
||||
<p style={{ marginTop: '4px' }}>{report.reason || 'Причина не указана'}</p>
|
||||
</div>
|
||||
{report.post && (
|
||||
<div className="report-post">
|
||||
<strong>Пост:</strong> {report.post.content || 'Без текста'}
|
||||
<div className="report-post" style={{ padding: '12px', backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<strong>Пост от @{report.post.author?.username || 'Удалён'}</strong>
|
||||
</div>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
{report.post.content || 'Без текста'}
|
||||
</div>
|
||||
{report.post.images?.length > 0 && (
|
||||
<div className="image-grid" style={{ marginTop: '8px' }}>
|
||||
{report.post.images.slice(0, 3).map((img, idx) => {
|
||||
const imageUrl = img.startsWith('http')
|
||||
? img
|
||||
: `${import.meta.env.VITE_API_URL || (import.meta.env.PROD ? window.location.origin : 'http://localhost:3000')}${img}`;
|
||||
|
||||
return (
|
||||
<img
|
||||
key={idx}
|
||||
src={imageUrl}
|
||||
alt=""
|
||||
style={{
|
||||
width: '80px',
|
||||
height: '80px',
|
||||
objectFit: 'cover',
|
||||
borderRadius: '4px',
|
||||
marginRight: '4px'
|
||||
}}
|
||||
onError={(e) => {
|
||||
e.target.style.display = 'none';
|
||||
console.error('Failed to load image:', imageUrl);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{report.post.images.length > 3 && (
|
||||
<span style={{ fontSize: '12px', color: 'var(--text-secondary)' }}>
|
||||
+{report.post.images.length - 3} ещё
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-item-actions">
|
||||
{report.post && (
|
||||
<>
|
||||
<button className="btn danger" onClick={() => handlePostDelete(report.post.id)}>
|
||||
<Trash2 size={16} />
|
||||
Удалить пост
|
||||
</button>
|
||||
{report.post.author && (
|
||||
<button className="btn warn" onClick={() => handleBanAuthor(report.post.id)}>
|
||||
<Ban size={16} />
|
||||
Забанить автора
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<button className="btn" onClick={() => handleReportStatus(report.id, 'resolved')}>
|
||||
Решено
|
||||
</button>
|
||||
<button className="btn warn" onClick={() => handleReportStatus(report.id, 'dismissed')}>
|
||||
Отклонить
|
||||
<button className="btn" onClick={() => handleReportStatus(report.id, 'dismissed')}>
|
||||
Отклонить репорт
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{reportsData.reports.length === 0 && (
|
||||
<div style={{ textAlign: 'center', padding: '40px', color: 'var(--text-secondary)' }}>
|
||||
Нет активных репортов
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 10240;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Кэширование статических файлов
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 10240;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Кэширование статических файлов
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для настройки удаленного хранилища медиа и базы данных
|
||||
# Использует SSHFS для монтирования удаленных директорий
|
||||
|
||||
# Цвета для вывода
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Конфигурация удаленного сервера
|
||||
REMOTE_SERVER="103.80.87.247"
|
||||
REMOTE_USER="root" # Измените на вашего пользователя
|
||||
REMOTE_MEDIA_PATH="/var/nakama/media"
|
||||
REMOTE_DB_PATH="/var/nakama/db"
|
||||
REMOTE_BACKUPS_PATH="/var/nakama/backups"
|
||||
|
||||
# Локальные точки монтирования
|
||||
LOCAL_MEDIA_MOUNT="/mnt/nakama-media"
|
||||
LOCAL_DB_MOUNT="/mnt/nakama-db"
|
||||
LOCAL_BACKUPS_MOUNT="/mnt/nakama-backups"
|
||||
|
||||
echo -e "${GREEN}=== Настройка удаленного хранилища ===${NC}"
|
||||
|
||||
# Проверка наличия SSHFS
|
||||
if ! command -v sshfs &> /dev/null; then
|
||||
echo -e "${RED}✗ SSHFS не установлен${NC}"
|
||||
echo -e "${YELLOW}Установка SSHFS...${NC}"
|
||||
|
||||
# Определить ОС и установить SSHFS
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
sudo apt-get update && sudo apt-get install -y sshfs
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
brew install macfuse sshfs
|
||||
else
|
||||
echo -e "${RED}Неподдерживаемая ОС. Установите SSHFS вручную.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ SSHFS установлен${NC}"
|
||||
|
||||
# Создать локальные директории для монтирования
|
||||
echo -e "${YELLOW}Создание локальных директорий...${NC}"
|
||||
sudo mkdir -p "$LOCAL_MEDIA_MOUNT" "$LOCAL_DB_MOUNT" "$LOCAL_BACKUPS_MOUNT"
|
||||
|
||||
# Создать директории на удаленном сервере
|
||||
echo -e "${YELLOW}Создание директорий на удаленном сервере...${NC}"
|
||||
ssh "${REMOTE_USER}@${REMOTE_SERVER}" "mkdir -p ${REMOTE_MEDIA_PATH} ${REMOTE_DB_PATH} ${REMOTE_BACKUPS_PATH}"
|
||||
|
||||
# Монтировать директории
|
||||
echo -e "${YELLOW}Монтирование удаленных директорий...${NC}"
|
||||
|
||||
# Медиа
|
||||
if mountpoint -q "$LOCAL_MEDIA_MOUNT"; then
|
||||
echo -e "${YELLOW}Медиа уже смонтированы${NC}"
|
||||
else
|
||||
sshfs "${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_MEDIA_PATH}" "$LOCAL_MEDIA_MOUNT" \
|
||||
-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3
|
||||
echo -e "${GREEN}✓ Медиа смонтированы: ${LOCAL_MEDIA_MOUNT}${NC}"
|
||||
fi
|
||||
|
||||
# База данных
|
||||
if mountpoint -q "$LOCAL_DB_MOUNT"; then
|
||||
echo -e "${YELLOW}БД уже смонтирована${NC}"
|
||||
else
|
||||
sshfs "${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_DB_PATH}" "$LOCAL_DB_MOUNT" \
|
||||
-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3
|
||||
echo -e "${GREEN}✓ БД смонтирована: ${LOCAL_DB_MOUNT}${NC}"
|
||||
fi
|
||||
|
||||
# Бекапы
|
||||
if mountpoint -q "$LOCAL_BACKUPS_MOUNT"; then
|
||||
echo -e "${YELLOW}Бекапы уже смонтированы${NC}"
|
||||
else
|
||||
sshfs "${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_BACKUPS_PATH}" "$LOCAL_BACKUPS_MOUNT" \
|
||||
-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3
|
||||
echo -e "${GREEN}✓ Бекапы смонтированы: ${LOCAL_BACKUPS_MOUNT}${NC}"
|
||||
fi
|
||||
|
||||
# Добавить в /etc/fstab для автоматического монтирования при загрузке (опционально)
|
||||
echo -e "${YELLOW}Хотите добавить автомонтирование при загрузке? (y/n)${NC}"
|
||||
read -r RESPONSE
|
||||
|
||||
if [[ "$RESPONSE" =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}Добавление в /etc/fstab...${NC}"
|
||||
|
||||
# Создать резервную копию fstab
|
||||
sudo cp /etc/fstab /etc/fstab.backup
|
||||
|
||||
# Добавить записи в fstab
|
||||
cat <<EOF | sudo tee -a /etc/fstab
|
||||
# Nakama remote storage
|
||||
${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_MEDIA_PATH} ${LOCAL_MEDIA_MOUNT} fuse.sshfs defaults,_netdev,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 0 0
|
||||
${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_DB_PATH} ${LOCAL_DB_MOUNT} fuse.sshfs defaults,_netdev,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 0 0
|
||||
${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_BACKUPS_PATH} ${LOCAL_BACKUPS_MOUNT} fuse.sshfs defaults,_netdev,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 0 0
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Автомонтирование настроено${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}=== Настройка завершена ===${NC}"
|
||||
echo ""
|
||||
echo "Монтированные директории:"
|
||||
echo " Медиа: ${LOCAL_MEDIA_MOUNT}"
|
||||
echo " БД: ${LOCAL_DB_MOUNT}"
|
||||
echo " Бекапы: ${LOCAL_BACKUPS_MOUNT}"
|
||||
echo ""
|
||||
echo "Для размонтирования используйте:"
|
||||
echo " sudo umount ${LOCAL_MEDIA_MOUNT}"
|
||||
echo " sudo umount ${LOCAL_DB_MOUNT}"
|
||||
echo " sudo umount ${LOCAL_BACKUPS_MOUNT}"
|
||||
|
||||
Loading…
Reference in New Issue