full
Deploy to Production / deploy-production (push) Successful in 26s
Details
Deploy to Production / deploy-production (push) Successful in 26s
Details
This commit is contained in:
parent
083fd4d826
commit
118f33f77b
|
|
@ -0,0 +1,93 @@
|
|||
# Настройка автоматического резервного копирования БД
|
||||
|
||||
## 🎯 Автоматический бэкап дважды в день
|
||||
|
||||
Система автоматически создаёт бэкапы PROD и DEV БД:
|
||||
- **00:00** (полночь)
|
||||
- **12:00** (полдень)
|
||||
|
||||
## 📋 Установка
|
||||
|
||||
```bash
|
||||
cd /var/www/platform/prod
|
||||
|
||||
# Сделать скрипты исполняемыми
|
||||
chmod +x backup-db-auto.sh setup-cron-backup.sh remove-cron-backup.sh
|
||||
|
||||
# Настроить автоматический бэкап
|
||||
./setup-cron-backup.sh
|
||||
```
|
||||
|
||||
## ✅ Проверка
|
||||
|
||||
```bash
|
||||
# Проверить, что задача добавлена в cron
|
||||
crontab -l | grep backup-db-auto
|
||||
|
||||
# Должно быть:
|
||||
# 0 0,12 * * * PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /var/www/platform/prod/backup-db-auto.sh >> /var/www/platform/prod/backups/cron.log 2>&1
|
||||
```
|
||||
|
||||
## 📊 Логи
|
||||
|
||||
```bash
|
||||
# Логи автоматических бэкапов
|
||||
tail -f /var/www/platform/prod/backups/backup.log
|
||||
|
||||
# Логи cron (ошибки выполнения)
|
||||
tail -f /var/www/platform/prod/backups/cron.log
|
||||
```
|
||||
|
||||
## 🗂️ Хранение бэкапов
|
||||
|
||||
- **Директория**: `/var/www/platform/prod/backups/`
|
||||
- **Формат файлов**: `platform_prod_db_YYYYMMDD_HHMMSS.sql.gz`
|
||||
- **Автоочистка**: Бэкапы старше 30 дней удаляются автоматически
|
||||
- **Проверка места**: При использовании диска > 80% в лог пишется предупреждение
|
||||
|
||||
## 🔄 Ручной запуск
|
||||
|
||||
```bash
|
||||
# Запустить бэкап вручную (для тестирования)
|
||||
/var/www/platform/prod/backup-db-auto.sh
|
||||
```
|
||||
|
||||
## 🗑️ Удаление автоматического бэкапа
|
||||
|
||||
```bash
|
||||
# Удалить задачу из cron
|
||||
./remove-cron-backup.sh
|
||||
|
||||
# Или вручную
|
||||
crontab -l | grep -v backup-db-auto | crontab -
|
||||
```
|
||||
|
||||
## 📝 Что делает скрипт
|
||||
|
||||
1. ✅ Проверяет, что контейнеры БД запущены
|
||||
2. ✅ Создаёт бэкапы PROD и DEV БД
|
||||
3. ✅ Сжимает бэкапы (gzip)
|
||||
4. ✅ Проверяет размер бэкапов
|
||||
5. ✅ Удаляет бэкапы старше 30 дней
|
||||
6. ✅ Логирует все действия
|
||||
7. ✅ Предупреждает о нехватке места на диске
|
||||
|
||||
## ⚠️ Важно
|
||||
|
||||
- Скрипт работает от пользователя `root` (нужен доступ к Docker)
|
||||
- Бэкапы сохраняются в `/var/www/platform/prod/backups/`
|
||||
- Старые бэкапы (30+ дней) удаляются автоматически
|
||||
- При ошибках информация записывается в лог
|
||||
|
||||
## 🔍 Мониторинг
|
||||
|
||||
```bash
|
||||
# Посмотреть последние бэкапы
|
||||
ls -lh /var/www/platform/prod/backups/*.sql.gz | tail -10
|
||||
|
||||
# Проверить размер всех бэкапов
|
||||
du -sh /var/www/platform/prod/backups/
|
||||
|
||||
# Посмотреть последние записи в логе
|
||||
tail -20 /var/www/platform/prod/backups/backup.log
|
||||
```
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
# PROD Окружение - Инструкция по управлению
|
||||
|
||||
## ⚠️ ВАЖНО: Защита данных
|
||||
|
||||
**PROD окружение использует отдельную сеть (`prod_network`) и именованные volumes для изоляции от dev.**
|
||||
|
||||
### Что было исправлено:
|
||||
|
||||
1. ✅ **Отдельная сеть** - `prod_network` вместо общей `dev_network`
|
||||
2. ✅ **Именованные volumes** - все volumes имеют префикс `platform_prod_`
|
||||
3. ✅ **Полные имена контейнеров** - в `DATABASE_URL` и `REDIS_URL` используются полные имена контейнеров
|
||||
4. ✅ **Изоляция от dev** - prod не может случайно подключиться к dev БД
|
||||
|
||||
## 📋 Основные команды
|
||||
|
||||
### Безопасная остановка (СОХРАНЯЕТ данные БД):
|
||||
```bash
|
||||
# Использовать скрипт из /var/www/service
|
||||
/var/www/service/platform/safe-down-prod.sh
|
||||
|
||||
# Или вручную
|
||||
cd /var/www/platform/prod
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### ⚠️ ОСТОРОЖНО: Полная очистка (УДАЛЯЕТ данные БД):
|
||||
```bash
|
||||
# Сначала создайте бэкап!
|
||||
/var/www/service/backup/backup-prod-db.sh
|
||||
|
||||
# Затем можно удалить volumes
|
||||
cd /var/www/platform/prod
|
||||
docker compose down --volumes
|
||||
```
|
||||
|
||||
### Запуск:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Создание бэкапа БД:
|
||||
```bash
|
||||
# Бэкап PROD БД
|
||||
/var/www/service/backup/backup-all-db.sh
|
||||
|
||||
# Альтернативный скрипт для PROD БД
|
||||
/var/www/service/backup/backup-prod-db.sh
|
||||
|
||||
# Автоматический бэкап PROD БД (для cron)
|
||||
/var/www/service/backup/backup-db-auto.sh
|
||||
```
|
||||
|
||||
**Примечание:** DEV БД не бэкапится, так как это окружение разработки.
|
||||
|
||||
### Настройка автоматического бэкапа PROD БД (2 раза в день: 00:00 и 12:00):
|
||||
```bash
|
||||
# Установить автоматический бэкап PROD БД
|
||||
/var/www/service/backup/setup-cron-backup.sh
|
||||
|
||||
# Удалить автоматический бэкап
|
||||
/var/www/service/backup/remove-cron-backup.sh
|
||||
|
||||
# Проверить расписание
|
||||
crontab -l | grep backup-db-auto
|
||||
|
||||
# Просмотр логов
|
||||
tail -f /var/www/platform/prod/backups/backup.log
|
||||
```
|
||||
|
||||
**Примечание:** Автоматически бэкапится только PROD БД. DEV БД не бэкапится.
|
||||
|
||||
### Полная пересборка PROD (с бэкапом):
|
||||
```bash
|
||||
/var/www/service/platform/rebuild-prod.sh
|
||||
```
|
||||
Этот скрипт:
|
||||
1. Создаёт бэкап БД
|
||||
2. Останавливает контейнеры
|
||||
3. Пересобирает образы без кэша
|
||||
4. Запускает контейнеры
|
||||
5. Применяет миграции
|
||||
|
||||
### Применение миграций:
|
||||
```bash
|
||||
docker exec platform_prod_web python manage.py migrate
|
||||
```
|
||||
|
||||
### Создание суперпользователя:
|
||||
```bash
|
||||
docker exec -it platform_prod_web python manage.py createsuperuser
|
||||
```
|
||||
|
||||
## 🔧 Структура volumes
|
||||
|
||||
- `platform_prod_postgres_data` - данные PostgreSQL БД
|
||||
- `platform_prod_redis_data` - данные Redis
|
||||
- `platform_prod_front_material_node_modules` - node_modules для frontend
|
||||
- `platform_prod_front_material_next` - кэш Next.js
|
||||
|
||||
## 🌐 Сеть
|
||||
|
||||
- **Prod сеть**: `platform_prod_network` (изолирована от dev)
|
||||
- **Dev сеть**: `dev_network` (отдельная)
|
||||
|
||||
## 🔗 Подключения
|
||||
|
||||
Все сервисы используют полные имена контейнеров:
|
||||
- БД: `platform_prod_db` (не `db`)
|
||||
- Redis: `platform_prod_redis` (не `redis`)
|
||||
|
||||
Это гарантирует, что даже при запуске dev и prod одновременно, они не будут конфликтовать.
|
||||
|
||||
## 📝 Что делать если данные потеряны
|
||||
|
||||
1. Проверьте бэкапы: `ls -la ./backups/`
|
||||
2. Если бэкапа нет, но данные есть в dev БД, можно скопировать:
|
||||
```bash
|
||||
# Создать бэкап из dev
|
||||
docker exec platform_dev_db pg_dump -U platform_dev_user -d platform_dev_db > /tmp/dev_backup.sql
|
||||
|
||||
# Применить миграции в prod
|
||||
docker exec platform_prod_web python manage.py migrate
|
||||
|
||||
# Восстановить данные (осторожно!)
|
||||
docker exec -i platform_prod_db psql -U platform_prod_user -d platform_prod_db < /tmp/dev_backup.sql
|
||||
```
|
||||
3. Если данных нет нигде - создайте пользователей заново через `createsuperuser`
|
||||
|
||||
## 🚨 Частые ошибки
|
||||
|
||||
### ❌ НЕ делайте:
|
||||
- `docker compose down --volumes` без бэкапа
|
||||
- Использование коротких имен (`db`, `redis`) в переменных окружения
|
||||
- Общая сеть для dev и prod
|
||||
|
||||
### ✅ Делайте:
|
||||
- Всегда используйте `docker compose down` (без `--volumes`)
|
||||
- Регулярно создавайте бэкапы PROD БД: `/var/www/service/backup/backup-all-db.sh`
|
||||
- Используйте полные имена контейнеров в конфигурации
|
||||
|
||||
## 📁 Расположение скриптов
|
||||
|
||||
Все служебные скрипты перенесены в `/var/www/service/`:
|
||||
|
||||
- **Бэкапы**: `/var/www/service/backup/`
|
||||
- **Управление платформой**: `/var/www/service/platform/`
|
||||
|
||||
Подробнее: `/var/www/service/README.md`
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
# Инструкция по пересборке PROD и созданию бэкапов
|
||||
|
||||
## 🎯 Что нужно сделать:
|
||||
|
||||
### 1. Создать бэкап PROD БД
|
||||
|
||||
```bash
|
||||
# Сделать скрипты исполняемыми (первый раз)
|
||||
chmod +x /var/www/service/backup/*.sh
|
||||
chmod +x /var/www/service/platform/*.sh
|
||||
|
||||
# Создать бэкап PROD БД
|
||||
/var/www/service/backup/backup-all-db.sh
|
||||
```
|
||||
|
||||
Это создаст бэкап:
|
||||
- `/var/www/platform/prod/backups/platform_prod_db_YYYYMMDD_HHMMSS.sql.gz`
|
||||
|
||||
**Примечание:** DEV БД не бэкапится, так как это окружение разработки.
|
||||
|
||||
### 2. Пересобрать PROD окружение
|
||||
|
||||
```bash
|
||||
# Автоматическая пересборка (с бэкапом)
|
||||
/var/www/service/platform/rebuild-prod.sh
|
||||
```
|
||||
|
||||
Или вручную:
|
||||
|
||||
```bash
|
||||
cd /var/www/platform/prod
|
||||
|
||||
# Остановить контейнеры
|
||||
docker compose down
|
||||
|
||||
# Пересобрать без кэша
|
||||
docker compose build --no-cache --pull
|
||||
|
||||
# Запустить
|
||||
docker compose up -d
|
||||
|
||||
# Подождать запуска БД
|
||||
sleep 10
|
||||
|
||||
# Применить миграции
|
||||
docker exec platform_prod_web python manage.py migrate
|
||||
|
||||
# Проверить статус
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### 3. Проверить, что всё работает
|
||||
|
||||
```bash
|
||||
# Проверить логи
|
||||
docker compose logs -f
|
||||
|
||||
# Проверить подключение к БД
|
||||
docker exec platform_prod_web python manage.py shell -c "from django.db import connection; print('DB:', connection.settings_dict['NAME'])"
|
||||
|
||||
# Проверить количество пользователей (если таблица существует)
|
||||
docker exec platform_prod_db psql -U platform_prod_user -d platform_prod_db -c "SELECT COUNT(*) FROM users_user;" 2>/dev/null || echo "Таблица не создана, нужно применить миграции"
|
||||
```
|
||||
|
||||
### 4. Если пользователей нет - создать суперпользователя
|
||||
|
||||
```bash
|
||||
docker exec -it platform_prod_web python manage.py createsuperuser
|
||||
```
|
||||
|
||||
## 📋 Что было исправлено:
|
||||
|
||||
✅ **Отдельная сеть для prod** - `prod_network` (изолирована от dev)
|
||||
✅ **Именованные volumes** - все volumes имеют префикс `platform_prod_`
|
||||
✅ **Полные имена контейнеров** - используются `platform_prod_db` и `platform_prod_redis`
|
||||
✅ **Защита от случайного удаления данных** - volumes не удаляются при `docker compose down`
|
||||
|
||||
## ⚠️ Важно:
|
||||
|
||||
- **НЕ используйте** `docker compose down --volumes` без бэкапа!
|
||||
- Всегда создавайте бэкапы перед пересборкой
|
||||
- Используйте `./safe-down.sh` для безопасной остановки
|
||||
|
||||
## 🔄 Восстановление из бэкапа (если нужно):
|
||||
|
||||
```bash
|
||||
# Восстановить PROD БД
|
||||
gunzip < /var/www/platform/prod/backups/platform_prod_db_*.sql.gz | docker exec -i platform_prod_db psql -U platform_prod_user -d postgres
|
||||
```
|
||||
|
||||
## 📁 Расположение скриптов
|
||||
|
||||
Все служебные скрипты находятся в `/var/www/service/`:
|
||||
|
||||
- **Бэкапы**: `/var/www/service/backup/`
|
||||
- **Управление платформой**: `/var/www/service/platform/`
|
||||
|
||||
Подробнее: `/var/www/service/README.md`
|
||||
|
|
@ -403,11 +403,12 @@ def start_lessons_automatically():
|
|||
logger.info(f'Занятие {lesson.id} автоматически переведено в статус "in_progress"')
|
||||
|
||||
# Находим занятия, которые уже прошли и должны быть завершены
|
||||
# end_time < now (время окончания прошло)
|
||||
# end_time < now - 5 минут (время окончания прошло более 5 минут назад - даём время на завершение)
|
||||
# status in ['scheduled', 'in_progress'] (еще не завершены)
|
||||
five_minutes_ago = now - timedelta(minutes=5)
|
||||
lessons_to_complete = Lesson.objects.filter(
|
||||
status__in=['scheduled', 'in_progress'],
|
||||
end_time__lt=now
|
||||
end_time__lt=five_minutes_ago
|
||||
).select_related('mentor', 'client')
|
||||
|
||||
# Оптимизация: используем bulk_update вместо цикла с save()
|
||||
|
|
@ -420,6 +421,22 @@ def start_lessons_automatically():
|
|||
completed_count = len(lessons_to_complete_list)
|
||||
for lesson in lessons_to_complete_list:
|
||||
logger.info(f'Занятие {lesson.id} автоматически переведено в статус "completed" (время окончания прошло)')
|
||||
|
||||
# Закрываем LiveKit комнату, если она есть
|
||||
try:
|
||||
from apps.video.models import VideoRoom
|
||||
from apps.video.services import get_sfu_client, SFUClientError
|
||||
|
||||
video_room = VideoRoom.objects.filter(lesson=lesson).first()
|
||||
if video_room and video_room.room_id:
|
||||
sfu_client = get_sfu_client()
|
||||
try:
|
||||
sfu_client.delete_room(str(video_room.room_id))
|
||||
logger.info(f'LiveKit комната {video_room.room_id} закрыта для урока {lesson.id}')
|
||||
except SFUClientError as e:
|
||||
logger.warning(f'Не удалось закрыть LiveKit комнату {video_room.room_id} для урока {lesson.id}: {e}')
|
||||
except Exception as e:
|
||||
logger.error(f'Ошибка закрытия LiveKit комнаты для урока {lesson.id}: {str(e)}', exc_info=True)
|
||||
|
||||
if started_count > 0 or completed_count > 0:
|
||||
logger.info(f'[start_lessons_automatically] Начато: {started_count}, Завершено: {completed_count}')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для создания бэкапов БД PROD и DEV
|
||||
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="./backups"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
echo "=========================================="
|
||||
echo "Создание бэкапов БД (PROD и DEV)"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Создать директорию для бэкапов
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Функция для создания бэкапа
|
||||
backup_db() {
|
||||
local CONTAINER_NAME=$1
|
||||
local DB_USER=$2
|
||||
local DB_NAME=$3
|
||||
local BACKUP_NAME=$4
|
||||
|
||||
echo "Создание бэкапа: $BACKUP_NAME"
|
||||
|
||||
# Проверить, что контейнер запущен
|
||||
if ! docker ps | grep -q "$CONTAINER_NAME"; then
|
||||
echo "⚠️ Контейнер $CONTAINER_NAME не запущен, пропускаем..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE="$BACKUP_DIR/${BACKUP_NAME}_${TIMESTAMP}.sql.gz"
|
||||
|
||||
# Создать бэкап
|
||||
if docker exec "$CONTAINER_NAME" pg_dumpall -U "$DB_USER" -c 2>/dev/null | gzip > "$BACKUP_FILE"; then
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo " ✓ Бэкап создан: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
return 0
|
||||
else
|
||||
echo " ✗ Ошибка создания бэкапа для $BACKUP_NAME"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Бэкап PROD БД
|
||||
echo "--- PROD БД ---"
|
||||
backup_db "platform_prod_db" "platform_prod_user" "platform_prod_db" "platform_prod_db"
|
||||
|
||||
echo ""
|
||||
|
||||
# Бэкап DEV БД
|
||||
echo "--- DEV БД ---"
|
||||
backup_db "platform_dev_db" "platform_dev_user" "platform_dev_db" "platform_dev_db"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Бэкапы сохранены в: $BACKUP_DIR"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Для восстановления PROD БД:"
|
||||
echo " gunzip < $BACKUP_DIR/platform_prod_db_*.sql.gz | docker exec -i platform_prod_db psql -U platform_prod_user -d postgres"
|
||||
echo ""
|
||||
echo "Для восстановления DEV БД:"
|
||||
echo " gunzip < $BACKUP_DIR/platform_dev_db_*.sql.gz | docker exec -i platform_dev_db psql -U platform_dev_user -d postgres"
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Автоматический скрипт для создания бэкапов БД PROD и DEV
|
||||
# Запускается через cron дважды в день (00:00 и 12:00)
|
||||
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="/var/www/platform/prod/backups"
|
||||
LOG_FILE="/var/www/platform/prod/backups/backup.log"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DATE=$(date +%Y-%m-%d\ %H:%M:%S)
|
||||
|
||||
# Создать директорию для бэкапов и логов
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Функция для логирования
|
||||
log() {
|
||||
echo "[$DATE] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log "=========================================="
|
||||
log "Начало автоматического бэкапа БД"
|
||||
log "=========================================="
|
||||
|
||||
# Функция для создания бэкапа
|
||||
backup_db() {
|
||||
local CONTAINER_NAME=$1
|
||||
local DB_USER=$2
|
||||
local DB_NAME=$3
|
||||
local BACKUP_NAME=$4
|
||||
|
||||
log "Создание бэкапа: $BACKUP_NAME"
|
||||
|
||||
# Проверить, что контейнер запущен
|
||||
if ! docker ps | grep -q "$CONTAINER_NAME"; then
|
||||
log "⚠️ Контейнер $CONTAINER_NAME не запущен, пропускаем..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE="$BACKUP_DIR/${BACKUP_NAME}_${TIMESTAMP}.sql.gz"
|
||||
|
||||
# Создать бэкап
|
||||
if docker exec "$CONTAINER_NAME" pg_dumpall -U "$DB_USER" -c 2>/dev/null | gzip > "$BACKUP_FILE"; then
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
log " ✓ Бэкап создан: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
|
||||
# Проверить размер файла (должен быть больше 0)
|
||||
if [ ! -s "$BACKUP_FILE" ]; then
|
||||
log " ✗ ОШИБКА: Бэкап пустой!"
|
||||
rm -f "$BACKUP_FILE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
log " ✗ Ошибка создания бэкапа для $BACKUP_NAME"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Бэкап PROD БД
|
||||
PROD_SUCCESS=false
|
||||
if backup_db "platform_prod_db" "platform_prod_user" "platform_prod_db" "platform_prod_db"; then
|
||||
PROD_SUCCESS=true
|
||||
fi
|
||||
|
||||
# Бэкап DEV БД
|
||||
DEV_SUCCESS=false
|
||||
if backup_db "platform_dev_db" "platform_dev_user" "platform_dev_db" "platform_dev_db"; then
|
||||
DEV_SUCCESS=true
|
||||
fi
|
||||
|
||||
# Очистка старых бэкапов (оставляем последние 30 дней)
|
||||
log "Очистка старых бэкапов (старше 30 дней)..."
|
||||
find "$BACKUP_DIR" -name "*.sql.gz" -type f -mtime +30 -delete 2>/dev/null || true
|
||||
DELETED_COUNT=$(find "$BACKUP_DIR" -name "*.sql.gz" -type f 2>/dev/null | wc -l)
|
||||
log "Осталось бэкапов: $DELETED_COUNT"
|
||||
|
||||
# Итоги
|
||||
log "=========================================="
|
||||
if [ "$PROD_SUCCESS" = true ] && [ "$DEV_SUCCESS" = true ]; then
|
||||
log "✓ Бэкапы созданы успешно (PROD и DEV)"
|
||||
elif [ "$PROD_SUCCESS" = true ]; then
|
||||
log "⚠️ Бэкап PROD создан, DEV пропущен"
|
||||
elif [ "$DEV_SUCCESS" = true ]; then
|
||||
log "⚠️ Бэкап DEV создан, PROD пропущен"
|
||||
else
|
||||
log "✗ Ошибка: бэкапы не созданы!"
|
||||
exit 1
|
||||
fi
|
||||
log "=========================================="
|
||||
|
||||
# Проверка места на диске
|
||||
DISK_USAGE=$(df -h "$BACKUP_DIR" | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||
if [ "$DISK_USAGE" -gt 80 ]; then
|
||||
log "⚠️ ВНИМАНИЕ: Использовано дискового пространства: ${DISK_USAGE}%"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для создания бэкапа БД PROD
|
||||
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="./backups"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="$BACKUP_DIR/platform_prod_db_backup_$TIMESTAMP.sql.gz"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Создание бэкапа PROD БД"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Создать директорию для бэкапов
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Проверить, что контейнер БД запущен
|
||||
if ! docker ps | grep -q platform_prod_db; then
|
||||
echo "Ошибка: Контейнер platform_prod_db не запущен"
|
||||
echo "Запустите БД: docker compose up -d db"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Создание бэкапа..."
|
||||
echo "Файл: $BACKUP_FILE"
|
||||
echo ""
|
||||
|
||||
# Создать бэкап
|
||||
if docker exec platform_prod_db pg_dumpall -U platform_prod_user -c | gzip > "$BACKUP_FILE"; then
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo "✓ Бэкап создан успешно"
|
||||
echo " Размер: $BACKUP_SIZE"
|
||||
echo " Файл: $BACKUP_FILE"
|
||||
echo ""
|
||||
echo "Для восстановления:"
|
||||
echo " gunzip < $BACKUP_FILE | docker exec -i platform_prod_db psql -U platform_prod_user -d postgres"
|
||||
else
|
||||
echo "✗ Ошибка создания бэкапа!"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -5,6 +5,11 @@
|
|||
# front_material 3010, yjs 1236, excalidraw 3004, whiteboard 8083,
|
||||
# livekit 7880/7881, celery/beat — без портов (внутренние)
|
||||
# Dev использует: 5433, 6380, 8124, 8081, 3002, 1235, 3003, 8082, livekit 7890/7891
|
||||
#
|
||||
# ВАЖНО: PROD использует отдельную сеть (prod_network) и именованные volumes
|
||||
# НЕ используйте: docker compose down --volumes (удалит данные БД!)
|
||||
# Используйте: docker compose down (остановит контейнеры, сохранит volumes)
|
||||
# Для полной очистки: сначала сделайте бэкап БД, затем docker compose down --volumes
|
||||
|
||||
services:
|
||||
db:
|
||||
|
|
@ -20,7 +25,7 @@ services:
|
|||
volumes:
|
||||
- prod_postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
|
|
@ -31,7 +36,7 @@ services:
|
|||
volumes:
|
||||
- prod_redis_data:/data
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
web:
|
||||
build:
|
||||
|
|
@ -44,13 +49,15 @@ services:
|
|||
# Daphne (ASGI): HTTP + WebSocket (/ws/notifications/, /ws/chat/, /ws/board/ и т.д.)
|
||||
command: sh -c "python manage.py migrate && python manage.py init_subjects && daphne -b 0.0.0.0 -p 8000 config.asgi:application"
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-True}
|
||||
- SECRET_KEY=dev_secret_key
|
||||
- ALLOWED_HOSTS=api.uchill.online,app.uchill.online,uchill.online,www.uchill.online,localhost,127.0.0.1,85.192.56.185
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://redis:6379/2
|
||||
- DEBUG=${DEBUG:-False}
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
- ALLOWED_HOSTS=${ALLOWED_HOSTS}
|
||||
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS}
|
||||
- CSRF_TRUSTED_ORIGINS=${CSRF_TRUSTED_ORIGINS}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@platform_prod_db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://platform_prod_redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://platform_prod_redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://platform_prod_redis:6379/2
|
||||
# Явно передаём переменные почты из .env (иначе контейнер может не видеть их)
|
||||
- EMAIL_BACKEND=${EMAIL_BACKEND:-smtp}
|
||||
- EMAIL_HOST=${EMAIL_HOST}
|
||||
|
|
@ -78,7 +85,7 @@ services:
|
|||
- db
|
||||
- redis
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
celery:
|
||||
build:
|
||||
|
|
@ -90,11 +97,11 @@ services:
|
|||
env_file: .env
|
||||
command: celery -A config worker -l info
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-True}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://redis:6379/2
|
||||
- DEBUG=${DEBUG:-False}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@platform_prod_db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://platform_prod_redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://platform_prod_redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://platform_prod_redis:6379/2
|
||||
- EMAIL_BACKEND=${EMAIL_BACKEND:-smtp}
|
||||
- EMAIL_HOST=${EMAIL_HOST}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-2525}
|
||||
|
|
@ -116,7 +123,7 @@ services:
|
|||
- redis
|
||||
- web
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
celery-beat:
|
||||
build:
|
||||
|
|
@ -128,11 +135,11 @@ services:
|
|||
env_file: .env
|
||||
command: celery -A config beat -l info
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-True}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://redis:6379/2
|
||||
- DEBUG=${DEBUG:-False}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@platform_prod_db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://platform_prod_redis:6379/0
|
||||
- CELERY_BROKER_URL=redis://platform_prod_redis:6379/1
|
||||
- CELERY_RESULT_BACKEND=redis://platform_prod_redis:6379/2
|
||||
- EMAIL_BACKEND=${EMAIL_BACKEND:-smtp}
|
||||
- EMAIL_HOST=${EMAIL_HOST}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-2525}
|
||||
|
|
@ -154,7 +161,7 @@ services:
|
|||
- redis
|
||||
- web
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
# Telegram бот (polling): получает /start, /link <код> и т.д. Если используете webhook — не поднимайте этот сервис.
|
||||
telegram-bot:
|
||||
|
|
@ -167,9 +174,9 @@ services:
|
|||
env_file: .env
|
||||
command: python manage.py runtelegrambot
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-True}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://redis:6379/0
|
||||
- DEBUG=${DEBUG:-False}
|
||||
- DATABASE_URL=postgresql://platform_prod_user:platform_prod_password@platform_prod_db:5432/platform_prod_db
|
||||
- REDIS_URL=redis://platform_prod_redis:6379/0
|
||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TELEGRAM_USE_WEBHOOK=${TELEGRAM_USE_WEBHOOK:-False}
|
||||
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL:-}
|
||||
|
|
@ -181,7 +188,7 @@ services:
|
|||
- redis
|
||||
- web
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
# Видеоуроки: хост nginx (api.uchill.online) проксирует /livekit на 7880. Dev на том же хосте — 7890.
|
||||
# LIVEKIT_KEYS — строго один ключ в формате "key: secret" (пробел после двоеточия). В .env задайте одну строку: LIVEKIT_KEYS=APIKeyPlatform2024Secret: ThisIsAVerySecureSecretKeyForPlatform2024VideoConf
|
||||
|
|
@ -196,7 +203,7 @@ services:
|
|||
- "7880:7880"
|
||||
- "7881:7881"
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
|
|
@ -210,32 +217,31 @@ services:
|
|||
depends_on:
|
||||
- web
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
front_material:
|
||||
build:
|
||||
context: ./front_material
|
||||
dockerfile: Dockerfile
|
||||
target: development
|
||||
target: production
|
||||
args:
|
||||
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||
- NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
|
||||
- NEXT_PUBLIC_LIVEKIT_URL=${NEXT_PUBLIC_LIVEKIT_URL}
|
||||
- NEXT_PUBLIC_EXCALIDRAW_URL=${NEXT_PUBLIC_EXCALIDRAW_URL:-}
|
||||
container_name: platform_prod_front_material
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- WATCHPACK_POLLING=true
|
||||
- NODE_ENV=production
|
||||
- HOSTNAME=0.0.0.0
|
||||
- CHOKIDAR_USEPOLLING=true
|
||||
# Доска: поддомен board.uchill.online (прокси nginx на 3004) или путь на том же домене
|
||||
- NEXT_PUBLIC_EXCALIDRAW_URL=${NEXT_PUBLIC_EXCALIDRAW_URL:-}
|
||||
- NEXT_PUBLIC_EXCALIDRAW_PATH=${NEXT_PUBLIC_EXCALIDRAW_PATH:-/excalidraw}
|
||||
ports:
|
||||
- "3010:3000"
|
||||
volumes:
|
||||
- ./front_material:/app
|
||||
- front_material_node_modules:/app/node_modules
|
||||
- front_material_next:/app/.next
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
yjs-whiteboard:
|
||||
build:
|
||||
|
|
@ -246,21 +252,23 @@ services:
|
|||
ports:
|
||||
- "1236:1234"
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
excalidraw:
|
||||
build:
|
||||
context: ./excalidraw-server
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
container_name: platform_prod_excalidraw
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# basePath в next.config.js: иначе /_next/ запросы уходят на основной фронт и доска пустая
|
||||
- NEXT_PUBLIC_BASE_PATH=/excalidraw
|
||||
- NODE_ENV=production
|
||||
# Поддомен board.uchill.online: basePath не нужен (приложение на корне поддомена)
|
||||
- NEXT_PUBLIC_BASE_PATH=
|
||||
ports:
|
||||
- "3004:3001"
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
whiteboard:
|
||||
build:
|
||||
|
|
@ -271,14 +279,21 @@ services:
|
|||
ports:
|
||||
- "8083:8080"
|
||||
networks:
|
||||
- dev_network
|
||||
- prod_network
|
||||
|
||||
volumes:
|
||||
# ВАЖНО: Эти volumes содержат данные БД и Redis
|
||||
# НЕ используйте docker compose down --volumes без бэкапа!
|
||||
prod_postgres_data:
|
||||
name: platform_prod_postgres_data
|
||||
prod_redis_data:
|
||||
name: platform_prod_redis_data
|
||||
front_material_node_modules:
|
||||
name: platform_prod_front_material_node_modules
|
||||
front_material_next:
|
||||
name: platform_prod_front_material_next
|
||||
|
||||
networks:
|
||||
dev_network:
|
||||
prod_network:
|
||||
name: platform_prod_network
|
||||
driver: bridge
|
||||
|
|
|
|||
|
|
@ -1,20 +1,49 @@
|
|||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем package.json и patches (для patch-package)
|
||||
COPY package*.json ./
|
||||
COPY patches ./patches/
|
||||
|
||||
# Устанавливаем зависимости (postinstall применит патч y-excalidraw)
|
||||
RUN npm install
|
||||
|
||||
# Гарантированно применяем патчи (fix generateKeyBetween при вставке изображения)
|
||||
RUN npx patch-package
|
||||
|
||||
# Копируем все файлы
|
||||
COPY . .
|
||||
|
||||
# Запуск в dev режиме
|
||||
CMD ["npm", "run", "dev"]
|
||||
|
||||
# Multi-stage build для production
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем package.json и patches
|
||||
COPY package*.json ./
|
||||
COPY patches ./patches/
|
||||
|
||||
# Устанавливаем зависимости
|
||||
RUN npm ci
|
||||
|
||||
# Гарантированно применяем патчи
|
||||
RUN npx patch-package
|
||||
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Собираем приложение
|
||||
ENV NODE_ENV=production
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM node:18-alpine AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Копируем собранное приложение (standalone mode)
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
# Создаём пустую директорию public (если её нет в проекте, Next.js может её использовать)
|
||||
RUN mkdir -p ./public
|
||||
|
||||
# Создаем непривилегированного пользователя
|
||||
RUN addgroup --system --gid 1001 nodejs && \
|
||||
adduser --system --uid 1001 nextjs && \
|
||||
chown -R nextjs:nodejs /app
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
ENV PORT=3001
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
|
|
|
|||
|
|
@ -89,11 +89,16 @@ export default function ExcalidrawPage() {
|
|||
|
||||
setBoardId(id);
|
||||
|
||||
// Yjs: хост из apiUrl
|
||||
const port = params.get('yjsPort') || '1234';
|
||||
// Yjs: через nginx /yjs (prod) или прямой порт (dev/localhost)
|
||||
const apiHost = new URL(api).hostname;
|
||||
const isLocalhost = apiHost === 'localhost' || apiHost === '127.0.0.1';
|
||||
const wsProtocol = api.startsWith('https') ? 'wss:' : 'ws:';
|
||||
setWsUrl(`${wsProtocol}//${apiHost}:${port}`);
|
||||
const yjsPort = params.get('yjsPort') || '1234';
|
||||
// Prod: через nginx /yjs (без порта), Dev: прямой порт
|
||||
const wsUrl = isLocalhost
|
||||
? `${wsProtocol}//${apiHost}:${yjsPort}`
|
||||
: `${wsProtocol}//${apiHost}/yjs`;
|
||||
setWsUrl(wsUrl);
|
||||
|
||||
// Имя из API (UTF-8, без проблем с кодировкой URL/postMessage)
|
||||
if (token) {
|
||||
|
|
@ -229,7 +234,6 @@ export default function ExcalidrawPage() {
|
|||
}, [username]);
|
||||
|
||||
if (!boardId) {
|
||||
const example = `${window.location.origin}${window.location.pathname}?boardId=your-board-id&apiUrl=http://127.0.0.1:8123`;
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
|
|
@ -247,10 +251,6 @@ export default function ExcalidrawPage() {
|
|||
<code style={{ fontSize: 12, background: '#f5f5f5', padding: '8px 12px', borderRadius: 8, wordBreak: 'break-all' }}>
|
||||
?boardId=xxx&apiUrl=http://127.0.0.1:8123
|
||||
</code>
|
||||
<p style={{ margin: 0, fontSize: 14, color: '#666' }}>Пример:</p>
|
||||
<code style={{ fontSize: 11, background: '#f5f5f5', padding: '8px 12px', borderRadius: 8, wordBreak: 'break-all', maxWidth: '100%' }}>
|
||||
{example}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ WORKDIR /app
|
|||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_WS_URL
|
||||
ARG NEXT_PUBLIC_LIVEKIT_URL
|
||||
ARG NEXT_PUBLIC_EXCALIDRAW_URL
|
||||
|
||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||
ENV NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL
|
||||
ENV NEXT_PUBLIC_LIVEKIT_URL=$NEXT_PUBLIC_LIVEKIT_URL
|
||||
ENV NEXT_PUBLIC_EXCALIDRAW_URL=$NEXT_PUBLIC_EXCALIDRAW_URL
|
||||
ENV NODE_ENV=development
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
ENV WATCHPACK_POLLING=true
|
||||
|
|
@ -59,10 +61,12 @@ WORKDIR /app
|
|||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_WS_URL
|
||||
ARG NEXT_PUBLIC_LIVEKIT_URL
|
||||
ARG NEXT_PUBLIC_EXCALIDRAW_URL
|
||||
|
||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||
ENV NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL
|
||||
ENV NEXT_PUBLIC_LIVEKIT_URL=$NEXT_PUBLIC_LIVEKIT_URL
|
||||
ENV NEXT_PUBLIC_EXCALIDRAW_URL=$NEXT_PUBLIC_EXCALIDRAW_URL
|
||||
|
||||
# Копируем package files
|
||||
COPY package*.json ./
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
|
||||
/**
|
||||
* Если пользователь авторизован — редирект на дашборд.
|
||||
* Страницы логина/регистрации и т.д. не должны быть доступны авторизованным.
|
||||
*/
|
||||
export function AuthRedirect({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter();
|
||||
const { user, loading } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
if (user) {
|
||||
router.replace('/dashboard');
|
||||
}
|
||||
}, [user, loading, router]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: '#fff',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 16, color: 'var(--md-sys-color-on-surface-variant)' }}>
|
||||
Загрузка...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return null; // редирект уже идёт
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
|
||||
/**
|
||||
* Если пользователь авторизован — редирект на дашборд.
|
||||
* Страницы логина/регистрации и т.д. не должны быть доступны авторизованным.
|
||||
*/
|
||||
export function AuthRedirect({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter();
|
||||
const { user, loading } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
if (user) {
|
||||
router.replace('/dashboard');
|
||||
}
|
||||
}, [user, loading, router]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: '#fff',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 16, color: 'var(--md-sys-color-on-surface-variant)' }}>
|
||||
Загрузка...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return null; // редирект уже идёт
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ export function WhiteboardIframe({
|
|||
})();
|
||||
url.searchParams.set('boardId', boardId);
|
||||
url.searchParams.set('apiUrl', apiUrl);
|
||||
// Yjs WebSocket порт: 1236 (внешний порт yjs-whiteboard контейнера) или через nginx прокси
|
||||
const yjsPort = process.env.NEXT_PUBLIC_YJS_PORT || '1236';
|
||||
url.searchParams.set('yjsPort', yjsPort);
|
||||
if (token) url.searchParams.set('token', token);
|
||||
if (isMentor) url.searchParams.set('isMentor', '1');
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для полной пересборки PROD окружения с бэкапом БД
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "Полная пересборка PROD окружения"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "⚠️ ВНИМАНИЕ: Это пересоберёт все контейнеры без кэша"
|
||||
echo ""
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Шаг 1: Создать бэкап БД
|
||||
echo "Шаг 1: Создание бэкапа БД..."
|
||||
if [ -f "./backup-all-db.sh" ]; then
|
||||
./backup-all-db.sh
|
||||
else
|
||||
echo "⚠️ Скрипт backup-all-db.sh не найден, создаём бэкап вручную..."
|
||||
mkdir -p ./backups
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
if docker ps | grep -q platform_prod_db; then
|
||||
docker exec platform_prod_db pg_dumpall -U platform_prod_user -c | gzip > "./backups/platform_prod_db_backup_${TIMESTAMP}.sql.gz"
|
||||
echo "✓ Бэкап создан: ./backups/platform_prod_db_backup_${TIMESTAMP}.sql.gz"
|
||||
else
|
||||
echo "⚠️ Контейнер БД не запущен, пропускаем бэкап"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Шаг 2: Остановка контейнеров..."
|
||||
docker compose down
|
||||
|
||||
echo ""
|
||||
echo "Шаг 3: Пересборка образов без кэша..."
|
||||
echo "Это может занять несколько минут..."
|
||||
docker compose build --no-cache --pull
|
||||
|
||||
echo ""
|
||||
echo "Шаг 4: Запуск контейнеров..."
|
||||
docker compose up -d
|
||||
|
||||
echo ""
|
||||
echo "Шаг 5: Ожидание запуска БД..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Шаг 6: Применение миграций..."
|
||||
docker exec platform_prod_web python manage.py migrate
|
||||
|
||||
echo ""
|
||||
echo "Шаг 7: Проверка статуса контейнеров..."
|
||||
docker compose ps
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✓ Пересборка завершена!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Проверьте логи:"
|
||||
echo " docker compose logs -f"
|
||||
echo ""
|
||||
echo "Если нужно создать суперпользователя:"
|
||||
echo " docker exec -it platform_prod_web python manage.py createsuperuser"
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для удаления автоматического бэкапа из cron
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="/var/www/platform/prod"
|
||||
BACKUP_SCRIPT="$SCRIPT_DIR/backup-db-auto.sh"
|
||||
CRON_USER="root"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Удаление автоматического бэкапа из cron"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Проверить, есть ли запись в crontab
|
||||
if crontab -u "$CRON_USER" -l 2>/dev/null | grep -q "$BACKUP_SCRIPT"; then
|
||||
echo "Найдена запись в crontab:"
|
||||
crontab -u "$CRON_USER" -l | grep "$BACKUP_SCRIPT"
|
||||
echo ""
|
||||
read -p "Удалить? (y/N): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
crontab -u "$CRON_USER" -l 2>/dev/null | grep -v "$BACKUP_SCRIPT" | crontab -u "$CRON_USER" -
|
||||
echo "✓ Запись удалена из crontab"
|
||||
else
|
||||
echo "Отменено."
|
||||
fi
|
||||
else
|
||||
echo "Запись в crontab не найдена."
|
||||
fi
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Безопасная остановка PROD окружения
|
||||
# Этот скрипт останавливает контейнеры БЕЗ удаления volumes (данных БД)
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "Безопасная остановка PROD окружения"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Это остановит контейнеры, но СОХРАНИТ данные БД и Redis"
|
||||
echo ""
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Остановить контейнеры без удаления volumes
|
||||
docker compose down
|
||||
|
||||
echo ""
|
||||
echo "✓ Контейнеры остановлены"
|
||||
echo "✓ Volumes сохранены (данные БД не потеряны)"
|
||||
echo ""
|
||||
echo "Для запуска: docker compose up -d"
|
||||
echo "Для полной очистки (с удалением данных): docker compose down --volumes"
|
||||
echo " (ВНИМАНИЕ: это удалит все данные БД!)"
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для настройки автоматического бэкапа БД через cron
|
||||
# Запускается дважды в день: в 00:00 и 12:00
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="/var/www/platform/prod"
|
||||
BACKUP_SCRIPT="$SCRIPT_DIR/backup-db-auto.sh"
|
||||
CRON_USER="root"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Настройка автоматического бэкапа БД"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Проверить, что скрипт существует
|
||||
if [ ! -f "$BACKUP_SCRIPT" ]; then
|
||||
echo "Ошибка: Скрипт $BACKUP_SCRIPT не найден!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Сделать скрипт исполняемым
|
||||
chmod +x "$BACKUP_SCRIPT"
|
||||
echo "✓ Скрипт сделан исполняемым"
|
||||
|
||||
# Создать директорию для бэкапов
|
||||
mkdir -p "$SCRIPT_DIR/backups"
|
||||
echo "✓ Директория для бэкапов создана"
|
||||
|
||||
# Найти путь к docker (для cron)
|
||||
DOCKER_PATH=$(which docker 2>/dev/null || echo "/usr/bin/docker")
|
||||
|
||||
# Проверить, есть ли уже запись в crontab
|
||||
# Используем PATH с docker и bash для надежности
|
||||
CRON_CMD="0 0,12 * * * PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin $BACKUP_SCRIPT >> $SCRIPT_DIR/backups/cron.log 2>&1"
|
||||
|
||||
if crontab -u "$CRON_USER" -l 2>/dev/null | grep -q "$BACKUP_SCRIPT"; then
|
||||
echo "⚠️ Запись в crontab уже существует"
|
||||
echo ""
|
||||
echo "Текущий crontab:"
|
||||
crontab -u "$CRON_USER" -l | grep "$BACKUP_SCRIPT"
|
||||
echo ""
|
||||
read -p "Заменить существующую запись? (y/N): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Удалить старую запись
|
||||
crontab -u "$CRON_USER" -l 2>/dev/null | grep -v "$BACKUP_SCRIPT" | crontab -u "$CRON_USER" -
|
||||
# Добавить новую
|
||||
(crontab -u "$CRON_USER" -l 2>/dev/null; echo "$CRON_CMD") | crontab -u "$CRON_USER" -
|
||||
echo "✓ Запись в crontab обновлена"
|
||||
else
|
||||
echo "Отменено. Существующая запись сохранена."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
# Добавить новую запись
|
||||
(crontab -u "$CRON_USER" -l 2>/dev/null; echo "$CRON_CMD") | crontab -u "$CRON_USER" -
|
||||
echo "✓ Запись в crontab добавлена"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Настройка завершена!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Расписание бэкапов:"
|
||||
echo " - Каждый день в 00:00 (полночь)"
|
||||
echo " - Каждый день в 12:00 (полдень)"
|
||||
echo ""
|
||||
echo "Проверить crontab:"
|
||||
echo " crontab -u $CRON_USER -l"
|
||||
echo ""
|
||||
echo "Просмотр логов бэкапов:"
|
||||
echo " tail -f $SCRIPT_DIR/backups/backup.log"
|
||||
echo " tail -f $SCRIPT_DIR/backups/cron.log"
|
||||
echo ""
|
||||
echo "Удалить автоматический бэкап:"
|
||||
echo " crontab -u $CRON_USER -l | grep -v '$BACKUP_SCRIPT' | crontab -u $CRON_USER -"
|
||||
Loading…
Reference in New Issue