#!/usr/bin/env bash # =========================================== # PhotoPlaces — Удалённый деплой на тестовый сервер # =========================================== # Запуск на сервере 192.168.88.128: # curl -fsSL http://192.168.88.96:10880/foxtime/photoplaces/raw/main/deploy/deploy-remote.sh | bash # Или склонировав репо и запустив: # cd /home/foxtime/photoplaces && bash deploy/deploy-remote.sh # =========================================== # ВНИМАНИЕ: Если репозиторий приватный, нужен токен доступа: # git clone http://TOKEN@192.168.88.96:10880/foxtime/photoplaces.git set -euo pipefail # Цвета для вывода RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } log_ok() { echo -e "${GREEN}[OK]${NC} $*"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } log_err() { echo -e "${RED}[ERR]${NC} $*"; } # Конфигурация REPO_URL="http://192.168.88.96:10880/foxtime/photoplaces.git" PROJECT_DIR="/home/foxtime/photoplaces" BRANCH="main" ENV_FILE="$PROJECT_DIR/deploy/env.prod" log_info "=== PhotoPlaces Remote Deploy ===" log_info "Сервер: $(hostname) ($(hostname -I | awk '{print $1}'))" log_info "Пользователь: $(whoami)" log_info "Директория: $PROJECT_DIR" # 1. Проверка зависимостей check_deps() { log_info "Проверка зависимостей..." local missing=() for cmd in docker git curl openssl; do if ! command -v "$cmd" >/dev/null 2>&1; then missing+=("$cmd") fi done if ! docker compose version >/dev/null 2>&1; then missing+=("docker compose") fi if [ ${#missing[@]} -gt 0 ]; then log_err "Отсутствуют: ${missing[*]}" log_info "Установите: sudo apt update && sudo apt install -y docker.io docker-compose-plugin git curl openssl" exit 1 fi log_ok "Все зависимости установлены" } # 2. Клонирование/обновление репозитория setup_repo() { log_info "Настройка репозитория..." if [ -d "$PROJECT_DIR/.git" ]; then log_info "Репозиторий существует, обновляю..." cd "$PROJECT_DIR" git fetch origin git reset --hard "origin/$BRANCH" git clean -fd else log_info "Клонирую репозиторий..." if ! git clone --branch "$BRANCH" "$REPO_URL" "$PROJECT_DIR" 2>&1; then log_err "Не удалось клонировать репозиторий. Возможно, он приватный." log_info "Создайте токен доступа в Gitea (Settings → Applications → Generate Token)" log_info "И запустите: git clone http://TOKEN@192.168.88.96:10880/foxtime/photoplaces.git $PROJECT_DIR" exit 1 fi cd "$PROJECT_DIR" fi log_ok "Репозиторий готов: $(git rev-parse --short HEAD)" } # 3. Настройка env.prod setup_env() { log_info "Настройка переменных окружения..." cd "$PROJECT_DIR" if [ ! -f "$ENV_FILE" ]; then cp deploy/env.prod.example "$ENV_FILE" log_info "Создан $ENV_FILE из шаблона" fi # Автогенерация секретов если пустые или CHANGE_ME generate_secret() { local var="$1" local current="${!var:-}" if [[ -z "$current" || "$current" == *"CHANGE_ME"* ]]; then local secret secret=$(openssl rand -hex 32) if [[ "$OSTYPE" == "darwin"* ]]; then sed -i '' "s/^${var}=.*/${var}=${secret}/" "$ENV_FILE" else sed -i "s/^${var}=.*/${var}=${secret}/" "$ENV_FILE" fi export "$var=$secret" log_ok "$var сгенерирован автоматически" fi } # Загружаем текущие значения set -a source "$ENV_FILE" set +a generate_secret "DB_PASSWORD" generate_secret "REDIS_PASSWORD" generate_secret "JWT_SECRET" generate_secret "JWT_REFRESH_SECRET" generate_secret "S3_SECRET_KEY" # Проверка обязательных переменных local required=("DB_PASSWORD" "REDIS_PASSWORD" "JWT_SECRET" "JWT_REFRESH_SECRET" "S3_SECRET_KEY" "YANDEX_MAPS_API_KEY") local missing=() for var in "${required[@]}"; do if [ -z "${!var:-}" ]; then missing+=("$var") fi done if [ ${#missing[@]} -gt 0 ]; then log_warn "Требуют ручного заполнения в $ENV_FILE:" for var in "${missing[@]}"; do echo " - $var" done log_info "Откройте $ENV_FILE и заполните недостающие значения, затем запустите скрипт снова" exit 1 fi log_ok "Все секреты настроены" } # 4. Создание Docker secrets (если Swarm) create_docker_secrets() { if docker info --format '{{.Swarm.LocalNodeState}}' 2>/dev/null | grep -q "active"; then log_info "Swarm mode: создание Docker secrets..." printf "%s" "$DB_PASSWORD" | docker secret create db_password - 2>/dev/null || docker secret rm db_password && printf "%s" "$DB_PASSWORD" | docker secret create db_password - printf "%s" "$REDIS_PASSWORD" | docker secret create redis_password - 2>/dev/null || docker secret rm redis_password && printf "%s" "$REDIS_PASSWORD" | docker secret create redis_password - printf "%s" "$JWT_SECRET" | docker secret create jwt_secret - 2>/dev/null || docker secret rm jwt_secret && printf "%s" "$JWT_SECRET" | docker secret create jwt_secret - printf "%s" "$JWT_REFRESH_SECRET" | docker secret create jwt_refresh_secret - 2>/dev/null || docker secret rm jwt_refresh_secret && printf "%s" "$JWT_REFRESH_SECRET" | docker secret create jwt_refresh_secret - printf "%s" "$S3_SECRET_KEY" | docker secret create s3_secret_key - 2>/dev/null || docker secret rm s3_secret_key && printf "%s" "$S3_SECRET_KEY" | docker secret create s3_secret_key - log_ok "Docker secrets созданы" fi } # 5. Деплой через docker compose deploy() { log_info "Запуск docker compose..." cd "$PROJECT_DIR" docker compose -f deploy/docker-compose.prod.yml up -d --build --remove-orphans log_ok "Контейнеры запущены" } # 6. Health checks health_check() { log_info "Ожидание готовности сервисов (до 120 сек)..." local max_attempts=60 local attempt=0 while [ $attempt -lt $max_attempts ]; do attempt=$((attempt + 1)) # Проверка Caddy (порт 2019 metrics) — curl есть в alpine if docker compose -f deploy/docker-compose.prod.yml exec -T caddy curl -sf http://localhost:2019/metrics >/dev/null 2>&1; then # Проверка backend через Caddy (HTTP, SSL на Synology) if curl -sf "http://localhost/api/v1/health" >/dev/null 2>&1; then log_ok "Backend здоров" break fi fi if [ $attempt -eq $max_attempts ]; then log_err "Таймаут ожидания здоровья" docker compose -f deploy/docker-compose.prod.yml logs --tail=50 caddy backend exit 1 fi sleep 2 done # Финальная проверка через HTTPS (Synology) log_info "Проверка через HTTPS (Synology)..." if curl -sf "https://photoplaces.ru/api/v1/health" >/dev/null 2>&1; then log_ok "HTTPS health check пройден" else log_warn "HTTPS check не прошёл (проверьте настройки Synology Reverse Proxy)" fi } # 7. Вывод результатов show_result() { echo log_ok "=== ДЕПЛОЙ УСПЕШНО ЗАВЕРШЁН ===" echo echo " Фронтенд: https://photoplaces.ru" echo " API: https://api.photoplaces.ru/api/v1" echo " S3 API: https://api.photoplaces.ru/s3" echo " MinIO Console: https://api.photoplaces.ru/minio-console/" echo echo " Логи: cd $PROJECT_DIR && docker compose -f deploy/docker-compose.prod.yml logs -f" echo " Статус: cd $PROJECT_DIR && docker compose -f deploy/docker-compose.prod.yml ps" echo " Остановка: cd $PROJECT_DIR && docker compose -f deploy/docker-compose.prod.yml down" echo } # =========================================== # MAIN # =========================================== main() { check_deps setup_repo setup_env create_docker_secrets deploy health_check show_result } main "$@"