Some checks failed
continuous-integration/drone/push Build is failing
✅ Исправления: - Удален дублированный Python код из master-deploy.sh (строка 491) - Исправлена логика завершения pre-deploy-check.sh - Предупреждения больше не вызывают ошибку выхода ⚡ Новые возможности: - Добавлен quick-deploy.sh для быстрого развертывания - Команда make quick-deploy для систем с ограниченными ресурсами - Команда make deploy-force для пропуска проверок - Команда make pre-deploy-check-force для игнорирования предупреждений 📋 Улучшения: - pre-deploy-check теперь корректно обрабатывает предупреждения о RAM - Добавлена поддержка флага --skip-checks в master-deploy.sh - Более понятные сообщения об ошибках и предупреждениях Теперь система готова к развертыванию даже на серверах с 1GB RAM.
737 lines
25 KiB
Bash
Executable File
737 lines
25 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# CatLink Master Deployment Script
|
||
# Полное автоматическое развертывание проекта с генерацией всех ключей и настроек
|
||
|
||
set -e
|
||
|
||
# Цвета для вывода
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Логирование
|
||
log() {
|
||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||
}
|
||
|
||
success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
exit 1
|
||
}
|
||
|
||
# Функция для генерации случайных паролей
|
||
generate_password() {
|
||
local length=${1:-32}
|
||
openssl rand -base64 $length | tr -d "=+/" | cut -c1-$length
|
||
}
|
||
|
||
# Функция для генерации Django secret key
|
||
generate_django_secret() {
|
||
python3 -c "
|
||
import secrets
|
||
import string
|
||
chars = string.ascii_letters + string.digits + '!@#$%^&*(-_=+)'
|
||
print(''.join(secrets.choice(chars) for i in range(50)))
|
||
"
|
||
}
|
||
|
||
# Глобальная переменная для команды Docker Compose
|
||
DOCKER_COMPOSE_CMD=""
|
||
|
||
# Проверка требований
|
||
check_requirements() {
|
||
log "🔍 Проверка системных требований..."
|
||
|
||
# Проверяем Docker
|
||
if ! command -v docker &> /dev/null; then
|
||
error "Docker не установлен. Установите Docker и попробуйте снова."
|
||
fi
|
||
|
||
# Проверяем Docker Compose (v1 или v2)
|
||
DOCKER_COMPOSE_CMD=$(./scripts/detect-docker-compose.sh)
|
||
if [[ -z "$DOCKER_COMPOSE_CMD" ]]; then
|
||
error "Docker Compose не установлен. Установите Docker Compose и попробуйте снова."
|
||
fi
|
||
|
||
if [[ "$DOCKER_COMPOSE_CMD" == "docker-compose" ]]; then
|
||
log "✅ Найден Docker Compose v1"
|
||
else
|
||
log "✅ Найден Docker Compose v2"
|
||
fi
|
||
|
||
# Проверяем nginx
|
||
if ! command -v nginx &> /dev/null; then
|
||
warning "nginx не установлен. Установка nginx..."
|
||
sudo apt update
|
||
sudo apt install -y nginx
|
||
fi
|
||
|
||
# Проверяем certbot
|
||
if ! command -v certbot &> /dev/null; then
|
||
warning "certbot не установлен. Установка certbot..."
|
||
sudo apt update
|
||
sudo apt install -y certbot python3-certbot-nginx
|
||
fi
|
||
|
||
# Проверяем openssl
|
||
if ! command -v openssl &> /dev/null; then
|
||
sudo apt install -y openssl
|
||
fi
|
||
|
||
# Проверяем python3
|
||
if ! command -v python3 &> /dev/null; then
|
||
sudo apt install -y python3
|
||
fi
|
||
|
||
success "Все системные требования выполнены"
|
||
}
|
||
|
||
# Сбор информации от пользователя
|
||
collect_deployment_info() {
|
||
log "📋 Сбор информации для развертывания..."
|
||
|
||
# Домен
|
||
while true; do
|
||
read -p "🌐 Введите основной домен (например: links.shareon.kr): " MAIN_DOMAIN
|
||
if [[ $MAIN_DOMAIN =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||
break
|
||
else
|
||
warning "Неверный формат домена. Попробуйте снова."
|
||
fi
|
||
done
|
||
|
||
# Дополнительные домены
|
||
read -p "🌐 Введите дополнительные домены через запятую (или Enter для пропуска): " ADDITIONAL_DOMAINS
|
||
|
||
# Email для Let's Encrypt
|
||
while true; do
|
||
read -p "📧 Введите email для Let's Encrypt: " LETSENCRYPT_EMAIL
|
||
if [[ $LETSENCRYPT_EMAIL =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||
break
|
||
else
|
||
warning "Неверный формат email. Попробуйте снова."
|
||
fi
|
||
done
|
||
|
||
# Режим SSL
|
||
echo "🔒 Выберите режим SSL:"
|
||
echo "1) Автоматически получить Let's Encrypt сертификаты"
|
||
echo "2) Использовать самоподписанные сертификаты (для тестирования)"
|
||
echo "3) Пропустить SSL (только HTTP)"
|
||
read -p "Выберите опцию (1-3): " SSL_MODE
|
||
|
||
# Окружение
|
||
echo "🏷️ Выберите окружение:"
|
||
echo "1) production (рекомендуется для живого сайта)"
|
||
echo "2) staging (для тестирования)"
|
||
echo "3) development (для разработки)"
|
||
read -p "Выберите опцию (1-3): " ENV_MODE
|
||
|
||
case $ENV_MODE in
|
||
1) ENVIRONMENT="production" ;;
|
||
2) ENVIRONMENT="staging" ;;
|
||
3) ENVIRONMENT="development" ;;
|
||
*) ENVIRONMENT="production" ;;
|
||
esac
|
||
|
||
success "Информация собрана"
|
||
}
|
||
|
||
# Генерация .env файла
|
||
generate_env_file() {
|
||
log "⚙️ Генерация .env файла с безопасными настройками..."
|
||
|
||
# Генерируем пароли
|
||
DJANGO_SECRET_KEY=$(generate_django_secret)
|
||
DB_PASSWORD=$(generate_password 32)
|
||
|
||
# Формируем список доменов
|
||
if [[ -n "$ADDITIONAL_DOMAINS" ]]; then
|
||
ALL_DOMAINS="$MAIN_DOMAIN,$ADDITIONAL_DOMAINS,localhost,127.0.0.1"
|
||
else
|
||
ALL_DOMAINS="$MAIN_DOMAIN,localhost,127.0.0.1"
|
||
fi
|
||
|
||
# Определяем API URL
|
||
if [[ "$SSL_MODE" == "1" ]] || [[ "$SSL_MODE" == "2" ]]; then
|
||
API_URL="https://$MAIN_DOMAIN"
|
||
else
|
||
API_URL="http://$MAIN_DOMAIN"
|
||
fi
|
||
|
||
# Создаем .env файл
|
||
cat > .env << EOF
|
||
# =============================================================================
|
||
# CatLink Configuration - Generated $(date)
|
||
# Environment: $ENVIRONMENT
|
||
# =============================================================================
|
||
|
||
# Django настройки
|
||
DJANGO_SECRET_KEY=$DJANGO_SECRET_KEY
|
||
DJANGO_DEBUG=$([ "$ENVIRONMENT" = "development" ] && echo "True" || echo "False")
|
||
DJANGO_ALLOWED_HOSTS=$ALL_DOMAINS
|
||
|
||
# База данных PostgreSQL
|
||
DATABASE_ENGINE=django.db.backends.postgresql
|
||
DATABASE_NAME=links_db
|
||
DATABASE_USER=links_user
|
||
DATABASE_PASSWORD=$DB_PASSWORD
|
||
DATABASE_HOST=db
|
||
DATABASE_PORT=5432
|
||
|
||
# PostgreSQL настройки для контейнера
|
||
POSTGRES_DB=links_db
|
||
POSTGRES_USER=links_user
|
||
POSTGRES_PASSWORD=$DB_PASSWORD
|
||
|
||
# Frontend настройки
|
||
NEXT_PUBLIC_API_URL=$API_URL
|
||
|
||
# SSL/Security настройки
|
||
DJANGO_SECURE_SSL_REDIRECT=$([ "$SSL_MODE" = "1" ] && echo "True" || echo "False")
|
||
DJANGO_SECURE_HSTS_SECONDS=$([ "$SSL_MODE" = "1" ] && echo "31536000" || echo "0")
|
||
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=$([ "$SSL_MODE" = "1" ] && echo "True" || echo "False")
|
||
DJANGO_SECURE_HSTS_PRELOAD=$([ "$SSL_MODE" = "1" ] && echo "True" || echo "False")
|
||
|
||
# Let's Encrypt настройки
|
||
LETSENCRYPT_EMAIL=$LETSENCRYPT_EMAIL
|
||
MAIN_DOMAIN=$MAIN_DOMAIN
|
||
ADDITIONAL_DOMAINS=$ADDITIONAL_DOMAINS
|
||
|
||
# Environment
|
||
ENVIRONMENT=$ENVIRONMENT
|
||
|
||
# Session/Cookie настройки
|
||
DJANGO_SESSION_COOKIE_SECURE=$([ "$SSL_MODE" = "1" ] && echo "True" || echo "False")
|
||
DJANGO_CSRF_COOKIE_SECURE=$([ "$SSL_MODE" = "1" ] && echo "True" || echo "False")
|
||
|
||
# Email настройки (опционально - настройте при необходимости)
|
||
# DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||
# DJANGO_EMAIL_HOST=smtp.gmail.com
|
||
# DJANGO_EMAIL_PORT=587
|
||
# DJANGO_EMAIL_HOST_USER=your-email@gmail.com
|
||
# DJANGO_EMAIL_HOST_PASSWORD=your-app-password
|
||
# DJANGO_EMAIL_USE_TLS=True
|
||
# DJANGO_DEFAULT_FROM_EMAIL=noreply@$MAIN_DOMAIN
|
||
|
||
# Backup настройки
|
||
BACKUP_SCHEDULE="0 2 * * *" # Ежедневно в 2:00
|
||
BACKUP_RETENTION_DAYS=30
|
||
|
||
EOF
|
||
|
||
success ".env файл создан с безопасными настройками"
|
||
|
||
# Создаем backup .env файла
|
||
cp .env ".env.backup.$(date +%Y%m%d_%H%M%S)"
|
||
log "📦 Backup .env файла создан"
|
||
}
|
||
|
||
# Настройка nginx
|
||
setup_nginx() {
|
||
log "🌐 Настройка nginx..."
|
||
|
||
# Создаем конфигурацию nginx
|
||
sudo tee /etc/nginx/sites-available/catlink << EOF
|
||
# CatLink nginx configuration
|
||
# Generated $(date)
|
||
|
||
server {
|
||
listen 80;
|
||
server_name $MAIN_DOMAIN${ADDITIONAL_DOMAINS:+ $ADDITIONAL_DOMAINS};
|
||
|
||
# Security headers
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||
|
||
# Gzip compression
|
||
gzip on;
|
||
gzip_vary on;
|
||
gzip_min_length 1024;
|
||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1: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;
|
||
|
||
# Timeout settings
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
}
|
||
|
||
location /api/ {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
proxy_http_version 1.1;
|
||
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;
|
||
|
||
# Timeout settings
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
}
|
||
|
||
location /admin/ {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
proxy_http_version 1.1;
|
||
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;
|
||
}
|
||
|
||
location /static/ {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
expires 1y;
|
||
add_header Cache-Control "public, immutable";
|
||
}
|
||
|
||
location /media/ {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
expires 1y;
|
||
add_header Cache-Control "public";
|
||
}
|
||
|
||
# Security
|
||
location ~ /\\.ht {
|
||
deny all;
|
||
}
|
||
|
||
location = /robots.txt {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
access_log off;
|
||
}
|
||
|
||
location = /favicon.ico {
|
||
proxy_pass http://127.0.0.1:3000;
|
||
access_log off;
|
||
}
|
||
}
|
||
EOF
|
||
|
||
# Включаем сайт
|
||
sudo ln -sf /etc/nginx/sites-available/catlink /etc/nginx/sites-enabled/
|
||
|
||
# Удаляем дефолтный сайт
|
||
sudo rm -f /etc/nginx/sites-enabled/default
|
||
|
||
# Проверяем конфигурацию
|
||
if sudo nginx -t; then
|
||
success "Конфигурация nginx корректна"
|
||
sudo systemctl reload nginx
|
||
else
|
||
error "Ошибка в конфигурации nginx"
|
||
fi
|
||
}
|
||
|
||
# Настройка SSL
|
||
setup_ssl() {
|
||
if [[ "$SSL_MODE" == "1" ]]; then
|
||
log "🔒 Получение Let's Encrypt сертификатов..."
|
||
|
||
# Формируем список доменов для certbot
|
||
CERT_DOMAINS="-d $MAIN_DOMAIN"
|
||
if [[ -n "$ADDITIONAL_DOMAINS" ]]; then
|
||
IFS=',' read -ra DOMAINS <<< "$ADDITIONAL_DOMAINS"
|
||
for domain in "${DOMAINS[@]}"; do
|
||
domain=$(echo $domain | xargs) # trim whitespace
|
||
CERT_DOMAINS="$CERT_DOMAINS -d $domain"
|
||
done
|
||
fi
|
||
|
||
# Получаем сертификаты
|
||
sudo certbot --nginx $CERT_DOMAINS --email $LETSENCRYPT_EMAIL --agree-tos --non-interactive --redirect
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
success "SSL сертификаты получены успешно"
|
||
|
||
# Настраиваем автообновление
|
||
echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -
|
||
log "📅 Настроено автообновление SSL сертификатов"
|
||
else
|
||
warning "Не удалось получить SSL сертификаты. Продолжаем без SSL."
|
||
SSL_MODE="3"
|
||
fi
|
||
|
||
elif [[ "$SSL_MODE" == "2" ]]; then
|
||
log "🔒 Создание самоподписанных SSL сертификатов..."
|
||
|
||
# Создаем директорию для сертификатов
|
||
sudo mkdir -p /etc/ssl/private /etc/ssl/certs
|
||
|
||
# Генерируем самоподписанный сертификат
|
||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||
-keyout /etc/ssl/private/$MAIN_DOMAIN.key \
|
||
-out /etc/ssl/certs/$MAIN_DOMAIN.crt \
|
||
-subj "/C=US/ST=State/L=City/O=Organization/CN=$MAIN_DOMAIN"
|
||
|
||
# Обновляем nginx конфигурацию для SSL
|
||
sudo tee -a /etc/nginx/sites-available/catlink << EOF
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name $MAIN_DOMAIN${ADDITIONAL_DOMAINS:+ $ADDITIONAL_DOMAINS};
|
||
|
||
ssl_certificate /etc/ssl/certs/$MAIN_DOMAIN.crt;
|
||
ssl_certificate_key /etc/ssl/private/$MAIN_DOMAIN.key;
|
||
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||
ssl_prefer_server_ciphers off;
|
||
|
||
# Redirect HTTP to HTTPS
|
||
if (\$scheme != "https") {
|
||
return 301 https://\$host\$request_uri;
|
||
}
|
||
|
||
# Include all location blocks from HTTP server
|
||
include /etc/nginx/snippets/catlink-locations.conf;
|
||
}
|
||
EOF
|
||
|
||
# Создаем общие location блоки
|
||
sudo tee /etc/nginx/snippets/catlink-locations.conf << 'EOF'
|
||
location / {
|
||
proxy_pass http://127.0.0.1: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;
|
||
}
|
||
|
||
location /api/ {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
proxy_http_version 1.1;
|
||
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;
|
||
}
|
||
EOF
|
||
|
||
sudo systemctl reload nginx
|
||
success "Самоподписанные SSL сертификаты созданы"
|
||
else
|
||
log "🔓 SSL пропущен - использование только HTTP"
|
||
fi
|
||
}
|
||
|
||
# Подготовка базы данных
|
||
setup_database() {
|
||
log "🗄️ Настройка и проверка безопасности базы данных..."
|
||
|
||
# Ждем запуска контейнеров
|
||
log "⏳ Ожидание запуска контейнеров..."
|
||
sleep 15
|
||
|
||
# Проверяем статус контейнеров
|
||
if ! make status | grep -q "Up"; then
|
||
warning "Контейнеры могут быть не готовы, ожидаем еще..."
|
||
sleep 10
|
||
fi
|
||
|
||
# Применяем миграции
|
||
log "🔄 Применение миграций базы данных..."
|
||
if ! make migrate; then
|
||
error "Ошибка применения миграций"
|
||
fi
|
||
|
||
# Создаем суперпользователя
|
||
log "👤 Настройка суперпользователя..."
|
||
cat > /tmp/create_superuser.py << 'PYTHON_EOF'
|
||
from django.contrib.auth import get_user_model
|
||
User = get_user_model()
|
||
if not User.objects.filter(username='admin').exists():
|
||
User.objects.create_superuser('admin', 'admin@example.com', 'admin123')
|
||
print('✅ Суперпользователь admin создан с паролем admin123')
|
||
print('⚠️ ВАЖНО: Смените пароль после первого входа!')
|
||
else:
|
||
print('ℹ️ Суперпользователь уже существует')
|
||
PYTHON_EOF
|
||
|
||
# Выполняем скрипт создания суперпользователя
|
||
if make shell-exec CMD="python manage.py shell < /tmp/create_superuser.py" 2>/dev/null; then
|
||
success "Суперпользователь настроен"
|
||
else
|
||
# Fallback - используем прямую команду
|
||
$(./scripts/detect-docker-compose.sh) exec web python manage.py shell < /tmp/create_superuser.py || warning "Не удалось создать суперпользователя автоматически"
|
||
fi
|
||
|
||
# Удаляем временный файл
|
||
rm -f /tmp/create_superuser.py
|
||
|
||
# Запускаем аудит безопасности БД
|
||
if [[ -f "./scripts/audit-db-security.sh" ]]; then
|
||
log "🔍 Запуск аудита безопасности базы данных..."
|
||
./scripts/audit-db-security.sh
|
||
fi
|
||
|
||
# Применяем настройки безопасности БД
|
||
if [[ -f "./scripts/setup-db-security.sh" ]]; then
|
||
log "🔒 Применение настроек безопасности базы данных..."
|
||
./scripts/setup-db-security.sh
|
||
fi
|
||
|
||
success "База данных настроена и защищена"
|
||
}
|
||
|
||
# Создание backup системы
|
||
setup_backup_system() {
|
||
log "💾 Настройка системы резервного копирования..."
|
||
|
||
# Создаем директории для backup
|
||
mkdir -p backups/{database,files,configs}
|
||
|
||
# Создаем скрипт backup
|
||
cat > scripts/auto-backup.sh << 'EOF'
|
||
#!/bin/bash
|
||
# Автоматический backup CatLink
|
||
|
||
set -e
|
||
|
||
BACKUP_DIR="/opt/links/backups"
|
||
DATE=$(date +%Y%m%d_%H%M%S)
|
||
|
||
# Database backup
|
||
docker exec links-db-1 pg_dump -U postgres links_db | gzip > "$BACKUP_DIR/database/links_db_$DATE.sql.gz"
|
||
|
||
# Files backup
|
||
tar -czf "$BACKUP_DIR/files/media_$DATE.tar.gz" backend/storage/
|
||
|
||
# Config backup
|
||
cp .env "$BACKUP_DIR/configs/env_$DATE"
|
||
cp docker-compose.yml "$BACKUP_DIR/configs/docker-compose_$DATE.yml"
|
||
|
||
# Cleanup old backups (keep 30 days)
|
||
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
|
||
find "$BACKUP_DIR" -name "*.sql" -mtime +30 -delete
|
||
|
||
echo "Backup completed: $DATE"
|
||
EOF
|
||
|
||
chmod +x scripts/auto-backup.sh
|
||
|
||
# Добавляем в crontab
|
||
(crontab -l 2>/dev/null || true; echo "0 2 * * * cd /opt/links && ./scripts/auto-backup.sh >> logs/backup.log 2>&1") | crontab -
|
||
|
||
success "Система backup настроена (ежедневно в 2:00)"
|
||
}
|
||
|
||
# Создание мониторинга
|
||
setup_monitoring() {
|
||
log "📊 Настройка базового мониторинга..."
|
||
|
||
# Создаем скрипт мониторинга
|
||
cat > scripts/health-check.sh << 'EOF'
|
||
#!/bin/bash
|
||
# Health check для CatLink
|
||
|
||
check_service() {
|
||
if docker-compose ps | grep -q "$1.*Up"; then
|
||
echo "✅ $1: OK"
|
||
return 0
|
||
else
|
||
echo "❌ $1: FAILED"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
echo "🏥 CatLink Health Check - $(date)"
|
||
echo "================================"
|
||
|
||
check_service "web"
|
||
check_service "db"
|
||
check_service "frontend"
|
||
|
||
echo ""
|
||
echo "🌐 URL checks:"
|
||
curl -s -o /dev/null -w "Frontend: %{http_code}\n" http://localhost:3000/ || echo "Frontend: FAILED"
|
||
curl -s -o /dev/null -w "Backend: %{http_code}\n" http://localhost:8000/api/ || echo "Backend: FAILED"
|
||
|
||
echo ""
|
||
echo "💾 Disk usage:"
|
||
df -h | grep -E "/(|opt|var)"
|
||
|
||
echo ""
|
||
echo "🐳 Docker resources:"
|
||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
|
||
EOF
|
||
|
||
chmod +x scripts/health-check.sh
|
||
|
||
success "Мониторинг настроен (./scripts/health-check.sh)"
|
||
}
|
||
|
||
# Финальная проверка
|
||
final_verification() {
|
||
log "🔍 Финальная проверка развертывания..."
|
||
|
||
# Проверяем контейнеры
|
||
if ! docker-compose ps | grep -q "Up"; then
|
||
error "Контейнеры не запущены"
|
||
fi
|
||
|
||
# Проверяем nginx
|
||
if ! sudo systemctl is-active --quiet nginx; then
|
||
error "nginx не запущен"
|
||
fi
|
||
|
||
# Проверяем доступность сервисов
|
||
sleep 10
|
||
|
||
if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/ | grep -q "200"; then
|
||
success "Frontend доступен"
|
||
else
|
||
warning "Frontend может быть недоступен"
|
||
fi
|
||
|
||
if curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/api/ | grep -q "200\|404"; then
|
||
success "Backend доступен"
|
||
else
|
||
warning "Backend может быть недоступен"
|
||
fi
|
||
|
||
success "Развертывание завершено успешно!"
|
||
}
|
||
|
||
# Отображение итогов
|
||
show_deployment_summary() {
|
||
echo ""
|
||
echo "🎉 ===== РАЗВЕРТЫВАНИЕ ЗАВЕРШЕНО ===== 🎉"
|
||
echo ""
|
||
echo "📋 Информация о развертывании:"
|
||
echo " 🌐 Основной домен: $MAIN_DOMAIN"
|
||
echo " 🔒 SSL режим: $([ "$SSL_MODE" = "1" ] && echo "Let's Encrypt" || [ "$SSL_MODE" = "2" ] && echo "Self-signed" || echo "Disabled")"
|
||
echo " 🏷️ Окружение: $ENVIRONMENT"
|
||
echo " 📧 Email: $LETSENCRYPT_EMAIL"
|
||
echo ""
|
||
echo "🔗 Ссылки:"
|
||
if [[ "$SSL_MODE" == "1" ]] || [[ "$SSL_MODE" == "2" ]]; then
|
||
echo " 🏠 Сайт: https://$MAIN_DOMAIN"
|
||
echo " 🔧 Админка: https://$MAIN_DOMAIN/admin/"
|
||
else
|
||
echo " 🏠 Сайт: http://$MAIN_DOMAIN"
|
||
echo " 🔧 Админка: http://$MAIN_DOMAIN/admin/"
|
||
fi
|
||
echo ""
|
||
echo "👤 Учетные данные:"
|
||
echo " 👨💼 Админ: admin / admin123 (СМЕНИТЕ ПАРОЛЬ!)"
|
||
echo ""
|
||
echo "📂 Важные файлы:"
|
||
echo " ⚙️ Конфигурация: .env"
|
||
echo " 💾 Backup: backups/"
|
||
echo " 📊 Логи: logs/"
|
||
echo ""
|
||
echo "🛠️ Полезные команды:"
|
||
echo " 📊 Проверка здоровья: ./scripts/health-check.sh"
|
||
echo " 💾 Backup: ./scripts/auto-backup.sh"
|
||
echo " 🔍 Аудит БД: make security-audit"
|
||
echo " 📝 Логи: make logs"
|
||
echo " 🔄 Перезапуск: make restart"
|
||
echo ""
|
||
echo "🆘 Поддержка:"
|
||
echo " 📖 Документация: README.md"
|
||
echo " 🔒 Безопасность: SECURITY.md"
|
||
echo ""
|
||
success "Проект готов к использованию!"
|
||
}
|
||
|
||
# Основная функция
|
||
main() {
|
||
echo "🚀 ========================================"
|
||
echo "🚀 CatLink Master Deployment Script "
|
||
echo "🚀 ========================================"
|
||
echo ""
|
||
|
||
# Проверяем аргументы командной строки
|
||
SKIP_CHECKS=false
|
||
for arg in "$@"; do
|
||
case $arg in
|
||
--skip-checks)
|
||
SKIP_CHECKS=true
|
||
shift
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Проверяем что мы в правильной директории
|
||
if [ ! -f "docker-compose.yml" ]; then
|
||
error "Файл docker-compose.yml не найден. Запустите скрипт из корня проекта."
|
||
fi
|
||
|
||
# Создаем необходимые директории
|
||
mkdir -p {logs,backups,scripts}
|
||
|
||
# Основной процесс развертывания
|
||
if [ "$SKIP_CHECKS" = false ]; then
|
||
check_requirements
|
||
else
|
||
warning "Пропуск проверки системных требований"
|
||
# Определяем Docker Compose команду без полной проверки
|
||
if command -v docker-compose &> /dev/null; then
|
||
DOCKER_COMPOSE_CMD="docker-compose"
|
||
elif docker compose version &> /dev/null; then
|
||
DOCKER_COMPOSE_CMD="docker compose"
|
||
else
|
||
error "Docker Compose не найден"
|
||
fi
|
||
fi
|
||
|
||
collect_deployment_info
|
||
generate_env_file
|
||
setup_nginx
|
||
|
||
log "🏗️ Сборка и запуск контейнеров..."
|
||
# Используем Makefile команды которые автоматически определяют Docker Compose
|
||
if ! make build-prod; then
|
||
error "Ошибка сборки контейнеров"
|
||
fi
|
||
|
||
if ! make up-prod; then
|
||
error "Ошибка запуска контейнеров"
|
||
fi
|
||
|
||
setup_database
|
||
setup_ssl
|
||
setup_backup_system
|
||
setup_monitoring
|
||
final_verification
|
||
show_deployment_summary
|
||
|
||
echo ""
|
||
echo "🎊 Развертывание CatLink завершено успешно!"
|
||
}
|
||
|
||
# Обработка сигналов
|
||
trap 'error "Развертывание прервано пользователем"' INT TERM
|
||
|
||
# Запуск
|
||
main "$@" |