Files
forkmessager/deploy.sh
Халимов Рустам b4b4b8e0d3 Original
2026-03-10 21:11:59 +03:00

441 lines
17 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ═══════════════════════════════════════════════════════════════
# Vortex Messenger — полный деплой на чистый VPS
# ═══════════════════════════════════════════════════════════════
#
# Требования:
# - Чистый VPS с Ubuntu 22.04 или 24.04
# - Домен с A-записью, направленной на IP сервера
#
# Инструкция:
# 1. Загрузите весь проект на сервер:
# scp -r ./* root@<IP>:/var/www/vortex/
# 2. Зайдите на сервер: ssh root@<IP>
# 3. Запустите:
# chmod +x /var/www/vortex/deploy.sh
# /var/www/vortex/deploy.sh
#
# ═══════════════════════════════════════════════════════════════
set -e
# ─── Цвета ───
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
print_step() { echo -e "\n${CYAN}━━━ $1 ━━━${NC}\n"; }
print_ok() { echo -e "${GREEN}$1${NC}"; }
print_warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
print_error(){ echo -e "${RED}$1${NC}"; }
# ─── Проверки ───
if [ "$EUID" -ne 0 ]; then
print_error "Запустите с правами root: sudo ./deploy.sh"
exit 1
fi
APP_DIR="/var/www/vortex"
if [ ! -f "$APP_DIR/package.json" ]; then
print_error "Файлы проекта не найдены в $APP_DIR!"
echo ""
echo "Сначала загрузите проект на сервер:"
echo " scp -r ./* root@<IP>:$APP_DIR/"
exit 1
fi
# ─── Ввод параметров ───
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════════════════╗"
echo "║ 🌀 Vortex Messenger — Деплой ║"
echo "╚═══════════════════════════════════════════════════╝"
echo -e "${NC}"
read -p "Введите домен (например messenger.ru): " DOMAIN
if [ -z "$DOMAIN" ]; then
print_error "Домен не указан!"
exit 1
fi
read -p "Введите email для SSL-сертификата: " SSL_EMAIL
if [ -z "$SSL_EMAIL" ]; then
print_error "Email не указан!"
exit 1
fi
# Генерация безопасных ключей
DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
JWT_SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c 64)
ENCRYPTION_KEY=$(openssl rand -hex 32)
TURN_SECRET=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
echo ""
print_ok "Параметры приняты. Начинаю установку..."
echo ""
# ═══════════════════════════════════════
# Шаг 1: Обновление системы
# ═══════════════════════════════════════
print_step "1/10 — Обновление системы"
export DEBIAN_FRONTEND=noninteractive
apt update && apt upgrade -y
apt install -y curl wget gnupg2 software-properties-common git unzip build-essential
print_ok "Система обновлена"
# ═══════════════════════════════════════
# Шаг 2: Node.js 20
# ═══════════════════════════════════════
print_step "2/10 — Установка Node.js 20"
if ! command -v node &>/dev/null || [[ $(node -v | cut -d. -f1 | tr -d v) -lt 20 ]]; then
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
fi
npm install -g pm2
print_ok "Node.js $(node -v), npm $(npm -v), PM2 установлен"
# ═══════════════════════════════════════
# Шаг 3: PostgreSQL
# ═══════════════════════════════════════
print_step "3/10 — Установка PostgreSQL"
apt install -y postgresql postgresql-contrib
systemctl enable postgresql
systemctl start postgresql
sudo -u postgres psql -c "DO \$\$ BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'vortex') THEN
CREATE ROLE vortex WITH LOGIN PASSWORD '${DB_PASSWORD}';
END IF;
END \$\$;"
sudo -u postgres psql -c "SELECT 1 FROM pg_database WHERE datname = 'vortex'" | grep -q 1 || \
sudo -u postgres createdb -O vortex vortex
print_ok "PostgreSQL готов (БД: vortex, пользователь: vortex)"
# ═══════════════════════════════════════
# Шаг 4: Nginx
# ═══════════════════════════════════════
print_step "4/10 — Установка Nginx"
apt install -y nginx
systemctl enable nginx
print_ok "Nginx установлен"
# ═══════════════════════════════════════
# Шаг 5: Certbot (SSL)
# ═══════════════════════════════════════
print_step "5/10 — Установка Certbot"
apt install -y certbot python3-certbot-nginx
print_ok "Certbot установлен"
# ═══════════════════════════════════════
# Шаг 6: coturn (TURN-сервер для звонков)
# ═══════════════════════════════════════
print_step "6/10 — Установка coturn (TURN)"
apt install -y coturn
cat > /etc/turnserver.conf << EOF
listening-port=3478
tls-listening-port=5349
realm=${DOMAIN}
server-name=${DOMAIN}
lt-cred-mech
use-auth-secret
static-auth-secret=${TURN_SECRET}
fingerprint
no-cli
no-tlsv1
no-tlsv1_1
EOF
sed -i 's/#TURNSERVER_ENABLED=1/TURNSERVER_ENABLED=1/' /etc/default/coturn 2>/dev/null || true
systemctl enable coturn
systemctl restart coturn
print_ok "coturn настроен"
# ═══════════════════════════════════════
# Шаг 7: Настройка окружения (.env)
# ═══════════════════════════════════════
print_step "7/10 — Создание .env"
mkdir -p ${APP_DIR}/apps/server/uploads/avatars
cat > ${APP_DIR}/apps/server/.env << EOF
DATABASE_URL=postgresql://vortex:${DB_PASSWORD}@localhost:5432/vortex
JWT_SECRET=${JWT_SECRET}
ENCRYPTION_KEY=${ENCRYPTION_KEY}
PORT=3001
NODE_ENV=production
CORS_ORIGINS=https://${DOMAIN}
MAX_REGISTRATIONS_PER_IP=5
TURN_URL=turn:${DOMAIN}:3478
TURN_SECRET=${TURN_SECRET}
STUN_URLS=stun:stun.l.google.com:19302,stun:${DOMAIN}:3478
EOF
print_ok "Файл .env создан"
# ═══════════════════════════════════════
# Шаг 8: Установка зависимостей и сборка
# ═══════════════════════════════════════
print_step "8/10 — Установка зависимостей и сборка"
cd ${APP_DIR}
# Установка зависимостей (npm workspaces)
npm install --legacy-peer-deps
print_ok "Зависимости установлены"
# --- Бэкенд ---
cd ${APP_DIR}/apps/server
npx prisma generate
npx prisma db push --accept-data-loss
print_ok "БД синхронизирована"
npx tsc
print_ok "Бэкенд скомпилирован (dist/)"
# --- Фронтенд ---
cd ${APP_DIR}/apps/web
npx vite build
print_ok "Фронтенд собран (dist/)"
# ═══════════════════════════════════════
# Шаг 9: Nginx конфигурация
# ═══════════════════════════════════════
print_step "9/10 — Настройка Nginx"
cat > /etc/nginx/sites-available/vortex << EOF
server {
listen 80;
server_name ${DOMAIN};
root ${APP_DIR}/apps/web/dist;
index index.html;
client_max_body_size 50M;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
gzip_min_length 1000;
location / {
try_files \$uri \$uri/ /index.html;
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
location /api/ {
proxy_pass http://127.0.0.1:3001;
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;
client_max_body_size 50M;
}
location /socket.io/ {
proxy_pass http://127.0.0.1:3001;
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_read_timeout 86400;
}
location /uploads/ {
proxy_pass http://127.0.0.1:3001/uploads/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
}
}
EOF
# Блокировка доступа по голому IP
cat > /etc/nginx/sites-available/block-ip << EOF
server {
listen 80 default_server;
server_name _;
return 444;
}
EOF
ln -sf /etc/nginx/sites-available/vortex /etc/nginx/sites-enabled/
ln -sf /etc/nginx/sites-available/block-ip /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
print_ok "Nginx настроен"
# --- SSL ---
print_step "Получение SSL-сертификата"
certbot --nginx -d ${DOMAIN} --non-interactive --agree-tos -m ${SSL_EMAIL} 2>&1 || {
print_warn "SSL не удалось получить автоматически."
print_warn "Проверьте что DNS A-запись ${DOMAIN} указывает на IP этого сервера."
print_warn "После настройки DNS выполните вручную:"
echo " certbot --nginx -d ${DOMAIN}"
}
# Обновить block-ip для HTTPS тоже
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
cat > /etc/nginx/sites-available/block-ip << EOF
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
return 444;
}
EOF
nginx -t && systemctl reload nginx
print_ok "Доступ по IP заблокирован (HTTP и HTTPS)"
fi
# ═══════════════════════════════════════
# Шаг 10: Запуск через PM2
# ═══════════════════════════════════════
print_step "10/10 — Запуск сервера"
cd ${APP_DIR}/apps/server
pm2 delete vortex-server 2>/dev/null || true
pm2 start dist/index.js --name vortex-server --cwd ${APP_DIR}/apps/server
pm2 save
pm2 startup systemd -u root --hp /root 2>/dev/null || pm2 startup
print_ok "Сервер запущен"
# ═══════════════════════════════════════
# Файрвол
# ═══════════════════════════════════════
if command -v ufw &>/dev/null; then
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3478/tcp
ufw allow 3478/udp
ufw --force enable
print_ok "Файрвол настроен"
fi
# ═══════════════════════════════════════
# Скрипт обновления (для будущих апдейтов)
# ═══════════════════════════════════════
cat > ${APP_DIR}/update.sh << 'UPDATESCRIPT'
#!/bin/bash
set -e
APP_DIR="/var/www/vortex"
ARCHIVE="/root/vortex-deploy.tar.gz"
ENV_BACKUP="/root/vortex-env-backup"
echo ""
echo "VORTEX - Update server"
echo ""
if [ ! -f "$ARCHIVE" ]; then
echo "Архив не найден: $ARCHIVE"; exit 1
fi
echo "1/7 Остановка сервера"
pm2 stop vortex-server 2>/dev/null || true
echo "2/7 Бэкап .env"
if [ -f "$APP_DIR/apps/server/.env" ]; then
cp "$APP_DIR/apps/server/.env" "$ENV_BACKUP"
fi
echo "3/7 Бэкап аватаров"
if [ -d "$APP_DIR/apps/server/uploads/avatars" ]; then
cp -r "$APP_DIR/apps/server/uploads/avatars" /root/vortex-avatars-backup 2>/dev/null || true
fi
echo "4/7 Распаковка обновления"
tar -xzf "$ARCHIVE" -C "$APP_DIR"
if [ -f "$ENV_BACKUP" ]; then
cp "$ENV_BACKUP" "$APP_DIR/apps/server/.env"
fi
if [ -d "/root/vortex-avatars-backup" ]; then
mkdir -p "$APP_DIR/apps/server/uploads/avatars"
cp -r /root/vortex-avatars-backup/* "$APP_DIR/apps/server/uploads/avatars/" 2>/dev/null || true
rm -rf /root/vortex-avatars-backup
fi
echo "5/7 Установка зависимостей"
cd "$APP_DIR"
npm install --legacy-peer-deps
echo "6/7 Сборка сервера"
cd "$APP_DIR/apps/server"
npx prisma generate
npx prisma db push --accept-data-loss
npx tsc
echo "7/7 Запуск сервера"
pm2 restart vortex-server
pm2 save
rm -f "$ARCHIVE"
echo ""
echo "Готово! Проверка: pm2 logs vortex-server --lines 10"
UPDATESCRIPT
chmod +x ${APP_DIR}/update.sh
# ═══════════════════════════════════════
# Итог
# ═══════════════════════════════════════
SERVER_IP=$(curl -s4 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ 🎉 Vortex Messenger развёрнут! ║${NC}"
echo -e "${GREEN}╠═══════════════════════════════════════════════════╣${NC}"
echo -e "${GREEN}${NC} ${GREEN}${NC}"
echo -e "${GREEN}${NC} 🌐 Сайт: https://${DOMAIN}${NC}"
echo -e "${GREEN}${NC} 📡 IP: ${SERVER_IP}${NC}"
echo -e "${GREEN}${NC} ${GREEN}${NC}"
echo -e "${GREEN}${NC} 📂 Проект: ${APP_DIR}${NC}"
echo -e "${GREEN}${NC} 🔧 Env: ${APP_DIR}/apps/server/.env${NC}"
echo -e "${GREEN}${NC} 🔄 Апдейт: ${APP_DIR}/update.sh${NC}"
echo -e "${GREEN}${NC} ${GREEN}${NC}"
echo -e "${GREEN}╠═══════════════════════════════════════════════════╣${NC}"
echo -e "${GREEN}${NC} Команды: ${GREEN}${NC}"
echo -e "${GREEN}${NC} pm2 logs vortex-server — логи ${GREEN}${NC}"
echo -e "${GREEN}${NC} pm2 restart vortex-server — перезапуск ${GREEN}${NC}"
echo -e "${GREEN}${NC} pm2 monit — мониторинг ${GREEN}${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
# Сохранить credentials
cat > ${APP_DIR}/CREDENTIALS.txt << EOF
=== Vortex Messenger — Credentials ===
Дата: $(date)
Домен: ${DOMAIN}
IP: ${SERVER_IP}
--- База данных ---
Host: localhost:5432
Database: vortex
User: vortex
Password: ${DB_PASSWORD}
--- Приложение ---
JWT_SECRET=${JWT_SECRET}
ENCRYPTION_KEY=${ENCRYPTION_KEY}
--- TURN сервер ---
TURN_URL=turn:${DOMAIN}:3478
TURN_SECRET=${TURN_SECRET}
EOF
chmod 600 ${APP_DIR}/CREDENTIALS.txt
echo ""
print_ok "Credentials сохранены в ${APP_DIR}/CREDENTIALS.txt"
echo ""