uploader-bot/start.sh

1866 lines
70 KiB
Bash
Executable File
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
# MY Network v3.0 - Автоматическая установка и запуск ноды
# Версия: 3.0.0
set -e
# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
# Переменные по умолчанию
SCRIPT_VERSION="3.0.0"
PROJECT_DIR="/opt/my-network"
STORAGE_DIR="/opt/my-network/storage"
CONFIG_DIR="/opt/my-network/config"
LOGS_DIR="/opt/my-network/logs"
# Параметры ноды
NODE_TYPE=""
NETWORK_MODE=""
ALLOW_INCOMING="false"
DOCKER_SOCK_PATH="/var/run/docker.sock"
BOOTSTRAP_CONFIG=""
TELEGRAM_API_KEY=""
CLIENT_TELEGRAM_API_KEY=""
NODE_VERSION="3.0.0"
ENABLE_SSL="false"
DOMAIN=""
EMAIL=""
ENABLE_WEB_CLIENT="true"
# Функция логирования
log() {
echo -e "${WHITE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Показать заставку
show_banner() {
clear
echo -e "${PURPLE}"
cat << "EOF"
╔══════════════════════════════════════════════════════════════╗
║ MY Network v3.0 ║
║ Децентрализованная сеть контента ║
║ ║
║ • Мгновенная трансляция без расшифровки ║
║ • Блокчейн интеграция для uploader-bot ║
║ • Полная децентрализация без консенсуса ║
║ • Автоматическая конвертация через Docker ║
╚══════════════════════════════════════════════════════════════╝
EOF
echo -e "${NC}"
echo ""
log_info "Запуск автоматической установки MY Network v3.0..."
echo ""
}
# Проверка прав root
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "Этот скрипт должен запускаться с правами root (sudo)"
echo "Использование: sudo bash start.sh"
exit 1
fi
}
# Определение операционной системы
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
OS_ID=$ID
else
log_error "Не удалось определить операционную систему"
exit 1
fi
log_info "Обнаружена ОС: $OS $VER"
# Проверка поддерживаемых ОС
case $OS_ID in
ubuntu|debian|centos|rhel|fedora)
log_success "Операционная система поддерживается"
;;
*)
log_warn "Операционная система может быть не полностью поддержана"
if check_interactive; then
echo -n "Продолжить установку? [y/N]: " >&2
read -r REPLY < /dev/tty
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
else
log_info "Неинтерактивный режим: продолжаем с неподдерживаемой ОС"
fi
;;
esac
}
# Проверка доступности TTY для интерактивного ввода
check_interactive() {
if [ -t 0 ] && [ -t 1 ]; then
return 0 # TTY доступен
else
return 1 # TTY недоступен
fi
}
# Безопасное чтение ввода
safe_read() {
local prompt="$1"
local default="$2"
local var_name="$3"
if check_interactive; then
# Интерактивный режим - используем /dev/tty
echo -n "$prompt" >&2
read -r input < /dev/tty
if [ -n "$input" ]; then
eval "$var_name='$input'"
else
eval "$var_name='$default'"
fi
else
# Неинтерактивный режим - используем значения по умолчанию
log_info "Неинтерактивный режим: $prompt -> используется значение по умолчанию: $default"
eval "$var_name='$default'"
fi
}
# Интерактивная настройка
interactive_setup() {
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${WHITE} НАСТРОЙКА MY NETWORK v3.0 ${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
# Проверяем режим
if ! check_interactive; then
log_warn "Обнаружен неинтерактивный режим (curl | bash)"
log_info "Используются настройки по умолчанию. Для интерактивной настройки скачайте и запустите скрипт локально:"
log_info "wget https://git.projscale.dev/my-dev/uploader-bot/raw/branch/main/start.sh && chmod +x start.sh && sudo ./start.sh"
echo ""
fi
# 1. Тип сети
echo -e "${PURPLE}❓ Выберите режим работы сети:${NC}"
echo " 1) Создать новую сеть (Bootstrap нода)"
echo " 2) Подключиться к существующей сети"
echo ""
if check_interactive; then
while true; do
echo -n "Введите номер [1-2]: " >&2
read -r network_choice < /dev/tty
case $network_choice in
1)
NETWORK_MODE="bootstrap"
NODE_TYPE="bootstrap"
log_info "Режим: Создание новой сети (Bootstrap нода)"
break
;;
2)
NETWORK_MODE="existing"
log_info "Режим: Подключение к существующей сети"
break
;;
*)
log_error "Неверный выбор. Введите 1 или 2."
;;
esac
done
else
# Неинтерактивный режим - создаем новую сеть по умолчанию
NETWORK_MODE="bootstrap"
NODE_TYPE="bootstrap"
log_info "Неинтерактивный режим: используется режим создания новой сети (Bootstrap нода)"
fi
# 2. Тип ноды (если подключаемся к существующей)
if [ "$NETWORK_MODE" = "existing" ]; then
echo ""
echo -e "${PURPLE}❓ Выберите тип ноды:${NC}"
echo " 1) Публичная нода (принимает входящие соединения)"
echo " 2) Приватная нода (только исходящие соединения)"
echo ""
if check_interactive; then
while true; do
echo -n "Введите номер [1-2]: " >&2
read -r node_choice < /dev/tty
case $node_choice in
1)
NODE_TYPE="public"
ALLOW_INCOMING="true"
log_info "Тип ноды: Публичная (открытые порты)"
break
;;
2)
NODE_TYPE="private"
ALLOW_INCOMING="false"
log_info "Тип ноды: Приватная (закрытые порты)"
break
;;
*)
log_error "Неверный выбор. Введите 1 или 2."
;;
esac
done
else
# Неинтерактивный режим - публичная нода по умолчанию
NODE_TYPE="public"
ALLOW_INCOMING="true"
log_info "Неинтерактивный режим: используется публичная нода по умолчанию"
fi
else
ALLOW_INCOMING="true" # Bootstrap нода всегда принимает подключения
fi
# 3. Bootstrap конфигурация
echo ""
echo -e "${PURPLE}❓ Конфигурация bootstrap узлов:${NC}"
if [ "$NETWORK_MODE" = "bootstrap" ]; then
log_info "Bootstrap нода будет создавать новую сеть"
BOOTSTRAP_CONFIG="new"
else
if check_interactive; then
echo -n "Путь до bootstrap.json [Enter для дефолтного]: " >&2
read -r custom_bootstrap < /dev/tty
else
custom_bootstrap=""
log_info "Неинтерактивный режим: используется дефолтный bootstrap.json"
fi
if [ -n "$custom_bootstrap" ] && [ -f "$custom_bootstrap" ]; then
BOOTSTRAP_CONFIG="$custom_bootstrap"
log_success "Использован кастомный bootstrap.json: $custom_bootstrap"
else
BOOTSTRAP_CONFIG="default"
log_info "Используется дефолтный bootstrap.json"
fi
fi
# 4. Docker socket
echo ""
echo -e "${PURPLE}❓ Настройка Docker для конвертации:${NC}"
if check_interactive; then
echo -n "Путь до docker.sock [$DOCKER_SOCK_PATH]: " >&2
read -r custom_docker_sock < /dev/tty
else
custom_docker_sock=""
log_info "Неинтерактивный режим: используется дефолтный путь $DOCKER_SOCK_PATH"
fi
if [ -n "$custom_docker_sock" ]; then
DOCKER_SOCK_PATH="$custom_docker_sock"
fi
if [ -S "$DOCKER_SOCK_PATH" ]; then
log_success "Docker socket найден: $DOCKER_SOCK_PATH"
else
log_warn "Docker socket не найден: $DOCKER_SOCK_PATH"
log_info "Docker будет установлен автоматически"
fi
# 5. Настройка веб-клиента
echo ""
echo -e "${PURPLE}❓ Настройка веб-интерфейса:${NC}"
if check_interactive; then
echo -n "Развернуть веб-клиент для управления нодой? [Y/n]: " >&2
read -r web_choice < /dev/tty
else
web_choice="Y"
log_info "Неинтерактивный режим: веб-клиент включен по умолчанию"
fi
if [[ ! $web_choice =~ ^[Nn]$ ]]; then
ENABLE_WEB_CLIENT="true"
log_success "Веб-клиент будет развернут"
else
ENABLE_WEB_CLIENT="false"
log_info "Веб-клиент отключен"
fi
# 6. SSL и домен (только для публичных нод с веб-клиентом)
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
else
ssl_choice="N"
log_info "Неинтерактивный режим: SSL отключен (требует ручной настройки)"
fi
if [[ $ssl_choice =~ ^[Yy]$ ]]; then
echo -n "Доменное имя: " >&2
read -r DOMAIN < /dev/tty
if [ -n "$DOMAIN" ]; 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
else
log_error "Домен не указан. SSL отключен"
fi
fi
else
log_info "SSL недоступен для данной конфигурации"
fi
# 7. Telegram API ключи
echo ""
echo -e "${PURPLE}❓ Настройка Telegram ботов (необязательно):${NC}"
if check_interactive; then
echo -n "TELEGRAM_API_KEY (основной бот) [Enter для пропуска]: " >&2
read -r TELEGRAM_API_KEY < /dev/tty
else
TELEGRAM_API_KEY=""
log_info "Неинтерактивный режим: Telegram боты отключены"
fi
if [ -n "$TELEGRAM_API_KEY" ]; then
log_success "TELEGRAM_API_KEY настроен"
if check_interactive; then
echo -n "CLIENT_TELEGRAM_API_KEY (клиентский бот) [Enter для пропуска]: " >&2
read -r CLIENT_TELEGRAM_API_KEY < /dev/tty
else
CLIENT_TELEGRAM_API_KEY=""
fi
if [ -n "$CLIENT_TELEGRAM_API_KEY" ]; then
log_success "CLIENT_TELEGRAM_API_KEY настроен"
else
log_info "Клиентский Telegram бот будет отключен"
fi
else
log_info "Telegram боты будут отключены"
fi
# 6. Подтверждение настроек
echo ""
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${WHITE} ПОДТВЕРЖДЕНИЕ НАСТРОЕК ${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${WHITE}Режим сети:${NC} $NETWORK_MODE"
echo -e "${WHITE}Тип ноды:${NC} $NODE_TYPE"
echo -e "${WHITE}Входящие соединения:${NC} $ALLOW_INCOMING"
echo -e "${WHITE}Docker socket:${NC} $DOCKER_SOCK_PATH"
echo -e "${WHITE}Bootstrap config:${NC} $BOOTSTRAP_CONFIG"
echo -e "${WHITE}Веб-клиент:${NC} $([ "$ENABLE_WEB_CLIENT" = "true" ] && echo "включен" || echo "отключен")"
echo -e "${WHITE}SSL сертификат:${NC} $([ "$ENABLE_SSL" = "true" ] && echo "включен для $DOMAIN" || echo "отключен")"
echo -e "${WHITE}Telegram основной:${NC} $([ -n "$TELEGRAM_API_KEY" ] && echo "настроен" || echo "отключен")"
echo -e "${WHITE}Telegram клиентский:${NC} $([ -n "$CLIENT_TELEGRAM_API_KEY" ] && echo "настроен" || echo "отключен")"
echo ""
if check_interactive; then
echo -n "Продолжить установку с этими настройками? [Y/n]: " >&2
read -r REPLY < /dev/tty
if [[ $REPLY =~ ^[Nn]$ ]]; then
log_info "Установка отменена пользователем"
exit 0
fi
else
log_info "Неинтерактивный режим: продолжаем установку с настройками по умолчанию"
sleep 3
fi
}
# Установка зависимостей
install_dependencies() {
log_info "📦 Установка системных зависимостей..."
# Настройка неинтерактивного режима для apt
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
# Обновление пакетов
case $OS_ID in
ubuntu|debian)
# Настройка для автоматического согласия с дефолтными настройками
echo 'DPkg::Options {"--force-confdef";"--force-confold";}' > /etc/apt/apt.conf.d/50unattended-upgrades-local
apt-get update -qq
apt-get upgrade -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
apt-get install -y -qq curl wget git unzip htop nano ufw fail2ban \
python3 python3-pip python3-venv build-essential \
postgresql-client jq netcat-openbsd
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf update -y -q
dnf install -y -q curl wget git unzip htop nano firewalld \
python3 python3-pip python3-devel gcc gcc-c++ \
postgresql jq nc
else
yum update -y -q
yum install -y -q curl wget git unzip htop nano firewalld \
python3 python3-pip python3-devel gcc gcc-c++ \
postgresql jq nc
fi
;;
esac
# Очистка переменных окружения
unset DEBIAN_FRONTEND
unset NEEDRESTART_MODE
unset NEEDRESTART_SUSPEND
log_success "Системные зависимости установлены"
}
# Установка Docker и Docker Compose
install_docker() {
if command -v docker &> /dev/null; then
log_info "Docker уже установлен: $(docker --version)"
else
log_info "🐳 Установка Docker..."
# Установка Docker через официальный скрипт
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
rm get-docker.sh
# Запуск и автозагрузка Docker
systemctl start docker
systemctl enable docker
log_success "Docker установлен: $(docker --version)"
fi
# Проверка Docker Compose
if command -v docker-compose &> /dev/null; then
log_info "Docker Compose уже установлен: $(docker-compose --version)"
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)"
fi
# Проверка доступности Docker socket
if [ ! -S "$DOCKER_SOCK_PATH" ]; then
log_error "Docker socket недоступен: $DOCKER_SOCK_PATH"
log_info "Перезапуск Docker..."
systemctl restart docker
sleep 5
if [ ! -S "$DOCKER_SOCK_PATH" ]; then
log_error "Не удалось настроить Docker socket"
exit 1
fi
fi
log_success "Docker socket доступен: $DOCKER_SOCK_PATH"
}
# Создание директорий проекта
create_directories() {
log_info "📁 Создание структуры директорий..."
# Основные директории
mkdir -p "$PROJECT_DIR"
mkdir -p "$STORAGE_DIR"
mkdir -p "$CONFIG_DIR"
mkdir -p "$LOGS_DIR"
# Поддиректории для хранения
mkdir -p "$STORAGE_DIR"
# Права доступа
chmod 755 "$PROJECT_DIR"
chmod 755 "$STORAGE_DIR"
chmod 700 "$CONFIG_DIR"
chmod 755 "$LOGS_DIR"
log_success "Структура директорий создана"
log_info "Проект: $PROJECT_DIR"
log_info "Хранилище: $STORAGE_DIR"
log_info "Конфигурация: $CONFIG_DIR"
log_info "Логи: $LOGS_DIR"
}
# Клонирование репозиториев
clone_repositories() {
log_info "📥 Клонирование репозиториев MY Network v3.0..."
cd "$PROJECT_DIR"
# Если проект уже существует, удаляем старую версию
if [ -d "my-network" ]; then
log_info "Удаление существующего проекта..."
rm -rf my-network
fi
# Создаем структуру проекта
mkdir -p my-network
cd my-network
# Клонирование всех репозиториев
log_info "Клонирование uploader-bot..."
if git clone https://git.projscale.dev/my-dev/uploader-bot.git .; then
log_success "uploader-bot клонирован"
else
log_error "Ошибка клонирования uploader-bot"
exit 1
fi
log_info "Клонирование web2-client..."
if git clone https://git.projscale.dev/my-dev/web2-client.git web2-client; then
log_success "web2-client клонирован"
else
log_error "Ошибка клонирования web2-client"
exit 1
fi
log_info "Клонирование converter-module..."
if git clone https://git.projscale.dev/my-dev/converter-module.git converter-module; then
log_success "converter-module клонирован"
else
log_error "Ошибка клонирования converter-module"
exit 1
fi
log_success "Все репозитории клонированы в $PROJECT_DIR/my-network"
}
# Создание файлов проекта
create_project_files() {
log_info "📝 Создание файлов проекта..."
cd "$PROJECT_DIR/my-network"
# Создание docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
app:
build: .
container_name: my-network-app
restart: unless-stopped
ports:
- "15100:15100"
volumes:
- ${STORAGE_PATH:-./storage}:/app/storage
- ${DOCKER_SOCK_PATH:-/var/run/docker.sock}:/var/run/docker.sock
- ./logs:/app/logs
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- NODE_ID=${NODE_ID}
- NODE_TYPE=${NODE_TYPE}
- NODE_VERSION=${NODE_VERSION}
- NETWORK_MODE=${NETWORK_MODE}
- ALLOW_INCOMING_CONNECTIONS=${ALLOW_INCOMING_CONNECTIONS}
- SECRET_KEY=${SECRET_KEY}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
- STORAGE_PATH=/app/storage
- API_HOST=${API_HOST}
- API_PORT=${API_PORT}
- DOCKER_SOCK_PATH=/var/run/docker.sock
- TELEGRAM_API_KEY=${TELEGRAM_API_KEY}
- CLIENT_TELEGRAM_API_KEY=${CLIENT_TELEGRAM_API_KEY}
- LOG_LEVEL=${LOG_LEVEL}
- LOG_PATH=/app/logs
- BOOTSTRAP_CONFIG=${BOOTSTRAP_CONFIG}
- MAX_PEER_CONNECTIONS=${MAX_PEER_CONNECTIONS}
- SYNC_INTERVAL=${SYNC_INTERVAL}
- CONVERT_MAX_PARALLEL=${CONVERT_MAX_PARALLEL}
- CONVERT_TIMEOUT=${CONVERT_TIMEOUT}
depends_on:
- postgres
- redis
networks:
- my-network
postgres:
image: postgres:15-alpine
container_name: my-network-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
networks:
- my-network
redis:
image: redis:7-alpine
container_name: my-network-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- my-network
volumes:
postgres_data:
redis_data:
networks:
my-network:
driver: bridge
EOF
# Создание Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
WORKDIR /app
# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
gcc \
g++ \
curl \
&& rm -rf /var/lib/apt/lists/*
# Копирование requirements и установка Python зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копирование кода приложения
COPY app/ ./app/
COPY alembic/ ./alembic/
COPY alembic.ini .
COPY bootstrap.json .
# Создание директорий
RUN mkdir -p /app/storage /app/logs
# Права доступа
RUN chmod +x /app/app/main.py
EXPOSE 15100
CMD ["python", "-m", "app.main"]
EOF
# Создание requirements.txt
cat > requirements.txt << 'EOF'
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.4.2
pydantic-settings==2.0.3
sqlalchemy==2.0.23
alembic==1.12.1
asyncpg==0.29.0
redis==5.0.1
aioredis==2.0.1
aiofiles==23.2.1
cryptography==41.0.7
python-jose[cryptography]==3.3.0
python-multipart==0.0.6
httpx==0.25.2
websockets==12.0
docker==6.1.3
base58==2.1.1
passlib[bcrypt]==1.7.4
python-telegram-bot==20.7
APScheduler==3.10.4
psutil==5.9.6
requests==2.31.0
PyYAML==6.0.1
python-dotenv==1.0.0
Pillow==10.1.0
ffmpeg-python==0.2.0
python-magic==0.4.27
jinja2==3.1.2
starlette==0.27.0
structlog==23.2.0
aiogram==3.3.0
EOF
# Создание init_db.sql
cat > init_db.sql << 'EOF'
-- MY Network v3.0 Database Initialization
-- Extension for UUID generation
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create enum types
DO $$ BEGIN
CREATE TYPE content_status AS ENUM ('pending', 'processing', 'completed', 'failed');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Create stored_content table (compatible with DEPRECATED-uploader-bot)
CREATE TABLE IF NOT EXISTS stored_content (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
hash VARCHAR(255) UNIQUE NOT NULL,
original_filename VARCHAR(255) NOT NULL,
file_type VARCHAR(100) NOT NULL,
file_size BIGINT NOT NULL,
content_type VARCHAR(255),
storage_path TEXT NOT NULL,
decrypted_path TEXT,
encrypted_path TEXT NOT NULL,
thumbnail_path TEXT,
converted_formats JSONB DEFAULT '{}',
metadata JSONB DEFAULT '{}',
encryption_key TEXT NOT NULL,
upload_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
last_accessed TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
access_count INTEGER DEFAULT 0,
status content_status DEFAULT 'pending',
uploader_id VARCHAR(255),
tags TEXT[],
description TEXT,
is_public BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_stored_content_hash ON stored_content(hash);
CREATE INDEX IF NOT EXISTS idx_stored_content_status ON stored_content(status);
CREATE INDEX IF NOT EXISTS idx_stored_content_upload_date ON stored_content(upload_date);
CREATE INDEX IF NOT EXISTS idx_stored_content_uploader_id ON stored_content(uploader_id);
CREATE INDEX IF NOT EXISTS idx_stored_content_file_type ON stored_content(file_type);
-- Create nodes table for network management
CREATE TABLE IF NOT EXISTS nodes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
node_id VARCHAR(255) UNIQUE NOT NULL,
address INET NOT NULL,
port INTEGER NOT NULL,
public_key TEXT,
node_type VARCHAR(50) NOT NULL,
version VARCHAR(20) NOT NULL,
last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
trust_score DECIMAL(3,2) DEFAULT 1.0,
is_active BOOLEAN DEFAULT true,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for nodes
CREATE INDEX IF NOT EXISTS idx_nodes_node_id ON nodes(node_id);
CREATE INDEX IF NOT EXISTS idx_nodes_address ON nodes(address);
CREATE INDEX IF NOT EXISTS idx_nodes_is_active ON nodes(is_active);
CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON nodes(last_seen);
-- Create content_sync table for decentralized synchronization
CREATE TABLE IF NOT EXISTS content_sync (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
content_hash VARCHAR(255) NOT NULL,
node_id VARCHAR(255) NOT NULL,
sync_status VARCHAR(50) DEFAULT 'pending',
attempts INTEGER DEFAULT 0,
last_attempt TIMESTAMP WITH TIME ZONE,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for content_sync
CREATE INDEX IF NOT EXISTS idx_content_sync_hash ON content_sync(content_hash);
CREATE INDEX IF NOT EXISTS idx_content_sync_node_id ON content_sync(node_id);
CREATE INDEX IF NOT EXISTS idx_content_sync_status ON content_sync(sync_status);
-- Create conversion_jobs table
CREATE TABLE IF NOT EXISTS conversion_jobs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
content_id UUID REFERENCES stored_content(id),
target_format VARCHAR(50) NOT NULL,
status content_status DEFAULT 'pending',
priority INTEGER DEFAULT 5,
attempts INTEGER DEFAULT 0,
max_attempts INTEGER DEFAULT 3,
error_message TEXT,
conversion_params JSONB DEFAULT '{}',
output_path TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
started_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE
);
-- Create indexes for conversion_jobs
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_content_id ON conversion_jobs(content_id);
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_status ON conversion_jobs(status);
CREATE INDEX IF NOT EXISTS idx_conversion_jobs_priority ON conversion_jobs(priority);
-- Update trigger for updated_at columns
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Apply update triggers
DROP TRIGGER IF EXISTS update_stored_content_updated_at ON stored_content;
CREATE TRIGGER update_stored_content_updated_at
BEFORE UPDATE ON stored_content
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_nodes_updated_at ON nodes;
CREATE TRIGGER update_nodes_updated_at
BEFORE UPDATE ON nodes
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_content_sync_updated_at ON content_sync;
CREATE TRIGGER update_content_sync_updated_at
BEFORE UPDATE ON content_sync
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_conversion_jobs_updated_at ON conversion_jobs;
CREATE TRIGGER update_conversion_jobs_updated_at
BEFORE UPDATE ON conversion_jobs
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
EOF
# Создание alembic.ini
cat > alembic.ini << 'EOF'
[alembic]
script_location = alembic
prepend_sys_path = .
version_path_separator = os
sqlalchemy.url =
[post_write_hooks]
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
EOF
log_success "Файлы проекта созданы"
}
# Загрузка и настройка проекта
setup_project() {
clone_repositories
create_project_files
cd "$PROJECT_DIR/my-network"
log_success "Проект настроен в $PROJECT_DIR/my-network"
}
# Функция для проверки доступности Docker registry
check_docker_registry() {
log_info "Проверка доступности Docker registry..."
# Проверяем доступность registry.docker.io
if timeout 30 curl -s --connect-timeout 10 https://registry.docker.io/v2/ >/dev/null 2>&1; then
log_success "Docker registry доступен"
return 0
else
log_warn "Docker registry недоступен или медленно отвечает"
return 1
fi
}
# Функция для настройки Docker daemon timeout
configure_docker_timeout() {
log_info "Настройка Docker timeout для сетевых операций..."
# Создаем или обновляем Docker daemon config
local docker_config="/etc/docker/daemon.json"
local temp_config="/tmp/daemon.json.tmp"
if [ -f "$docker_config" ]; then
# Читаем существующий конфиг
cp "$docker_config" "$temp_config"
else
# Создаем новый конфиг
echo '{}' > "$temp_config"
fi
# Добавляем настройки timeout с помощью jq если доступен
if command -v jq >/dev/null 2>&1; then
jq '. + {
"registry-mirrors": [],
"insecure-registries": [],
"max-concurrent-downloads": 3,
"max-concurrent-uploads": 3,
"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
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
fi
fi
fi
rm -f "$temp_config" 2>/dev/null
}
# Сборка converter образа
build_converter_image() {
log_info "🔧 Сборка converter образа из converter-module..."
cd "$PROJECT_DIR/my-network"
# Проверяем наличие клонированного converter-module
if [ ! -d "converter-module" ]; then
log_error "converter-module не найден. Проверьте клонирование репозиториев."
return 1
fi
cd converter-module
# Проверяем наличие Dockerfile в папке converter
if [ ! -f "converter/Dockerfile" ]; then
log_error "Dockerfile не найден в converter-module/converter/"
return 1
fi
# Переходим в папку converter для сборки
cd converter
# Настраиваем Docker timeout
configure_docker_timeout
# Проверяем доступность registry
if ! check_docker_registry; then
log_warn "Docker registry недоступен, пробуем продолжить с увеличенным timeout"
fi
# Сборка converter образа из оригинального репозитория с retry логикой
log_info "Сборка Docker образа для converter..."
# Попытки сборки с retry
local max_attempts=3
local attempt=1
local success=false
while [ $attempt -le $max_attempts ] && [ "$success" = false ]; do
log_info "Попытка сборки $attempt из $max_attempts..."
# Сборка с увеличенными таймаутами и дополнительными параметрами
if docker build \
--network=host \
--build-arg BUILDKIT_PROGRESS=plain \
--build-arg HTTP_TIMEOUT=300 \
--build-arg HTTPS_TIMEOUT=300 \
-t my-network-converter:latest . ; then
log_success "Converter образ собран: my-network-converter:latest"
success=true
else
log_warn "Попытка $attempt неудачна"
if [ $attempt -lt $max_attempts ]; then
log_info "Ожидание 15 секунд перед следующей попыткой..."
sleep 15
# Очистка Docker build cache и системы при неудачной попытке
log_info "Очистка Docker cache..."
docker builder prune -f >/dev/null 2>&1 || true
docker system prune -f >/dev/null 2>&1 || true
# Попытка сброса сетевых настроек Docker
if [ $attempt -eq 2 ]; then
log_info "Перезапуск Docker daemon для сброса сетевых настроек..."
systemctl restart docker >/dev/null 2>&1 || true
sleep 10
fi
fi
attempt=$((attempt + 1))
fi
done
if [ "$success" = false ]; then
log_error "Не удалось собрать converter образ после $max_attempts попыток"
log_warn "Возможные причины:"
log_warn "1. Проблемы с подключением к Docker Hub"
log_warn "2. Сетевые проблемы на сервере"
log_warn "3. Временные проблемы Docker Registry"
log_info "Установка продолжится без converter образа"
log_info "Converter можно собрать позже командой:"
log_info "cd $PROJECT_DIR/my-network/converter-module/converter && docker build -t my-network-converter:latest ."
fi
cd "$PROJECT_DIR/my-network"
}
# Установка и настройка nginx
setup_nginx() {
if [ "$ENABLE_WEB_CLIENT" = "true" ] || [ "$ENABLE_SSL" = "true" ]; then
log_info "🌐 Установка и настройка nginx..."
# Установка nginx
case $OS_ID in
ubuntu|debian)
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
apt-get update -qq
apt-get install -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
nginx certbot python3-certbot-nginx
unset DEBIAN_FRONTEND
unset NEEDRESTART_MODE
unset NEEDRESTART_SUSPEND
;;
centos|rhel|fedora)
if command -v dnf &> /dev/null; then
dnf install -y -q nginx certbot python3-certbot-nginx
else
yum install -y -q nginx certbot python3-certbot-nginx
fi
;;
esac
# Развертывание web2-client
if [ "$ENABLE_WEB_CLIENT" = "true" ]; then
log_info "Развертывание web2-client из репозитория..."
# Проверяем наличие клонированного web2-client
if [ ! -d "$PROJECT_DIR/my-network/web2-client" ]; then
log_error "web2-client не найден. Проверьте клонирование репозиториев."
return 1
fi
# Создаем директорию для nginx
mkdir -p /var/www/my-network-web
# Копируем файлы web2-client
cd "$PROJECT_DIR/my-network/web2-client"
# Если есть сборка (build process), выполняем её
if [ -f "package.json" ]; then
log_info "Установка зависимостей web2-client..."
npm install || log_warn "Не удалось установить зависимости npm"
# Если есть build скрипт, выполняем сборку
if npm run build 2>/dev/null; then
log_success "Сборка web2-client завершена"
# Копируем собранные файлы
if [ -d "build" ]; then
cp -r build/* /var/www/my-network-web/
elif [ -d "dist" ]; then
cp -r dist/* /var/www/my-network-web/
else
cp -r . /var/www/my-network-web/
fi
else
log_warn "Сборка не требуется, копируем исходные файлы"
cp -r . /var/www/my-network-web/
fi
else
# Копируем файлы как есть
log_info "Копирование статических файлов web2-client..."
cp -r . /var/www/my-network-web/
fi
# Настройка прав доступа
chown -R www-data:www-data /var/www/my-network-web/
chmod -R 755 /var/www/my-network-web/
log_success "Web2-client развернут в /var/www/my-network-web"
fi
# Создание конфигурации nginx
cat > /etc/nginx/sites-available/my-network << EOF
# MY Network v3.0 nginx configuration
# Upstream для API
upstream my_network_api {
server 127.0.0.1:15100;
}
server {
listen 80;
server_name ${DOMAIN:-localhost};
# Максимальный размер для chunked uploads
client_max_body_size 10G;
client_body_timeout 300s;
client_header_timeout 300s;
# Proxy buffering для больших файлов
proxy_buffering off;
proxy_request_buffering off;
proxy_max_temp_file_size 0;
# Статический контент (веб-интерфейс)
location / {
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " root /var/www/my-network-web;" || echo " return 404;")
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " index index.html;" || echo "")
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " try_files \$uri \$uri/ =404;" || echo "")
}
# API proxy
location /api/ {
proxy_pass http://my_network_api;
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;
# Для chunked uploads
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
# Таймауты для больших файлов
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# Health check
location /health {
proxy_pass http://my_network_api;
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 /monitor {
proxy_pass http://my_network_api;
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;
}
}
EOF
# Активация сайта
if [ ! -L "/etc/nginx/sites-enabled/my-network" ]; then
ln -s /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
fi
# Отключение дефолтного сайта
if [ -L "/etc/nginx/sites-enabled/default" ]; then
rm /etc/nginx/sites-enabled/default
fi
# Тест конфигурации nginx
if nginx -t; then
log_success "Конфигурация nginx корректна"
else
log_error "Ошибка в конфигурации nginx"
return 1
fi
# Запуск nginx
systemctl enable nginx
systemctl restart nginx
log_success "Nginx настроен и запущен"
# Настройка SSL если нужно
if [ "$ENABLE_SSL" = "true" ] && [ -n "$DOMAIN" ] && [ -n "$EMAIL" ]; then
install_ssl_certificates
fi
else
log_info "Nginx пропущен (веб-клиент и SSL отключены)"
fi
}
# Установка SSL сертификатов
install_ssl_certificates() {
log_info "🔒 Установка SSL сертификата для $DOMAIN..."
# Проверка DNS записи
if ! host "$DOMAIN" > /dev/null 2>&1; then
log_warn "DNS запись для $DOMAIN не найдена"
log_info "Убедитесь что домен указывает на этот сервер"
if check_interactive; then
echo -n "Продолжить установку SSL? [y/N]: " >&2
read -r ssl_continue < /dev/tty
if [[ ! $ssl_continue =~ ^[Yy]$ ]]; then
log_info "Установка SSL пропущена"
return 0
fi
else
log_info "Неинтерактивный режим: пропускаем SSL (требует ручной настройки)"
return 0
fi
fi
# Проверка и остановка запущенных процессов certbot
log_info "Проверка запущенных процессов certbot..."
if pgrep -f certbot > /dev/null; then
log_warn "Обнаружен запущенный процесс certbot, завершаем..."
pkill -f certbot 2>/dev/null || true
sleep 5
fi
# Очистка временных файлов certbot
rm -rf /tmp/tmp*/log 2>/dev/null || true
# Проверка существующих сертификатов и их очистка при конфликте
log_info "Проверка существующих сертификатов..."
if [ -d "/etc/letsencrypt/live/$DOMAIN" ]; then
log_warn "Обнаружен существующий сертификат для $DOMAIN, удаляем для предотвращения конфликтов..."
certbot delete --cert-name "$DOMAIN" --non-interactive 2>/dev/null || true
# Перезапуск nginx после удаления сертификатов чтобы очистить SSL конфигурацию
log_info "Перезапуск nginx для обновления конфигурации..."
systemctl restart nginx
sleep 5
# Проверяем что nginx работает корректно без SSL
if ! nginx -t 2>/dev/null; then
log_warn "Nginx конфигурация некорректна, восстанавливаем базовую HTTP конфигурацию..."
# Пересоздаем чистую HTTP конфигурацию
cat > /etc/nginx/sites-available/my-network << EOF
server {
listen 80;
server_name $DOMAIN;
client_max_body_size 10G;
client_body_timeout 300s;
client_header_timeout 300s;
location / {
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " root /var/www/my-network-web;" || echo " return 404;")
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " index index.html;" || echo "")
$([ "$ENABLE_WEB_CLIENT" = "true" ] && echo " try_files \$uri \$uri/ =404;" || echo "")
}
location /api/ {
proxy_pass http://127.0.0.1:15100;
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;
}
}
EOF
systemctl restart nginx
fi
sleep 2
fi
# Получение сертификата через certbot с явным указанием типа ключа
log_info "Запуск certbot для получения SSL сертификата..."
if certbot --nginx -d "$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive --redirect --key-type rsa --cert-name "$DOMAIN"; then
log_success "SSL сертификат установлен для $DOMAIN"
# Настройка автообновления
if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
log_success "Автообновление SSL настроено"
fi
# Обновление firewall для HTTPS
case $OS_ID in
ubuntu|debian)
ufw allow 443/tcp 2>/dev/null || true
;;
centos|rhel|fedora)
firewall-cmd --permanent --add-service=https 2>/dev/null || true
firewall-cmd --reload 2>/dev/null || true
;;
esac
log_success "HTTPS порт открыт в firewall"
else
log_error "Ошибка установки SSL сертификата"
log_warn "Возможные причины:"
log_warn "1. DNS запись $DOMAIN не указывает на этот сервер"
log_warn "2. Порт 80 заблокирован или недоступен из интернета"
log_warn "3. Nginx не запущен или неправильно настроен"
log_warn "4. Достигнут лимит запросов Let's Encrypt"
log_warn "5. Другой процесс certbot уже запущен"
log_info "Система продолжит работу без SSL. SSL можно настроить позже вручную:"
log_info "certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect"
# НЕ завершаем скрипт, SSL не критичен для работы системы
fi
}
# Генерация конфигурации
generate_config() {
log_info "⚙️ Генерация конфигурации..."
# Генерация уникальных ключей
SECRET_KEY=$(openssl rand -hex 32)
JWT_SECRET_KEY=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -hex 16)
# Генерация NODE_ID
NODE_ID="node-$(date +%s)-$(shuf -i 1000-9999 -n 1)"
# Создание .env файла
cat > "$CONFIG_DIR/.env" << EOF
# MY Network v3.0 Configuration
# Generated: $(date)
# Node Configuration
NODE_ID=$NODE_ID
NODE_TYPE=$NODE_TYPE
NODE_VERSION=$NODE_VERSION
NETWORK_MODE=$NETWORK_MODE
ALLOW_INCOMING_CONNECTIONS=$ALLOW_INCOMING
# Database Configuration
DATABASE_URL=postgresql://myuser:$DB_PASSWORD@postgres:5432/mynetwork
POSTGRES_DB=mynetwork
POSTGRES_USER=myuser
POSTGRES_PASSWORD=$DB_PASSWORD
# Redis Configuration
REDIS_URL=redis://redis:6379/0
# Security
SECRET_KEY=$SECRET_KEY
JWT_SECRET_KEY=$JWT_SECRET_KEY
ENCRYPTION_KEY=$ENCRYPTION_KEY
# Storage
STORAGE_PATH=$STORAGE_DIR
# API Configuration
API_HOST=0.0.0.0
API_PORT=15100
# Docker Configuration
DOCKER_SOCK_PATH=$DOCKER_SOCK_PATH
# Telegram Bots
TELEGRAM_API_KEY=$TELEGRAM_API_KEY
CLIENT_TELEGRAM_API_KEY=$CLIENT_TELEGRAM_API_KEY
# Logging
LOG_LEVEL=INFO
LOG_PATH=$LOGS_DIR
# Network Configuration
BOOTSTRAP_CONFIG=$BOOTSTRAP_CONFIG
MAX_PEER_CONNECTIONS=50
SYNC_INTERVAL=300
# Converter Configuration
CONVERT_MAX_PARALLEL=3
CONVERT_TIMEOUT=300
EOF
# Создание bootstrap.json
if [ "$BOOTSTRAP_CONFIG" = "new" ]; then
cat > "$CONFIG_DIR/bootstrap.json" << EOF
{
"version": "$NODE_VERSION",
"network_id": "my-network-$(date +%s)",
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"bootstrap_nodes": [
{
"node_id": "$NODE_ID",
"address": "$(curl -s ifconfig.me || echo 'localhost')",
"port": 15100,
"public_key": "",
"trusted": true,
"node_type": "bootstrap"
}
],
"network_settings": {
"protocol_version": "3.0",
"max_peers": 50,
"sync_interval": 300,
"individual_decisions": true,
"no_consensus": true
}
}
EOF
log_success "Создан новый bootstrap.json для новой сети"
elif [ "$BOOTSTRAP_CONFIG" != "default" ]; then
cp "$BOOTSTRAP_CONFIG" "$CONFIG_DIR/bootstrap.json"
log_success "Скопирован кастомный bootstrap.json"
fi
# Копирование конфигурации в проект
cp "$CONFIG_DIR/.env" "$PROJECT_DIR/my-network/.env"
if [ -f "$CONFIG_DIR/bootstrap.json" ]; then
cp "$CONFIG_DIR/bootstrap.json" "$PROJECT_DIR/my-network/bootstrap.json"
fi
log_success "Конфигурация сгенерирована"
}
# Настройка firewall
setup_firewall() {
if [ "$ALLOW_INCOMING" = "true" ]; then
log_info "🔥 Настройка firewall для публичной ноды..."
case $OS_ID in
ubuntu|debian)
# UFW для Ubuntu/Debian
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 15100/tcp # API порт MY Network
ufw --force enable
;;
centos|rhel|fedora)
# Firewalld для CentOS/RHEL/Fedora
systemctl start firewalld
systemctl enable firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-port=15100/tcp
firewall-cmd --reload
;;
esac
log_success "Firewall настроен (порт 15100 открыт)"
else
log_info "Приватная нода - firewall настройка пропущена"
fi
}
# Сборка и запуск контейнеров
build_and_start() {
log_info "🐳 Сборка и запуск MY Network v3.0..."
cd "$PROJECT_DIR/my-network"
# Остановка существующих контейнеров
docker-compose down 2>/dev/null || true
# Сборка образов
log_info "Сборка Docker образов..."
docker-compose build --no-cache
# Запуск сервисов
log_info "Запуск сервисов..."
docker-compose up -d
# Ожидание готовности сервисов
log_info "Ожидание готовности сервисов..."
sleep 30
# Проверка статуса контейнеров
if docker-compose ps | grep -q "Up"; then
log_success "Контейнеры запущены"
else
log_error "Ошибка запуска контейнеров"
docker-compose logs
exit 1
fi
}
# Инициализация базы данных
init_database() {
log_info "🗄️ Инициализация базы данных..."
cd "$PROJECT_DIR/my-network"
# Ожидание готовности 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
break
fi
echo -n "."
sleep 2
done
echo ""
# Выполнение миграций
log_info "Выполнение миграций базы данных..."
docker-compose exec -T app alembic upgrade head
if [ $? -eq 0 ]; then
log_success "База данных инициализирована"
else
log_error "Ошибка инициализации базы данных"
return 1
fi
}
# Подключение к сети
connect_to_network() {
log_info "🌐 Подключение к MY Network..."
cd "$PROJECT_DIR/my-network"
# Ожидание готовности API
log_info "Ожидание готовности API..."
for i in {1..60}; do
if curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
break
fi
echo -n "."
sleep 2
done
echo ""
if ! curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
log_error "API недоступно"
return 1
fi
log_success "API готово: http://localhost:15100"
# Статистика ноды
log_info "Получение статистики ноды..."
node_stats=$(curl -s "http://localhost:15100/api/v3/node/status" 2>/dev/null || echo "{}")
echo "$node_stats" | jq '.' 2>/dev/null || echo "Статистика недоступна"
# Подключение к bootstrap нодам (если не bootstrap)
if [ "$NODE_TYPE" != "bootstrap" ]; then
log_info "Попытка подключения к bootstrap нодам..."
# Автообнаружение пиров
curl -X POST "http://localhost:15100/api/v3/node/connect" \
-H "Content-Type: application/json" \
-d '{"auto_discover": true}' > /dev/null 2>&1
sleep 10
# Проверка подключений
peers_response=$(curl -s "http://localhost:15100/api/v3/node/peers" 2>/dev/null || echo '{"count": 0}')
peer_count=$(echo "$peers_response" | jq -r '.count // 0' 2>/dev/null || echo "0")
if [ "$peer_count" -gt 0 ]; then
log_success "Подключено к $peer_count пир(ам)"
else
log_warn "Пока не удалось подключиться к другим нодам"
log_info "Нода будет продолжать попытки подключения в фоне"
fi
else
log_info "Bootstrap нода готова принимать подключения"
fi
# Статистика сети
network_stats=$(curl -s "http://localhost:15100/api/v3/network/stats" 2>/dev/null || echo '{}')
log_info "Статистика сети:"
echo "$network_stats" | jq '.' 2>/dev/null || echo "Статистика сети недоступна"
}
# Создание systemd сервиса
create_systemd_service() {
log_info "⚙️ Создание systemd сервиса..."
cat > /etc/systemd/system/my-network.service << EOF
[Unit]
Description=MY Network v3.0 Node
Requires=docker.service
After=docker.service
[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
TimeoutStartSec=300
User=root
[Install]
WantedBy=multi-user.target
EOF
# Активация сервиса
systemctl daemon-reload
systemctl enable my-network
log_success "Systemd сервис создан и активирован"
}
# Финальный отчет
final_report() {
clear
echo -e "${GREEN}"
cat << "EOF"
╔══════════════════════════════════════════════════════════════╗
УСТАНОВКА ЗАВЕРШЕНА! ║
║ MY Network v3.0 ║
╚══════════════════════════════════════════════════════════════╝
EOF
echo -e "${NC}"
echo ""
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${WHITE} СТАТУС СИСТЕМЫ ${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
# Проверка сервисов
cd "$PROJECT_DIR/my-network"
echo ""
echo -e "${WHITE}🐳 Docker контейнеры:${NC}"
docker-compose ps --format "table {{.Name}}\t{{.State}}\t{{.Ports}}"
echo ""
echo -e "${WHITE}⚙️ Systemd сервис:${NC}"
if systemctl is-active my-network >/dev/null 2>&1; then
echo -e " ${GREEN}✅ my-network: активен${NC}"
else
echo -e " ${RED}❌ my-network: неактивен${NC}"
fi
echo ""
echo -e "${WHITE}🌐 Сетевая конфигурация:${NC}"
echo -e " Тип ноды: ${YELLOW}$NODE_TYPE${NC}"
echo -e " Режим сети: ${YELLOW}$NETWORK_MODE${NC}"
echo -e " Входящие соединения: ${YELLOW}$ALLOW_INCOMING${NC}"
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 ":15100 "; then
echo -e " API порт 15100: ${GREEN}✅ открыт${NC}"
else
echo -e " API порт 15100: ${RED}❌ недоступен${NC}"
fi
else
echo -e " Режим: ${YELLOW}Приватная нода (только исходящие)${NC}"
fi
echo ""
echo -e "${WHITE}📡 API и интерфейсы:${NC}"
if curl -f "http://localhost:15100/health" > /dev/null 2>&1; then
echo -e " API: ${GREEN}✅ http://localhost:15100${NC}"
echo -e " Health: ${GREEN}✅ http://localhost:15100/health${NC}"
echo -e " Мониторинг: ${GREEN}✅ http://localhost:15100/api/my/monitor/${NC}"
echo -e " Статус ноды: ${GREEN}✅ http://localhost:15100/api/v3/node/status${NC}"
else
echo -e " API: ${RED}❌ недоступно${NC}"
fi
# Веб-клиент и SSL информация
if [ "$ENABLE_WEB_CLIENT" = "true" ]; then
echo ""
echo -e "${WHITE}🌐 Веб-интерфейс:${NC}"
if systemctl is-active nginx >/dev/null 2>&1; then
if [ "$ENABLE_SSL" = "true" ] && [ -n "$DOMAIN" ]; then
echo -e " Веб-интерфейс: ${GREEN}✅ https://$DOMAIN${NC}"
echo -e " SSL сертификат: ${GREEN}✅ активен для $DOMAIN${NC}"
echo -e " HTTP redirect: ${GREEN}✅ автоматический переход на HTTPS${NC}"
else
if [ "$ALLOW_INCOMING" = "true" ]; then
PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "localhost")
echo -e " Веб-интерфейс: ${GREEN}✅ http://$PUBLIC_IP${NC}"
else
echo -e " Веб-интерфейс: ${GREEN}✅ http://localhost${NC}"
fi
echo -e " SSL: ${YELLOW}⚠ не настроен${NC}"
fi
echo -e " Nginx: ${GREEN}✅ работает${NC}"
echo -e " Chunked upload: ${GREEN}✅ поддерживается (до 10GB)${NC}"
else
echo -e " Веб-интерфейс: ${RED}❌ Nginx не запущен${NC}"
fi
else
echo -e " Веб-интерфейс: ${YELLOW}⚠ отключен${NC}"
fi
echo ""
echo -e "${WHITE}🤖 Telegram боты:${NC}"
if [ -n "$TELEGRAM_API_KEY" ]; then
echo -e " Основной бот: ${GREEN}✅ настроен${NC}"
else
echo -e " Основной бот: ${YELLOW}⚠ отключен${NC}"
fi
if [ -n "$CLIENT_TELEGRAM_API_KEY" ]; then
echo -e " Клиентский бот: ${GREEN}✅ настроен${NC}"
else
echo -e " Клиентский бот: ${YELLOW}⚠ отключен${NC}"
fi
echo ""
echo -e "${WHITE}💾 Хранилище и конвертация:${NC}"
echo -e " Путь хранения: ${YELLOW}$STORAGE_DIR${NC}"
echo -e " Docker socket: ${YELLOW}$DOCKER_SOCK_PATH${NC}"
if [ -S "$DOCKER_SOCK_PATH" ]; then
echo -e " Converter: ${GREEN}✅ готов к работе${NC}"
# Проверка наличия converter образа
if docker images | grep -q "my-network-converter"; then
echo -e " Converter образ: ${GREEN}✅ my-network-converter:latest${NC}"
else
echo -e " Converter образ: ${YELLOW}⚠ не найден${NC}"
fi
else
echo -e " Converter: ${RED}❌ Docker socket недоступен${NC}"
fi
echo ""
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${WHITE} КОМАНДЫ УПРАВЛЕНИЯ ${NC}"
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${WHITE}🔧 Управление сервисом:${NC}"
echo -e " ${BLUE}systemctl start my-network${NC} # Запуск"
echo -e " ${BLUE}systemctl stop my-network${NC} # Остановка"
echo -e " ${BLUE}systemctl restart my-network${NC} # Перезапуск"
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}curl http://localhost:15100/api/v3/node/status | jq${NC}"
echo -e " ${BLUE}curl http://localhost:15100/api/v3/network/stats | jq${NC}"
echo ""
echo -e "${WHITE}📁 Важные файлы:${NC}"
echo -e " Конфигурация: ${YELLOW}$CONFIG_DIR/.env${NC}"
echo -e " Bootstrap: ${YELLOW}$CONFIG_DIR/bootstrap.json${NC}"
echo -e " Логи: ${YELLOW}$LOGS_DIR/${NC}"
echo -e " Проект: ${YELLOW}$PROJECT_DIR/my-network/${NC}"
echo ""
echo -e "${GREEN}🎉 MY Network v3.0 успешно установлен и запущен!${NC}"
echo ""
echo -e "${WHITE}Особенности v3.0:${NC}"
echo -e " ✅ Полная децентрализация без консенсуса"
echo -e " ✅ Мгновенная трансляция контента"
echo -e " ✅ Автоматическая конвертация через Docker"
echo -e " ✅ Блокчейн интеграция для uploader-bot"
echo -e " ✅ Поддержка приватных и публичных нод"
echo ""
# Сохранение отчета
cat > "$PROJECT_DIR/installation-report.txt" << EOF
MY Network v3.0 Installation Report
Generated: $(date)
Node Configuration:
- Node ID: $NODE_ID
- Node Type: $NODE_TYPE
- Network Mode: $NETWORK_MODE
- Version: $NODE_VERSION
- Allow Incoming: $ALLOW_INCOMING
Paths:
- Project: $PROJECT_DIR/my-network
- Storage: $STORAGE_DIR
- Config: $CONFIG_DIR
- Logs: $LOGS_DIR
- Docker Socket: $DOCKER_SOCK_PATH
API Endpoints:
- Health: http://localhost:15100/health
- Node Status: http://localhost:15100/api/v3/node/status
- Network Stats: http://localhost:15100/api/v3/network/stats
- Monitoring: http://localhost:15100/api/my/monitor/
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
Telegram Bots:
- Main Bot: $([ -n "$TELEGRAM_API_KEY" ] && echo "enabled" || echo "disabled")
- Client Bot: $([ -n "$CLIENT_TELEGRAM_API_KEY" ] && echo "enabled" || echo "disabled")
EOF
log_success "Отчет об установке сохранен: $PROJECT_DIR/installation-report.txt"
}
# Основная функция
main() {
show_banner
check_root
detect_os
interactive_setup
install_dependencies
install_docker
create_directories
setup_project
build_converter_image
setup_nginx
generate_config
setup_firewall
build_and_start
init_database
connect_to_network
create_systemd_service
final_report
}
# Обработка ошибок
error_handler() {
log_error "Ошибка на строке $1. Код выхода: $2"
log_error "Установка прервана"
# Показать логи для диагностики
if [ -d "$PROJECT_DIR/my-network" ]; then
log_info "Логи для диагностики:"
cd "$PROJECT_DIR/my-network"
docker-compose logs --tail=50
fi
exit $2
}
trap 'error_handler $LINENO $?' ERR
# Запуск установки
main "$@"