405 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| #!/bin/bash
 | ||
| # Скрипт развертывания AI Code Review Platform на Ubuntu с systemd
 | ||
| 
 | ||
| set -e
 | ||
| 
 | ||
| # Цвета для вывода
 | ||
| RED='\033[0;31m'
 | ||
| GREEN='\033[0;32m'
 | ||
| YELLOW='\033[1;33m'
 | ||
| BLUE='\033[0;34m'
 | ||
| NC='\033[0m' # No Color
 | ||
| 
 | ||
| # Проверка что скрипт запущен с sudo
 | ||
| if [ "$EUID" -ne 0 ]; then 
 | ||
|     echo -e "${RED}Ошибка: Этот скрипт должен быть запущен с sudo${NC}"
 | ||
|     echo "Используйте: sudo ./deploy-ubuntu.sh"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| # Получаем имя реального пользователя (не root)
 | ||
| REAL_USER=${SUDO_USER:-$USER}
 | ||
| REAL_HOME=$(eval echo ~$REAL_USER)
 | ||
| 
 | ||
| echo -e "${BLUE}"
 | ||
| echo "========================================="
 | ||
| echo "  AI Code Review Platform - Установка"
 | ||
| echo "========================================="
 | ||
| echo -e "${NC}"
 | ||
| 
 | ||
| # 1. Проверка зависимостей
 | ||
| echo -e "${YELLOW}[1/10] Проверка системных зависимостей...${NC}"
 | ||
| 
 | ||
| # Проверка Python
 | ||
| if ! command -v python3 &> /dev/null; then
 | ||
|     echo -e "${RED}Python 3 не установлен!${NC}"
 | ||
|     echo "Установка Python 3..."
 | ||
|     apt-get update
 | ||
|     apt-get install -y python3 python3-pip python3-venv
 | ||
| fi
 | ||
| 
 | ||
| # Проверка Node.js
 | ||
| if ! command -v node &> /dev/null; then
 | ||
|     echo -e "${RED}Node.js не установлен!${NC}"
 | ||
|     echo "Установка Node.js..."
 | ||
|     curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
 | ||
|     apt-get install -y nodejs
 | ||
| fi
 | ||
| 
 | ||
| # Установка nginx (опционально)
 | ||
| if ! command -v nginx &> /dev/null; then
 | ||
|     echo -e "${YELLOW}Установить nginx как reverse proxy? [y/N]${NC}"
 | ||
|     read -r install_nginx
 | ||
|     if [[ "$install_nginx" =~ ^[Yy]$ ]]; then
 | ||
|         apt-get install -y nginx
 | ||
|     fi
 | ||
| fi
 | ||
| 
 | ||
| echo -e "${GREEN}✓ Зависимости проверены${NC}"
 | ||
| 
 | ||
| # 2. Определение пути установки
 | ||
| echo -e "${YELLOW}[2/10] Настройка путей...${NC}"
 | ||
| 
 | ||
| # Определить, откуда запущен скрипт
 | ||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 | ||
| CURRENT_DIR="$SCRIPT_DIR"
 | ||
| 
 | ||
| # Спросить пользователя, куда устанавливать
 | ||
| echo "Текущая директория: $CURRENT_DIR"
 | ||
| echo ""
 | ||
| echo "Выберите директорию установки:"
 | ||
| echo "  1) Использовать текущую директорию ($CURRENT_DIR)"
 | ||
| echo "  2) Установить в /opt/ai-review"
 | ||
| echo ""
 | ||
| read -p "Выбор [1/2] (по умолчанию 1): " install_choice
 | ||
| install_choice=${install_choice:-1}
 | ||
| 
 | ||
| if [ "$install_choice" = "2" ]; then
 | ||
|     INSTALL_DIR="/opt/ai-review"
 | ||
|     NEED_COPY=true
 | ||
| else
 | ||
|     INSTALL_DIR="$CURRENT_DIR"
 | ||
|     NEED_COPY=false
 | ||
| fi
 | ||
| 
 | ||
