|
|
@@ -0,0 +1,218 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+# ===========================================
|
|
|
+# PhotoPlaces — Удалённый деплой на тестовый сервер
|
|
|
+# ===========================================
|
|
|
+# Запуск на сервере 192.168.88.128:
|
|
|
+# curl -fsSL https://raw.githubusercontent.com/foxtime/photoplaces/main/deploy/deploy-remote.sh | bash
|
|
|
+# Или склонировав репо и запустив:
|
|
|
+# cd /home/foxtime/photoplaces && bash deploy/deploy-remote.sh
|
|
|
+# ===========================================
|
|
|
+
|
|
|
+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 "Клонирую репозиторий..."
|
|
|
+ git clone --branch "$BRANCH" "$REPO_URL" "$PROJECT_DIR"
|
|
|
+ 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)
|
|
|
+ if docker compose -f deploy/docker-compose.prod.yml exec -T caddy wget -q --spider http://localhost:2019/metrics 2>/dev/null; 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 "$@"
|