This commit is contained in:
Халимов Рустам
2026-03-10 21:11:59 +03:00
parent a3c0d35263
commit b4b4b8e0d3
80 changed files with 23310 additions and 0 deletions

440
deploy.sh Normal file
View File

@@ -0,0 +1,440 @@
#!/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 ""