diff --git a/start.sh b/start.sh index 1eac17e..0f832cb 100755 --- a/start.sh +++ b/start.sh @@ -3,7 +3,7 @@ # MY Network v3.0 - Автоматическая установка и запуск ноды # Версия: 3.0.0 -set -e +set -eE # Цвета для вывода RED='\033[0;31m' @@ -31,10 +31,10 @@ BOOTSTRAP_CONFIG="" TELEGRAM_API_KEY="" CLIENT_TELEGRAM_API_KEY="" NODE_VERSION="3.0.0" -ENABLE_SSL="false" -DOMAIN="" -EMAIL="" -ENABLE_WEB_CLIENT="true" +ENABLE_SSL="${ENABLE_SSL:-false}" +DOMAIN="${DOMAIN:-}" +EMAIL="${EMAIL:-}" +ENABLE_WEB_CLIENT="${ENABLE_WEB_CLIENT:-true}" # Функция логирования log() { @@ -57,6 +57,20 @@ log_error() { echo -e "${RED}[ERROR]${NC} $1" } +# Утилиты и Docker Compose обнаружение +has_cmd() { command -v "$1" >/dev/null 2>&1; } + +COMPOSE_CMD="" +init_compose_cmd() { + if has_cmd docker && docker compose version >/dev/null 2>&1; then + COMPOSE_CMD="docker compose" + elif has_cmd docker-compose; then + COMPOSE_CMD="docker-compose" + else + COMPOSE_CMD="" + fi +} + # Показать заставку show_banner() { clear @@ -135,7 +149,7 @@ check_existing_installation() { fi # Проверяем systemd сервис - if systemctl list-unit-files | grep -q "my-network.service"; then + if has_cmd systemctl && systemctl list-unit-files | grep -q "my-network.service"; then log_info "Обнаружен systemd сервис: my-network" existing_installation=true @@ -146,20 +160,24 @@ check_existing_installation() { fi # Проверяем Docker контейнеры - if docker ps -a --format "table {{.Names}}" | grep -q "my-network"; then - log_info "Обнаружены Docker контейнеры MY Network" - existing_installation=true - - if docker ps --format "table {{.Names}}" | grep -q "my-network"; then - log_info "Найдены запущенные контейнеры MY Network" - services_running=true + if has_cmd docker; then + if docker ps -a --format "table {{.Names}}" | grep -q "my-network"; then + log_info "Обнаружены Docker контейнеры MY Network" + existing_installation=true + + if docker ps --format "table {{.Names}}" | grep -q "my-network"; then + log_info "Найдены запущенные контейнеры MY Network" + services_running=true + fi fi fi # Проверяем Docker образы - if docker images --format "table {{.Repository}}" | grep -q "my-network"; then - log_info "Обнаружены Docker образы MY Network" - existing_installation=true + if has_cmd docker; then + if docker images --format "table {{.Repository}}" | grep -q "my-network"; then + log_info "Обнаружены Docker образы MY Network" + existing_installation=true + fi fi if [ "$existing_installation" = true ]; then @@ -186,7 +204,7 @@ check_existing_installation() { echo "" echo -e "${CYAN}Для ручного управления используйте:${NC}" echo -e "${BLUE}systemctl stop my-network${NC} # Остановка сервиса" - echo -e "${BLUE}docker-compose -f $PROJECT_DIR/my-network/docker-compose.yml down${NC} # Остановка контейнеров" + echo -e "${BLUE}docker compose -f $PROJECT_DIR/my-network/docker-compose.yml down${NC} # Остановка контейнеров" echo -e "${BLUE}sudo rm -rf $PROJECT_DIR${NC} # Удаление проекта" echo "" exit 0 @@ -220,7 +238,10 @@ cleanup_existing_installation() { # Переходим в папку проекта если существует if [ -d "$PROJECT_DIR/my-network" ]; then cd "$PROJECT_DIR/my-network" - docker-compose down --remove-orphans --volumes 2>/dev/null || true + init_compose_cmd + if [ -n "$COMPOSE_CMD" ]; then + $COMPOSE_CMD down --remove-orphans --volumes 2>/dev/null || true + fi fi # Принудительная остановка всех контейнеров MY Network @@ -242,9 +263,8 @@ cleanup_existing_installation() { # 4. Очистка Docker системы log_info "Очистка Docker кэша и неиспользуемых ресурсов..." - docker system prune -f --volumes 2>/dev/null || true + docker system prune -f 2>/dev/null || true docker builder prune -f 2>/dev/null || true - docker volume prune -f 2>/dev/null || true # 5. Удаление systemd сервиса if [ -f "/etc/systemd/system/my-network.service" ]; then @@ -311,7 +331,7 @@ cleanup_existing_installation() { backup_dir="/tmp/my-network-db-backup-$(date +%s)" # Создаем резервную копию volumes перед удалением - if docker volume ls | grep -q "my-network.*postgres"; then + if has_cmd docker && docker volume ls | grep -q "my-network.*postgres"; then log_info "Создание резервной копии базы данных..." mkdir -p "$backup_dir" @@ -342,7 +362,9 @@ cleanup_existing_installation() { # 10. Очистка Docker volumes (только если удаляем БД) if [ "$remove_database" = true ]; then log_info "Удаление Docker volumes..." - docker volume ls | grep "my-network" | awk '{print $2}' | xargs -r docker volume rm 2>/dev/null || true + if has_cmd docker; then + docker volume ls | grep "my-network" | awk '{print $2}' | xargs -r docker volume rm 2>/dev/null || true + fi fi log_success "Очистка завершена" @@ -518,7 +540,7 @@ interactive_setup() { # 5. Настройка веб-клиента echo "" echo -e "${PURPLE}❓ Настройка веб-интерфейса:${NC}" - if check_interactive; then + if check_interactive && [ -z "${ENABLE_WEB_CLIENT}" ]; then echo -n "Развернуть веб-клиент для управления нодой? [Y/n]: " >&2 read -r web_choice < /dev/tty else @@ -538,28 +560,33 @@ interactive_setup() { if [ "$ALLOW_INCOMING" = "true" ] && [ "$ENABLE_WEB_CLIENT" = "true" ]; then echo "" echo -e "${PURPLE}❓ Настройка SSL сертификата:${NC}" - if check_interactive; then - echo -n "Настроить SSL сертификат? [y/N]: " >&2 - read -r ssl_choice < /dev/tty + if [ -n "$DOMAIN" ] && [ -n "$EMAIL" ]; then + ssl_choice="Y" + log_info "Обнаружены DOMAIN и EMAIL в окружении — включаем SSL автоматически" else - ssl_choice="N" - log_info "Неинтерактивный режим: SSL отключен (требует ручной настройки)" + if check_interactive; then + echo -n "Настроить SSL сертификат? [y/N]: " >&2 + read -r ssl_choice < /dev/tty + else + ssl_choice="N" + log_info "Неинтерактивный режим: SSL отключен (требует ручной настройки)" + fi fi if [[ $ssl_choice =~ ^[Yy]$ ]]; then - echo -n "Доменное имя: " >&2 - read -r DOMAIN < /dev/tty - if [ -n "$DOMAIN" ]; then + if [ -z "$DOMAIN" ]; then + echo -n "Доменное имя: " >&2 + read -r DOMAIN < /dev/tty + fi + if [ -n "$DOMAIN" ] && [ -z "$EMAIL" ]; then echo -n "Email для уведомлений SSL: " >&2 read -r EMAIL < /dev/tty - if [ -n "$EMAIL" ]; then - ENABLE_SSL="true" - log_success "SSL будет настроен для домена: $DOMAIN" - else - log_error "Email не указан. SSL отключен" - fi + fi + if [ -n "$DOMAIN" ] && [ -n "$EMAIL" ]; then + ENABLE_SSL="true" + log_success "SSL будет настроен для домена: $DOMAIN" else - log_error "Домен не указан. SSL отключен" + log_error "DOMAIN/EMAIL не указаны. SSL отключен" fi fi else @@ -688,18 +715,42 @@ install_docker() { log_success "Docker установлен: $(docker --version)" fi - # Проверка Docker Compose - if command -v docker-compose &> /dev/null; then - log_info "Docker Compose уже установлен: $(docker-compose --version)" + # Проверка Docker Compose (предпочитаем docker compose plugin) + init_compose_cmd + if [ -n "$COMPOSE_CMD" ]; then + log_info "Compose доступен: $($COMPOSE_CMD version 2>/dev/null | head -n1)" else log_info "🐳 Установка Docker Compose..." - - # Установка последней версии Docker Compose - COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name) - curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - - log_success "Docker Compose установлен: $(docker-compose --version)" + case $OS_ID in + ubuntu|debian) + # Попытаться установить plugin через apt + apt-get update -qq || true + apt-get install -y -qq docker-compose-plugin || true + ;; + centos|rhel|fedora) + if command -v dnf &>/dev/null; then + dnf install -y -q docker-compose-plugin || true + else + yum install -y -q docker-compose-plugin || true + fi + ;; + esac + init_compose_cmd + if [ -z "$COMPOSE_CMD" ]; then + # Фоллбэк: скачать статический бинарник docker-compose v2 + COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name 2>/dev/null || echo "v2.24.7") + curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + if /usr/local/bin/docker-compose version >/dev/null 2>&1; then + COMPOSE_CMD="/usr/local/bin/docker-compose" + fi + fi + if [ -n "$COMPOSE_CMD" ]; then + log_success "Docker Compose установлен: $($COMPOSE_CMD version 2>/dev/null | head -n1)" + else + log_error "Не удалось установить Docker Compose" + exit 1 + fi fi # Проверка доступности Docker socket @@ -874,22 +925,25 @@ networks: driver: bridge EOF - # Создание Dockerfile + # Создание Dockerfile (минимум системных зависимостей для wheels) cat > Dockerfile << 'EOF' FROM python:3.11-slim WORKDIR /app -# Установка системных зависимостей -RUN apt-get update && apt-get install -y \ - gcc \ - g++ \ - curl \ +# Установка системных зависимостей (только необходимые) +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + gcc \ + g++ \ + curl \ + ffmpeg \ + libmagic1 \ && rm -rf /var/lib/apt/lists/* -# Копирование requirements и установка Python зависимостей -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt + # Копирование requirements и установка Python зависимостей + COPY requirements.txt . + RUN python -m pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt # Копирование кода приложения COPY app/ ./app/ @@ -914,7 +968,7 @@ EXPOSE 8000 CMD ["uvicorn", "app.fastapi_main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] EOF - # Создание requirements.txt + # Создание requirements.txt (пинованные версии для Py3.11, wheels) cat > requirements.txt << 'EOF' fastapi==0.104.1 uvicorn[standard]==0.24.0 @@ -926,6 +980,9 @@ asyncpg==0.29.0 redis==5.0.1 aioredis==2.0.1 aiofiles==23.2.1 +aiohttp==3.12.15 +yarl==1.17.1 +multidict==6.0.5 cryptography==41.0.7 python-jose[cryptography]==3.3.0 python-multipart==0.0.6 @@ -946,11 +1003,19 @@ python-magic==0.4.27 jinja2==3.1.2 starlette==0.27.0 structlog==23.2.0 -aiogram==3.3.0 +aiogram==3.21.0 +magic-filter==1.0.12 sanic==23.12.1 PyJWT==2.8.0 -cryptography==41.0.7 ed25519==1.5 +tonsdk==1.0.15 +pytonconnect==0.3.2 +prometheus-client==0.22.1 +pydub==0.25.1 +pycryptodome==3.23.0 +psycopg2-binary==2.9.10 +PyNaCl==1.5.0 +uvloop==0.21.0 EOF # Создание init_db.sql @@ -1153,6 +1218,26 @@ setup_project() { log_success "Проект настроен в $PROJECT_DIR/my-network" } +# Патч приложения: гарантировать регистрацию v3‑роутов +patch_app_routes() { + log_info "Проверка регистрации роутера /api/v3..." + local mfile="$PROJECT_DIR/my-network/app/fastapi_main.py" + if [ ! -f "$mfile" ]; then + log_warn "fastapi_main.py не найден; пропускаем патч роутов" + return 0 + fi + if ! grep -q "fastapi_v3_routes" "$mfile"; then + log_info "Добавляем импорт fastapi_v3_routes и include_router()" + # Вставляем импорт рядом с другими импортами роутеров + sed -i "/from app.api.fastapi_system_routes/a from app.api.fastapi_v3_routes import router as v3_router" "$mfile" + # Вставляем include_router сразу после регистрации node_stats_router + sed -i "/app.include_router(node_stats_router)/a \ app.include_router(v3_router) # /api/v3/*" "$mfile" + log_success "Роутер /api/v3 зарегистрирован" + else + log_info "Роутер /api/v3 уже зарегистрирован" + fi +} + # Функция для проверки доступности Docker registry check_docker_registry() { log_info "Проверка доступности Docker registry..." @@ -1193,11 +1278,11 @@ configure_docker_timeout() { "default-runtime": "runc" }' "$temp_config" > "${temp_config}.new" && mv "${temp_config}.new" "$temp_config" - if sudo cp "$temp_config" "$docker_config" 2>/dev/null; then + if cp "$temp_config" "$docker_config" 2>/dev/null; then log_info "Docker daemon конфигурация обновлена" # Перезапускаем Docker только если это безопасно if ! docker ps >/dev/null 2>&1 || [ "$(docker ps -q | wc -l)" -eq 0 ]; then - sudo systemctl reload docker 2>/dev/null || true + systemctl reload docker 2>/dev/null || true fi fi fi @@ -1342,10 +1427,14 @@ setup_nginx() { # Если есть сборка (build process), выполняем её if [ -f "package.json" ]; then log_info "Установка зависимостей web2-client..." - npm install || log_warn "Не удалось установить зависимости npm" + if has_cmd npm; then + npm install || log_warn "Не удалось установить зависимости npm" + else + log_warn "npm не установлен; пропускаем установку зависимостей" + fi # Если есть build скрипт, выполняем сборку - if npm run build 2>/dev/null; then + if has_cmd npm && npm run build 2>/dev/null; then log_success "Сборка web2-client завершена" # Копируем собранные файлы if [ -d "build" ]; then @@ -1791,26 +1880,32 @@ build_and_start() { cd "$PROJECT_DIR/my-network" # Остановка существующих контейнеров - docker-compose down 2>/dev/null || true + init_compose_cmd + $COMPOSE_CMD down 2>/dev/null || true # Сборка образов log_info "Сборка Docker образов..." - docker-compose build --no-cache + if ! $COMPOSE_CMD config >/dev/null 2>&1; then + log_error "docker-compose.yml содержит ошибки. Проверьте конфигурацию и .env" + $COMPOSE_CMD config || true + exit 1 + fi + $COMPOSE_CMD build --no-cache # Запуск сервисов log_info "Запуск сервисов..." - docker-compose up -d + $COMPOSE_CMD up -d # Ожидание готовности сервисов log_info "Ожидание готовности сервисов..." sleep 30 # Проверка статуса контейнеров - if docker-compose ps | grep -q "Up"; then + if $COMPOSE_CMD ps | grep -q "Up"; then log_success "Контейнеры запущены" else log_error "Ошибка запуска контейнеров" - docker-compose logs + $COMPOSE_CMD logs || true exit 1 fi } @@ -1824,7 +1919,7 @@ init_database() { # Ожидание готовности PostgreSQL log_info "Ожидание готовности PostgreSQL..." for i in {1..30}; do - if docker-compose exec -T postgres pg_isready -U myuser -d mynetwork > /dev/null 2>&1; then + if $COMPOSE_CMD exec -T postgres pg_isready -U myuser -d mynetwork > /dev/null 2>&1; then break fi echo -n "." @@ -1838,7 +1933,7 @@ init_database() { # Проверяем что база данных готова log_info "Проверка готовности базы данных..." - if docker-compose exec -T postgres psql -U myuser -d mynetwork -c "SELECT 1;" > /dev/null 2>&1; then + if $COMPOSE_CMD exec -T postgres psql -U myuser -d mynetwork -c "SELECT 1;" > /dev/null 2>&1; then log_success "База данных инициализирована" else log_error "Ошибка доступа к базе данных" @@ -1958,6 +2053,16 @@ connect_to_network() { create_systemd_service() { log_info "⚙️ Создание systemd сервиса..." + # Определяем команду compose для systemd + local compose_service_cmd + if has_cmd docker && docker compose version >/dev/null 2>&1; then + compose_service_cmd="docker compose" + elif has_cmd docker-compose; then + compose_service_cmd="/usr/local/bin/docker-compose" + else + compose_service_cmd="docker compose" # по умолчанию + fi + cat > /etc/systemd/system/my-network.service << EOF [Unit] Description=MY Network v3.0 Node @@ -1968,8 +2073,8 @@ After=docker.service Type=oneshot RemainAfterExit=yes WorkingDirectory=$PROJECT_DIR/my-network -ExecStart=/usr/local/bin/docker-compose up -d -ExecStop=/usr/local/bin/docker-compose down +ExecStart=/bin/sh -lc "$compose_service_cmd up -d" +ExecStop=/bin/sh -lc "$compose_service_cmd down" TimeoutStartSec=300 User=root @@ -2006,7 +2111,12 @@ EOF echo "" echo -e "${WHITE}🐳 Docker контейнеры:${NC}" - docker-compose ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}" + init_compose_cmd + if $COMPOSE_CMD ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}" >/dev/null 2>&1; then + $COMPOSE_CMD ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}" + else + $COMPOSE_CMD ps || true + fi echo "" echo -e "${WHITE}⚙️ Systemd сервис:${NC}" @@ -2025,7 +2135,7 @@ EOF if [ "$ALLOW_INCOMING" = "true" ]; then PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "неизвестен") echo -e " Публичный IP: ${YELLOW}$PUBLIC_IP${NC}" - if netstat -tlnp 2>/dev/null | grep -q ":8000 "; then + if (has_cmd ss && ss -ltnp 2>/dev/null | grep -q ":8000 ") || (has_cmd netstat && netstat -tlnp 2>/dev/null | grep -q ":8000 "); then echo -e " API порт 8000: ${GREEN}✅ открыт${NC}" else echo -e " API порт 8000: ${RED}❌ недоступен${NC}" @@ -2114,7 +2224,7 @@ EOF echo -e " ${BLUE}systemctl status my-network${NC} # Статус" echo "" echo -e "${WHITE}📊 Мониторинг:${NC}" - echo -e " ${BLUE}docker-compose -f $PROJECT_DIR/my-network/docker-compose.yml logs -f${NC}" + echo -e " ${BLUE}$COMPOSE_CMD -f $PROJECT_DIR/my-network/docker-compose.yml logs -f${NC}" echo -e " ${BLUE}curl http://localhost:8000/api/v3/node/status | jq${NC}" echo -e " ${BLUE}curl http://localhost:8000/api/v3/network/stats | jq${NC}" echo "" @@ -2173,7 +2283,7 @@ Management Commands: - Start: systemctl start my-network - Stop: systemctl stop my-network - Status: systemctl status my-network -- Logs: docker-compose -f $PROJECT_DIR/my-network/docker-compose.yml logs -f + - Logs: $COMPOSE_CMD -f $PROJECT_DIR/my-network/docker-compose.yml logs -f Telegram Bots: - Main Bot: $([ -n "$TELEGRAM_API_KEY" ] && echo "enabled" || echo "disabled") @@ -2194,6 +2304,7 @@ main() { install_docker create_directories setup_project + patch_app_routes build_converter_image setup_nginx generate_config @@ -2207,6 +2318,7 @@ main() { # Обработка ошибок error_handler() { + set +e log_error "Ошибка на строке $1. Код выхода: $2" log_error "Установка прервана" @@ -2214,7 +2326,10 @@ error_handler() { if [ -d "$PROJECT_DIR/my-network" ]; then log_info "Логи для диагностики:" cd "$PROJECT_DIR/my-network" - docker-compose logs --tail=50 + init_compose_cmd + if [ -n "$COMPOSE_CMD" ]; then + $COMPOSE_CMD logs --tail=50 || true + fi fi exit $2 @@ -2223,4 +2338,4 @@ error_handler() { trap 'error_handler $LINENO $?' ERR # Запуск установки -main "$@" \ No newline at end of file +main "$@"