| echo "Директория установки: $INSTALL_DIR"
 | ||
| 
 | ||
| # 3. Копирование файлов (если нужно)
 | ||
| echo -e "${YELLOW}[3/10] Подготовка файлов проекта...${NC}"
 | ||
| 
 | ||
| if [ "$NEED_COPY" = true ]; then
 | ||
|     echo "Копирование в $INSTALL_DIR..."
 | ||
|     mkdir -p "$INSTALL_DIR"
 | ||
|     cp -r "$CURRENT_DIR"/* "$INSTALL_DIR"/ || {
 | ||
|         echo -e "${RED}✗ Ошибка копирования файлов${NC}"
 | ||
|         exit 1
 | ||
|     }
 | ||
|     chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR"
 | ||
|     echo -e "${GREEN}✓ Файлы скопированы${NC}"
 | ||
| else
 | ||
|     echo "Используем текущую директорию"
 | ||
|     chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR"
 | ||
|     echo -e "${GREEN}✓ Права установлены${NC}"
 | ||
| fi
 | ||
| 
 | ||
| cd "$INSTALL_DIR"
 | ||
| 
 | ||
| # 4. Сборка frontend
 | ||
| echo -e "${YELLOW}[4/10] Сборка frontend...${NC}"
 | ||
| cd frontend
 | ||
| 
 | ||
| # Проверить наличие package.json
 | ||
| if [ ! -f "package.json" ]; then
 | ||
|     echo -e "${RED}✗ Не найден package.json${NC}"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| # Создаем .env.production для относительных путей
 | ||
| cat > .env.production << 'EOF'
 | ||
| # Production - используем относительные пути
 | ||
| VITE_API_URL=/api
 | ||
| VITE_WS_URL=
 | ||
| EOF
 | ||
| 
 | ||
| echo "Установка npm зависимостей..."
 | ||
| sudo -u "$REAL_USER" npm install || {
 | ||
|     echo -e "${RED}✗ Ошибка установки npm зависимостей${NC}"
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| echo "Сборка frontend..."
 | ||
| sudo -u "$REAL_USER" npm run build || {
 | ||
|     echo -e "${RED}✗ Ошибка сборки frontend${NC}"
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| # Проверить, что build создан
 | ||
| if [ ! -d "../backend/public" ]; then
 | ||
|     echo -e "${RED}✗ Frontend не собрался (отсутствует backend/public)${NC}"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| cd ..
 | ||
| echo -e "${GREEN}✓ Frontend собран (файлов: $(ls -1 backend/public | wc -l))${NC}"
 | ||
| 
 | ||
| # 5. Установка Python зависимостей
 | ||
| echo -e "${YELLOW}[5/10] Установка Python зависимостей...${NC}"
 | ||
| cd backend
 | ||
| 
 | ||
| # Проверить наличие requirements.txt
 | ||
| if [ ! -f "requirements.txt" ]; then
 | ||
|     echo -e "${RED}✗ Не найден requirements.txt${NC}"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| echo "Создание Python virtual environment..."
 | ||
| sudo -u "$REAL_USER" python3 -m venv venv || {
 | ||
|     echo -e "${RED}✗ Ошибка создания venv${NC}"
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| # Проверить, что venv создан
 | ||
| if [ ! -d "venv" ]; then
 | ||
|     echo -e "${RED}✗ venv не создан${NC}"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| echo "Установка Python пакетов..."
 | ||
| sudo -u "$REAL_USER" bash -c "source venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt" || {
 | ||
|     echo -e "${RED}✗ Ошибка установки Python зависимостей${NC}"
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| echo -e "${GREEN}✓ Python зависимости установлены${NC}"
 | ||
| 
 | ||
| # Применить миграции БД
 | ||
| echo "Применение миграций БД..."
 | ||
| if [ -f "migrate.py" ]; then
 | ||
|     sudo -u "$REAL_USER" bash -c "source venv/bin/activate && python migrate.py" || {
 | ||
|         echo -e "${YELLOW}⚠️  Предупреждение: миграции не применились${NC}"
 | ||
|     }
 | ||
|     
 | ||
|     # Проверить, что БД создана
 | ||
|     if [ -f "review.db" ]; then
 | ||
|         echo -e "${GREEN}✓ База данных создана${NC}"
 | ||
|     else
 | ||
|         echo -e "${YELLOW}⚠️  База данных будет создана при первом запуске${NC}"
 | ||
|     fi
 | ||
| else
 | ||
|     echo -e "${YELLOW}⚠️  Скрипт миграции не найден, БД будет создана автоматически${NC}"
 | ||
| fi
 | ||
| 
 | ||
| cd ..
 | ||
| echo -e "${GREEN}✓ Backend настроен${NC}"
 | ||
| 
 | ||
| # 6. Настройка .env
 | ||
| echo -e "${YELLOW}[6/10] Настройка конфигурации...${NC}"
 | ||
| 
 | ||
| if [ ! -f "backend/.env" ]; then
 | ||
|     echo "Создание .env файла..."
 | ||
|     
 | ||
|     # Генерируем ключ шифрования
 | ||
|     ENCRYPTION_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
 | ||
|     
 | ||
|     cat > backend/.env << EOF
 | ||
| # Ollama Configuration
 | ||
| OLLAMA_BASE_URL=http://localhost:11434
 | ||
| OLLAMA_MODEL=mistral:7b
 | ||
| 
 | ||
| # Database
 | ||
| DATABASE_URL=sqlite+aiosqlite:///$INSTALL_DIR/backend/review.db
 | ||
| 
 | ||
| # Security
 | ||
| SECRET_KEY=$(openssl rand -hex 32)
 | ||
| ENCRYPTION_KEY=$ENCRYPTION_KEY
 | ||
| 
 | ||
| # Master Git Tokens (optional)
 | ||
| MASTER_GITEA_TOKEN=
 | ||
| MASTER_GITHUB_TOKEN=
 | ||
| MASTER_BITBUCKET_TOKEN=
 | ||
| 
 | ||
| # Server
 | ||
| HOST=0.0.0.0
 | ||
| PORT=8000
 | ||
| DEBUG=false
 | ||
| 
 | ||
| # CORS
 | ||
| CORS_ORIGINS=http://localhost:8000,http://localhost:5173
 | ||
| EOF
 | ||
|     
 | ||
|     chown "$REAL_USER":"$REAL_USER" backend/.env
 | ||
|     echo -e "${GREEN}✓ Создан .env файл${NC}"
 | ||
|     echo -e "${YELLOW}⚠️  ВАЖНО: Отредактируйте backend/.env и добавьте необходимые токены!${NC}"
 | ||
| else
 | ||
|     echo ".env уже существует"
 | ||
| fi
 | ||
| 
 | ||
| # 7. Создание systemd service
 | ||
| echo -e "${YELLOW}[7/10] Создание systemd service...${NC}"
 | ||
| 
 | ||
| cat > /etc/systemd/system/ai-review.service << EOF
 | ||
| [Unit]
 | ||
| Description=AI Code Review Platform
 | ||
| After=network.target
 | ||
| 
 | ||
| [Service]
 | ||
| Type=simple
 | ||
| User=$REAL_USER
 | ||
| Group=$REAL_USER
 | ||
| WorkingDirectory=$INSTALL_DIR/backend
 | ||
| Environment="PATH=$INSTALL_DIR/backend/venv/bin:/usr/local/bin:/usr/bin:/bin"
 | ||
| ExecStart=$INSTALL_DIR/backend/venv/bin/python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000
 | ||
| Restart=always
 | ||
| RestartSec=10
 | ||
| StandardOutput=append:/var/log/ai-review/access.log
 | ||
| StandardError=append:/var/log/ai-review/error.log
 | ||
| 
 | ||
| # Security
 | ||
| NoNewPrivileges=true
 | ||
| PrivateTmp=true
 | ||
| ProtectSystem=strict
 | ||
| ProtectHome=true
 | ||
| ReadWritePaths=$INSTALL_DIR/backend
 | ||
| 
 | ||
| [Install]
 | ||
| WantedBy=multi-user.target
 | ||
| EOF
 | ||
| 
 | ||
| echo -e "${GREEN}✓ Systemd service создан${NC}"
 | ||
| 
 | ||
| # 8. Создание директории для логов
 | ||
| echo -e "${YELLOW}[8/10] Настройка логирования...${NC}"
 | ||
| mkdir -p /var/log/ai-review
 | ||
| chown "$REAL_USER":"$REAL_USER" /var/log/ai-review
 | ||
| echo -e "${GREEN}✓ Директория логов создана${NC}"
 | ||
| 
 | ||
| # 9. Включение и запуск сервиса
 | ||
| echo -e "${YELLOW}[9/10] Запуск сервиса...${NC}"
 | ||
| systemctl daemon-reload
 | ||
| systemctl enable ai-review.service
 | ||
| systemctl start ai-review.service
 | ||
| 
 | ||
| # Ждем запуска
 | ||
| echo "Ожидание запуска (5 секунд)..."
 | ||
| sleep 5
 | ||
| 
 | ||
| # Проверка статуса
 | ||
| if systemctl is-active --quiet ai-review.service; then
 | ||
|     echo -e "${GREEN}✓ Сервис успешно запущен${NC}"
 | ||
| else
 | ||
|     echo -e "${RED}✗ Ошибка запуска сервиса${NC}"
 | ||
|     echo ""
 | ||
|     echo -e "${YELLOW}Последние 30 строк логов:${NC}"
 | ||
|     journalctl -u ai-review.service -n 30 --no-pager
 | ||
|     echo ""
 | ||
|     echo -e "${YELLOW}Для детальной диагностики запустите:${NC}"
 | ||
|     echo "  journalctl -u ai-review.service -n 100"
 | ||
|     echo "  cd $INSTALL_DIR/backend && source venv/bin/activate && python -m uvicorn app.main:app"
 | ||
|     echo ""
 | ||
|     echo -e "${YELLOW}Проверьте что:${NC}"
 | ||
|     echo "  1. venv создан: ls -la $INSTALL_DIR/backend/venv"
 | ||
|     echo "  2. Frontend собран: ls -la $INSTALL_DIR/backend/public"
 | ||
|     echo "  3. .env настроен: cat $INSTALL_DIR/backend/.env"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| # 10. Настройка nginx (опционально)
 | ||
| echo -e "${YELLOW}[10/10] Настройка nginx...${NC}"
 | ||
| 
 | ||
| if command -v nginx &> /dev/null; then
 | ||
|     echo -e "${YELLOW}Настроить nginx как reverse proxy? [y/N]${NC}"
 | ||
|     read -r setup_nginx
 | ||
|     
 | ||
|     if [[ "$setup_nginx" =~ ^[Yy]$ ]]; then
 | ||
|         echo "Введите доменное имя (или нажмите Enter для localhost):"
 | ||
|         read -r domain_name
 | ||
|         domain_name=${domain_name:-localhost}
 | ||
|         
 | ||
|         cat > /etc/nginx/sites-available/ai-review << EOF
 | ||
| server {
 | ||
|     listen 80;
 | ||
|     server_name $domain_name;
 | ||
| 
 | ||
|     client_max_body_size 50M;
 | ||
| 
 | ||
|     location / {
 | ||
|         proxy_pass http://localhost:8000;
 | ||
|         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;
 | ||
|     }
 | ||
| 
 | ||
|     location /ws {
 | ||
|         proxy_pass http://localhost:8000;
 | ||
|         proxy_http_version 1.1;
 | ||
|         proxy_set_header Upgrade \$http_upgrade;
 | ||
|         proxy_set_header Connection "upgrade";
 | ||
|         proxy_set_header Host \$host;
 | ||
|     }
 | ||
| 
 | ||
|     location /api {
 | ||
|         proxy_pass http://localhost:8000;
 | ||
|         proxy_set_header Host \$host;
 | ||
|         proxy_set_header X-Real-IP \$remote_addr;
 | ||
|     }
 | ||
| }
 | ||
| EOF
 | ||
|         
 | ||
|         ln -sf /etc/nginx/sites-available/ai-review /etc/nginx/sites-enabled/
 | ||
|         nginx -t && systemctl reload nginx
 | ||
|         echo -e "${GREEN}✓ Nginx настроен${NC}"
 | ||
|         echo -e "${GREEN}✓ Доступ: http://$domain_name${NC}"
 | ||
|     fi
 | ||
| fi
 | ||
| 
 | ||
| # Итоговая информация
 | ||
| echo ""
 | ||
| echo -e "${GREEN}=========================================${NC}"
 | ||
| echo -e "${GREEN}  🎉 Установка завершена!${NC}"
 | ||
| echo -e "${GREEN}=========================================${NC}"
 | ||
| echo ""
 | ||
| echo -e "📍 ${BLUE}Директория установки:${NC} $INSTALL_DIR"
 | ||
| echo -e "📍 ${BLUE}Конфигурация:${NC} $INSTALL_DIR/backend/.env"
 | ||
| echo -e "📍 ${BLUE}Логи:${NC} /var/log/ai-review/"
 | ||
| echo ""
 | ||
| echo -e "${YELLOW}Полезные команды:${NC}"
 | ||
| echo ""
 | ||
| echo -e "  ${BLUE}Статус сервиса:${NC}"
 | ||
| echo "    sudo systemctl status ai-review"
 | ||
| echo ""
 | ||
| echo -e "  ${BLUE}Перезапуск:${NC}"
 | ||
| echo "    sudo systemctl restart ai-review"
 | ||
| echo ""
 | ||
| echo -e "  ${BLUE}Остановка:${NC}"
 | ||
| echo "    sudo systemctl stop ai-review"
 | ||
| echo ""
 | ||
| echo -e "  ${BLUE}Просмотр логов:${NC}"
 | ||
| echo "    sudo journalctl -u ai-review -f"
 | ||
| echo "    tail -f /var/log/ai-review/access.log"
 | ||
| echo "    tail -f /var/log/ai-review/error.log"
 | ||
| echo ""
 | ||
| echo -e "  ${BLUE}Редактирование конфигурации:${NC}"
 | ||
| echo "    sudo nano $INSTALL_DIR/backend/.env"
 | ||
| echo "    sudo systemctl restart ai-review"
 | ||
| echo ""
 | ||
| echo -e "${GREEN}🌐 Приложение доступно по адресу:${NC}"
 | ||
| echo -e "   ${BLUE}http://localhost:8000${NC}"
 | ||
| if [ -n "$domain_name" ] && [ "$domain_name" != "localhost" ]; then
 | ||
|     echo -e "   ${BLUE}http://$domain_name${NC}"
 | ||
| fi
 | ||
| echo ""
 | ||
| echo -e "${YELLOW}⚠️  Не забудьте:${NC}"
 | ||
| echo "   1. Отредактировать $INSTALL_DIR/backend/.env"
 | ||
| echo "   2. Добавить токены для Git платформ"
 | ||
| echo "   3. Настроить firewall (разрешить порт 80/443)"
 | ||
| echo ""
 | ||
| echo -e "${YELLOW}📦 Проверка установки:${NC}"
 | ||
| echo "   Backend venv: $([ -d $INSTALL_DIR/backend/venv ] && echo '✓' || echo '✗')"
 | ||
| echo "   Backend DB: $([ -f $INSTALL_DIR/backend/review.db ] && echo '✓' || echo '⚠️  будет создана')"
 | ||
| echo "   Frontend build: $([ -d $INSTALL_DIR/backend/public ] && echo '✓' || echo '✗')"
 | ||
| echo ""
 | ||
| echo -e "${GREEN}✨ Готово к работе!${NC}"
 | ||
| echo ""
 | ||
| 
 |