#!/usr/bin/env bash # Деплой на VPS. Образы собираются на самом сервере (нативный amd64; под эмуляцией на Mac pnpm падает), # после сборки кеш чистится — сервер общий и диск небольшой. # infra/prod/deploy.sh — синхронизировать код, собрать, перезапустить # ONLY=app infra/prod/deploy.sh — собрать только образ app (API/воркер/боты/лендинг) set -euo pipefail cd "$(dirname "$0")/../.." HOST=${DEPLOY_HOST:-root@69.62.120.231} KEY=${DEPLOY_KEY:-$HOME/.ssh/vps_claude} DIR=/opt/barev DOMAIN=${BAREV_DOMAIN:-barevclub.com} BOT=${CLIENT_BOT_USERNAME:-barevclub_bot} r() { ssh -i "$KEY" "$HOST" "$@"; } # один деплой за раз: два параллельных запуска ломают пересоздание контейнеров (конфликт имён → 502) LOCK=$DIR/.deploy-lock if ! r "mkdir -p $DIR && mkdir $LOCK 2>/dev/null"; then echo "✗ на сервере уже идёт деплой (есть $LOCK). Дождитесь его окончания." echo " Если прошлый деплой оборвался, снимите блокировку: ssh -i $KEY $HOST rmdir $LOCK" exit 1 fi trap 'r "rmdir $LOCK" 2>/dev/null || true' EXIT echo "→ синхронизация кода" r "mkdir -p $DIR/src" rsync -az --delete -e "ssh -i $KEY" \ --exclude node_modules --exclude .next --exclude dist --exclude .turbo --exclude '*.tsbuildinfo' \ --exclude .git --exclude .env --exclude docs/design --exclude i18n-review --exclude '/*.pdf' \ ./ "$HOST:$DIR/src/" scp -q -i "$KEY" infra/prod/docker-compose.yml "$HOST:$DIR/docker-compose.yml" # секреты генерируются один раз и дальше не перезаписываются if ! r "test -f $DIR/.env"; then echo "→ создаю $DIR/.env с новыми секретами" rnd() { openssl rand -hex 24; } DBP=$(rnd); S3K=$(rnd); ADMINP=$(openssl rand -base64 12 | tr -d '/+=') sed -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$DBP|" \ -e "s|^DATABASE_URL=.*|DATABASE_URL=postgresql://barev:$DBP@barev-db:5432/barev|" \ -e "s|^ADMIN_SESSION_SECRET=.*|ADMIN_SESSION_SECRET=$(rnd)|" \ -e "s|^SEED_OWNER_PASSWORD=.*|SEED_OWNER_PASSWORD=$ADMINP|" \ -e "s|^S3_SECRET_KEY=.*|S3_SECRET_KEY=$S3K|" \ -e "s|^CLIENT_BOT_TOKEN=.*|CLIENT_BOT_TOKEN=$(grep ^CLIENT_BOT_TOKEN= .env | cut -d= -f2-)|" \ -e "s|^PRO_BOT_TOKEN=.*|PRO_BOT_TOKEN=$(grep ^PRO_BOT_TOKEN= .env | cut -d= -f2-)|" \ infra/prod/env.example | r "umask 077 && cat > $DIR/.env" sed "s|barev-secret|$S3K|" infra/prod/seaweed-s3.json.template | r "cat > $DIR/seaweed-s3.json" # ключ шифрования файлов в S3 (SSE-S3) printf '[s3.sse]\nkek = "%s"\n' "$(openssl rand -hex 32)" | r "cat > $DIR/security.toml && chmod 644 $DIR/security.toml" echo " пароль владельца админки: $ADMINP" fi for target in ${ONLY:-web app}; do echo "→ сборка barev-$target" r "cd $DIR/src && nice -n 10 docker build --network host -f infra/prod/Dockerfile --target $target \ --build-arg LANDING_URL=https://$DOMAIN --build-arg API_PUBLIC_URL=https://api.$DOMAIN \ --build-arg CLIENT_BOT_USERNAME=$BOT -t barev-$target:latest . > /tmp/barev-build-$target.log 2>&1 \ || (tail -40 /tmp/barev-build-$target.log; exit 1)" done echo "→ запуск" r "cd $DIR && docker compose up -d --remove-orphans && docker builder prune -af >/dev/null && docker image prune -f >/dev/null" r "cd $DIR && docker compose ps --format '{{.Name}}\t{{.Status}}' && df -h / | tail -1"