diff --git a/.gitignore b/.gitignore index b122779..43ad084 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,13 @@ .env +.env.local +.env.prod *.log main dist/ node_modules/ *.tar + +# Database dumps +database-dumps/*.sql +database-dumps/*.sql.gz +!database-dumps/.gitkeep diff --git a/database-dumps/.gitkeep b/database-dumps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database-dumps/README.md b/database-dumps/README.md new file mode 100644 index 0000000..b658108 --- /dev/null +++ b/database-dumps/README.md @@ -0,0 +1,62 @@ +# Database Dumps + +Эта директория содержит дампы базы данных для разработки и тестирования. + +## Использование + +### Создание дампа + +```bash +# Дамп из production БД (по умолчанию .env.prod) +./dump-db.sh + +# Дамп с именем +./dump-db.sh production-backup + +# Дамп из локальной БД +./dump-db.sh --env-file .env.local + +# Дамп из другого окружения +./dump-db.sh --env-file .env.prod my-backup +``` + +### Просмотр дампов + +```bash +./list-dumps.sh +``` + +### Восстановление дампа + +```bash +# Восстановление в локальную БД (по умолчанию .env.local) +./restore-db.sh dump_20240101_120000.sql.gz + +# Восстановление в production БД +./restore-db.sh --env-file .env.prod dump_20240101_120000.sql.gz + +# Можно указать имя без расширения +./restore-db.sh dump_20240101_120000 +``` + +## Поведение по умолчанию + +- **Создание дампа**: использует `.env.prod` (production БД) +- **Восстановление**: использует `.env.local` (локальная БД) + +Это можно изменить с помощью параметра `--env-file`. + +## Важно + +⚠️ **Восстановление дампа удалит все данные в целевой базе данных!** + +Всегда проверяйте, в какую БД вы восстанавливаете данные. + +## Формат файлов + +Дампы сохраняются в формате: +- `dump_YYYYMMDD_HHMMSS.sql.gz` - автоматическое имя с датой/временем +- `имя_дампа.sql.gz` - именованный дамп + +Все дампы автоматически сжимаются с помощью gzip. + diff --git a/docker-compose.yml b/docker-compose.yml index bc13ad9..756c8a2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,8 +25,8 @@ services: # Backend сервер (Go) backend: build: - context: ./play-life-backend - dockerfile: Dockerfile + context: . + dockerfile: ./play-life-backend/Dockerfile ports: - "${PORT:-8080}:8080" environment: diff --git a/dump-db.sh b/dump-db.sh new file mode 100755 index 0000000..2a01d67 --- /dev/null +++ b/dump-db.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Скрипт для создания дампа базы данных +# Использование: +# ./dump-db.sh [имя_дампа] # Дамп из .env.prod +# ./dump-db.sh --env-file .env.local [имя] # Дамп из указанного файла +# ./dump-db.sh production-backup # Именованный дамп из .env.prod + +set -e + +# Значения по умолчанию +DEFAULT_ENV_FILE=".env.prod" +ENV_FILE="$DEFAULT_ENV_FILE" +DUMP_NAME="" + +# Парсим аргументы +while [[ $# -gt 0 ]]; do + case $1 in + --env-file) + ENV_FILE="$2" + shift 2 + ;; + *) + if [ -z "$DUMP_NAME" ]; then + DUMP_NAME="$1" + else + echo "❌ Ошибка: Неизвестный аргумент: $1" + echo "Использование: ./dump-db.sh [--env-file FILE] [имя_дампа]" + exit 1 + fi + shift + ;; + esac +done + +# Загружаем переменные окружения из указанного файла +if [ -f "$ENV_FILE" ]; then + export $(cat "$ENV_FILE" | grep -v '^#' | grep -v '^$' | xargs) + echo "📋 Используется файл окружения: $ENV_FILE" +else + echo "⚠️ Файл $ENV_FILE не найден, используются значения по умолчанию" +fi + +DB_HOST=${DB_HOST:-localhost} +DB_PORT=${DB_PORT:-5432} +DB_USER=${DB_USER:-playeng} +DB_PASSWORD=${DB_PASSWORD:-playeng} +DB_NAME=${DB_NAME:-playeng} + +# Создаем директорию для дампов, если её нет +mkdir -p database-dumps + +# Генерируем имя файла с датой и временем, если не указано +if [ -z "$DUMP_NAME" ]; then + DUMP_NAME="dump_$(date +%Y%m%d_%H%M%S).sql" +else + DUMP_NAME="$DUMP_NAME.sql" +fi + +DUMP_PATH="database-dumps/$DUMP_NAME" + +echo "🗄️ Создание дампа базы данных..." +echo " База: $DB_NAME" +echo " Хост: $DB_HOST:$DB_PORT" +echo " Пользователь: $DB_USER" +echo " Файл: $DUMP_PATH" + +# Создаем дамп через docker-compose, если контейнер запущен +if docker-compose ps db 2>/dev/null | grep -q "Up"; then + echo " Используется docker-compose..." + docker-compose exec -T db pg_dump -U "$DB_USER" -d "$DB_NAME" > "$DUMP_PATH" +elif command -v pg_dump &> /dev/null; then + # Или напрямую через pg_dump, если БД доступна локально + echo " Используется локальный pg_dump..." + PGPASSWORD="$DB_PASSWORD" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" > "$DUMP_PATH" +elif command -v docker &> /dev/null; then + # Используем Docker образ postgres для создания дампа + # Используем latest для совместимости с разными версиями сервера + echo " Используется Docker (postgres:latest)..." + # Используем --network host для доступа к удаленным хостам + docker run --rm -i --network host \ + -e PGPASSWORD="$DB_PASSWORD" \ + postgres:latest \ + pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" > "$DUMP_PATH" +else + echo "❌ Ошибка: pg_dump не найден, docker-compose не запущен и Docker недоступен" + echo " Установите PostgreSQL клиент или Docker" + exit 1 +fi + +# Сжимаем дамп +echo " Сжатие дампа..." +gzip -f "$DUMP_PATH" +DUMP_PATH="${DUMP_PATH}.gz" + +echo "✅ Дамп успешно создан: $DUMP_PATH" +echo " Размер: $(du -h "$DUMP_PATH" | cut -f1)" + +# Ограничиваем количество дампов (максимум 10) +MAX_DUMPS=10 +DUMP_COUNT=$(ls -1 database-dumps/*.sql.gz 2>/dev/null | wc -l | tr -d ' ') + +if [ "$DUMP_COUNT" -gt "$MAX_DUMPS" ]; then + echo "" + echo "🧹 Очистка старых дампов (максимум $MAX_DUMPS)..." + # Сортируем по дате модификации (новые первыми) и удаляем самые старые + OLD_DUMPS=$(ls -1t database-dumps/*.sql.gz 2>/dev/null | tail -n +$((MAX_DUMPS + 1))) + if [ -n "$OLD_DUMPS" ]; then + REMOVED_COUNT=0 + for old_dump in $OLD_DUMPS; do + rm -f "$old_dump" + REMOVED_COUNT=$((REMOVED_COUNT + 1)) + echo " Удален: $(basename "$old_dump")" + done + echo " Удалено дампов: $REMOVED_COUNT" + echo " Осталось дампов: $MAX_DUMPS" + fi +fi + diff --git a/list-dumps.sh b/list-dumps.sh new file mode 100755 index 0000000..3e736f5 --- /dev/null +++ b/list-dumps.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Скрипт для просмотра списка доступных дампов + +DUMP_DIR="database-dumps" + +if [ ! -d "$DUMP_DIR" ]; then + echo "❌ Директория дампов не найдена: $DUMP_DIR" + exit 1 +fi + +echo "📦 Доступные дампы базы данных:" +echo "" + +# Показываем дампы с информацией о размере и дате +if ls "$DUMP_DIR"/*.sql.gz 2>/dev/null | grep -q .; then + ls -lh "$DUMP_DIR"/*.sql.gz 2>/dev/null | awk '{ + filename = $9 + gsub(/.*\//, "", filename) + printf " %-40s %8s %s %s %s\n", filename, $5, $6, $7, $8 + }' + echo "" + echo "Всего дампов: $(ls -1 "$DUMP_DIR"/*.sql.gz 2>/dev/null | wc -l | tr -d ' ')" + echo "" + echo "Для восстановления используйте:" + echo " ./restore-db.sh <имя_дампа.sql.gz> # В .env.local" + echo " ./restore-db.sh --env-file .env.prod <имя_дампа> # В указанный файл" +else + echo " (нет дампов)" + echo "" + echo "Для создания дампа используйте:" + echo " ./dump-db.sh # Из .env.prod" + echo " ./dump-db.sh --env-file .env.local [имя] # Из указанного файла" +fi + diff --git a/restore-db.sh b/restore-db.sh new file mode 100755 index 0000000..5496223 --- /dev/null +++ b/restore-db.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# Скрипт для восстановления базы данных из дампа +# Использование: +# ./restore-db.sh [имя_дампа.sql.gz] # Восстановление в .env.local +# ./restore-db.sh --env-file .env.prod [имя_дампа] # Восстановление в указанный файл +# ./restore-db.sh production-backup.sql.gz # Восстановление в .env.local + +set -e + +# Значения по умолчанию +DEFAULT_ENV_FILE=".env.local" +ENV_FILE="$DEFAULT_ENV_FILE" +DUMP_FILE="" + +# Парсим аргументы +while [[ $# -gt 0 ]]; do + case $1 in + --env-file) + ENV_FILE="$2" + shift 2 + ;; + *) + if [ -z "$DUMP_FILE" ]; then + DUMP_FILE="$1" + else + echo "❌ Ошибка: Неизвестный аргумент: $1" + echo "Использование: ./restore-db.sh [--env-file FILE] [имя_дампа.sql.gz]" + exit 1 + fi + shift + ;; + esac +done + +# Загружаем переменные окружения из указанного файла +if [ -f "$ENV_FILE" ]; then + export $(cat "$ENV_FILE" | grep -v '^#' | grep -v '^$' | xargs) + echo "📋 Используется файл окружения: $ENV_FILE" +else + echo "⚠️ Файл $ENV_FILE не найден, используются значения по умолчанию" +fi + +DB_HOST=${DB_HOST:-localhost} +DB_PORT=${DB_PORT:-5432} +DB_USER=${DB_USER:-playeng} +DB_PASSWORD=${DB_PASSWORD:-playeng} +DB_NAME=${DB_NAME:-playeng} + +# Проверяем наличие дампа +if [ -z "$DUMP_FILE" ]; then + echo "❌ Ошибка: Укажите имя дампа" + echo "Использование: ./restore-db.sh [--env-file FILE] [имя_дампа.sql.gz]" + echo "" + echo "Доступные дампы:" + ls -lh database-dumps/*.sql.gz 2>/dev/null | awk '{print " " $9}' | sed "s|database-dumps/||g" || echo " (нет дампов)" + exit 1 +fi + +# Определяем полный путь к файлу +if [[ "$DUMP_FILE" =~ ^/ ]]; then + # Абсолютный путь + FULL_DUMP_PATH="$DUMP_FILE" +else + # Относительный путь + if [[ "$DUMP_FILE" == *.sql.gz ]] || [[ "$DUMP_FILE" == *.sql ]]; then + FULL_DUMP_PATH="database-dumps/$DUMP_FILE" + else + # Пробуем с расширениями + if [ -f "database-dumps/$DUMP_FILE.sql.gz" ]; then + FULL_DUMP_PATH="database-dumps/$DUMP_FILE.sql.gz" + elif [ -f "database-dumps/$DUMP_FILE.sql" ]; then + FULL_DUMP_PATH="database-dumps/$DUMP_FILE.sql" + else + FULL_DUMP_PATH="database-dumps/$DUMP_FILE" + fi + fi +fi + +if [ ! -f "$FULL_DUMP_PATH" ]; then + echo "❌ Ошибка: Файл дампа не найден: $FULL_DUMP_PATH" + echo "" + echo "Доступные дампы:" + ls -lh database-dumps/*.sql.gz 2>/dev/null | awk '{print " " $9}' | sed "s|database-dumps/||g" || echo " (нет дампов)" + exit 1 +fi + +echo "⚠️ ВНИМАНИЕ: Это действие удалит все данные в базе $DB_NAME!" +echo " Хост: $DB_HOST:$DB_PORT" +echo " Пользователь: $DB_USER" +read -p " Продолжить? (yes/no): " confirm + +if [ "$confirm" != "yes" ]; then + echo "❌ Отменено." + exit 0 +fi + +echo "🔄 Восстановление базы данных из дампа..." +echo " База: $DB_NAME" +echo " Хост: $DB_HOST:$DB_PORT" +echo " Файл: $FULL_DUMP_PATH" + +# Распаковываем, если сжат +TEMP_DUMP="/tmp/restore_$$.sql" +if [[ "$FULL_DUMP_PATH" == *.gz ]]; then + echo " Распаковка дампа..." + gunzip -c "$FULL_DUMP_PATH" > "$TEMP_DUMP" +else + cp "$FULL_DUMP_PATH" "$TEMP_DUMP" +fi + +# Восстанавливаем через docker-compose, если контейнер запущен +if docker-compose ps db 2>/dev/null | grep -q "Up"; then + echo " Используется docker-compose..." + # Очищаем базу и восстанавливаем + docker-compose exec -T db psql -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;" + docker-compose exec -T db psql -U "$DB_USER" -d postgres -c "CREATE DATABASE $DB_NAME;" + docker-compose exec -T db psql -U "$DB_USER" -d "$DB_NAME" < "$TEMP_DUMP" +elif command -v psql &> /dev/null; then + # Или напрямую через psql + echo " Используется локальный psql..." + PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;" + PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "CREATE DATABASE $DB_NAME;" + PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" < "$TEMP_DUMP" +else + echo "❌ Ошибка: psql не найден и docker-compose не запущен" + echo " Запустите docker-compose или установите PostgreSQL клиент" + rm -f "$TEMP_DUMP" + exit 1 +fi + +# Удаляем временный файл +rm -f "$TEMP_DUMP" + +echo "✅ База данных успешно восстановлена из дампа!" +