docs / script

This commit is contained in:
user 2025-07-25 15:36:24 +03:00
parent 5a158222d7
commit 47db638ea6
9 changed files with 5122 additions and 176 deletions

266
README.md
View File

@ -1,170 +1,176 @@
# MY Network - Универсальная установка
# MY Network v3.0 - Единый установочный скрипт
🚀 **Универсальный установщик MY Network для любого сервера**
**Автоматическая установка и запуск децентрализованной сети контента одной командой**
## Быстрая установка
## 🚀 Быстрая установка
Один скрипт для полной установки на любом сервере:
### Развертывание на новом сервере одной командой:
```bash
# Скачать проект
git clone <repository_url>
cd uploader-bot # или my-uploader-bot, или название вашего проекта
# Запустить установку
chmod +x universal_installer.sh
sudo ./universal_installer.sh
curl -fsSL https://git.projscale.dev/my-dev/uploader-bot/raw/branch/main/start.sh | sudo bash
```
## 🌐 Настройка домена и HTTPS
После установки настройте домен и SSL сертификат:
### Или скачать и запустить локально:
```bash
# 1. Исправить nginx (если есть ошибки)
chmod +x fix_nginx_now.sh
sudo ./fix_nginx_now.sh
# 2. Настроить домен и SSL
chmod +x scripts/setup_domain_ssl.sh
sudo ./scripts/setup_domain_ssl.sh
wget https://git.projscale.dev/my-dev/uploader-bot/raw/branch/main/start.sh
chmod +x start.sh
sudo ./start.sh
```
**Требования для SSL:**
- Домен должен быть направлен на IP сервера (A-запись в DNS)
- Порты 80 и 443 должны быть открыты
- Сервер должен быть доступен из интернета
## 📋 Что устанавливается
## 📁 Структура проекта
Скрипт `start.sh` автоматически:
```
my-uploader-bot/
├── universal_installer.sh # 🚀 Основной установщик
├── fix_nginx_now.sh # 🔧 Быстрое исправление nginx
├── app/ # 💻 Код приложения
├── scripts/ # 🛠️ Утилиты и скрипты
│ ├── setup_domain_ssl.sh # 🌐 Настройка домена и SSL
│ ├── diagnose.sh # 🔍 Диагностика проблем
│ └── ...
├── deployment/ # 📦 Файлы развертывания
├── docs/ # 📚 Документация
└── ...
```
1. **Клонирует все репозитории:**
- `uploader-bot` - основное приложение
- `web2-client` - веб-интерфейс управления нодой
- `converter-module` - модуль конвертации медиа
- `contracts` - блокчейн контракты
### 📦 deployment/
- `docker-compose.production.yml` - Основной compose файл
- `Dockerfile` - Образ приложения
- `requirements.txt` - Python зависимости
- `env.example` - Пример переменных окружения
2. **Устанавливает зависимости:**
- Docker и Docker Compose
- Python 3.11+ и системные библиотеки
- Nginx (при включении веб-клиента)
- Certbot (при включении SSL)
### 🛠️ scripts/
- `setup_domain_ssl.sh` - Настройка домена и SSL
- `diagnose.sh` - Диагностика проблем
- `quick_check.sh` - Быстрая проверка статуса
- `setup_*.sh` - Скрипты настройки компонентов
3. **Настраивает инфраструктуру:**
- PostgreSQL база данных с миграциями
- Redis для кеширования
- Nginx с поддержкой chunked uploads до 10GB
- SSL сертификаты через Let's Encrypt (опционально)
### 📚 docs/
- Документация проекта
- `archive/` - Устаревшие файлы
4. **Создает файлы проекта:**
- `docker-compose.yml` с полной конфигурацией
- `Dockerfile` для сборки приложения
- `requirements.txt` со всеми зависимостями
- `init_db.sql` с настройкой базы данных
- `alembic.ini` для миграций
## 🔧 Что делает universal_installer.sh
## 🔧 Интерактивная настройка
1. **🔍 Автопоиск проекта** - находит папку проекта в любой директории
2. **📦 Установка зависимостей** - Docker, Docker Compose, Nginx, UFW
3. **🔒 Настройка безопасности** - файрвол, fail2ban
4. **🗑️ Очистка** - удаляет старые контейнеры
5. **⚙️ Конфигурация** - создает .env, настраивает nginx
6. **🚀 Запуск** - запускает все сервисы
7. **🔧 SystemD service** - автозапуск при перезагрузке
8. **✅ Тестирование** - проверяет работу API
При запуске скрипт предложит настроить:
### Сетевые настройки:
- **Режим сети:** Создать новую сеть (Bootstrap) или подключиться к существующей
- **Тип ноды:** Публичная (с входящими соединениями) или приватная
- **Bootstrap конфигурация:** Использовать дефолтную или кастомную
### Веб-интерфейс:
- **Веб-клиент:** Развертывание интерфейса управления нодой
- **SSL сертификат:** Автоматическое получение и настройка HTTPS
- **Домен и email:** Для SSL сертификата
### Дополнительные опции:
- **Docker socket:** Путь к docker.sock для конвертации
- **Telegram боты:** API ключи для основного и клиентского ботов
## 🌐 После установки
### HTTP доступ:
- `http://YOUR_SERVER_IP/api/health` - API health check
- `http://YOUR_SERVER_IP/health` - Альтернативный health check
### HTTPS доступ (после настройки домена):
- `https://your-domain.com/api/health` - API health check
- `https://your-domain.com/health` - Альтернативный health check
## 🛠️ Управление
### Доступ к ноде:
- **API:** `http://localhost:15100` или `https://your-domain.com`
- **Веб-интерфейс:** `http://localhost` или `https://your-domain.com`
- **Health check:** `/health`
- **Статус ноды:** `/api/v3/node/status`
### Управление сервисом:
```bash
# Запуск/остановка
systemctl start my-network
systemctl stop my-network
systemctl restart my-network
# Статус
sudo systemctl status mynetwork
systemctl status my-network
# Перезапуск
sudo systemctl restart mynetwork
# Логи приложения
docker logs $(docker ps --format "{{.Names}}" | grep app | head -1)
# Логи nginx
sudo journalctl -u nginx -f
# Логи
docker-compose -f /opt/my-network/my-network/docker-compose.yml logs -f
```
## 🔍 Диагностика
Если что-то не работает:
### Мониторинг:
```bash
# Быстрая диагностика
./scripts/diagnose.sh
# Статус ноды
curl http://localhost:15100/api/v3/node/status | jq
# Полная диагностика
./scripts/full_diagnosis.sh
# Статистика сети
curl http://localhost:15100/api/v3/network/stats | jq
# Исправление nginx
sudo ./fix_nginx_now.sh
# Список пиров
curl http://localhost:15100/api/v3/node/peers | jq
```
## 🔒 SSL и безопасность
## 🏗️ Архитектура v3.0
После настройки SSL:
- Автоматическое перенаправление HTTP → HTTPS
- Автообновление сертификатов Let's Encrypt
- Безопасные заголовки HTTP
- Современные SSL протоколы
### Ключевые особенности:
- ✅ **Полная децентрализация** - без консенсуса и центральных узлов
- ✅ **Мгновенная трансляция** - контент доступен без расшифровки
- ✅ **Автоматическая конвертация** - через Docker контейнеры
- ✅ **Блокчейн интеграция** - совместимость с uploader-bot
- ✅ **Chunked uploads** - поддержка файлов до 10GB
- ✅ **SSL автоматизация** - Let's Encrypt интеграция
```bash
# Проверка сертификата
sudo certbot certificates
### Компоненты системы:
- **API Server** - FastAPI приложение на порту 15100
- **База данных** - PostgreSQL с автомиграциями
- **Кеширование** - Redis для быстрого доступа
- **Веб-интерфейс** - Nginx + статические файлы
- **Конвертер** - Docker контейнер для медиа-обработки
# Тест обновления
sudo certbot renew --dry-run
## 🔐 Безопасность
- **Шифрование** - AES-256 для контента в сети
- **JWT токены** - для API аутентификации
- **SSL/TLS** - автоматические сертификаты
- **Firewall** - автоматическая настройка портов
- **Fail2ban** - защита от брутфорса
## 📁 Структура проекта
После установки создается:
```
## 📋 Поддерживаемые системы
- ✅ Ubuntu 20.04+
- ✅ Debian 11+
- ✅ CentOS 8+ (адаптация)
## 🚨 Решение проблем
### Nginx ошибки:
```bash
sudo ./fix_nginx_now.sh
/opt/my-network/
├── my-network/ # Основной проект
│ ├── uploader-bot/ # Основное приложение
│ ├── web2-client/ # Веб-интерфейс
│ ├── converter-module/ # Модуль конвертации
│ ├── contracts/ # Блокчейн контракты
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── requirements.txt
│ └── init_db.sql
├── storage/ # Хранилище контента
├── config/ # Конфигурация (.env, bootstrap.json)
└── logs/ # Логи системы
```
### Контейнеры перезапускаются:
```bash
docker logs $(docker ps --format "{{.Names}}" | grep app | head -1)
./scripts/diagnose.sh
```
### SSL не работает:
- Проверьте DNS записи
- Убедитесь что порты 80/443 открыты
- Проверьте файрвол: `sudo ufw status`
## 🆘 Поддержка
Скрипт автоматически:
- Находит проект в любой директории
- Адаптируется к разным именам контейнеров
- Работает с разными compose файлами
- Создает резервные копии конфигураций
- Показывает детальную диагностику при ошибках
- Настраивает SSL с автообновлением
После установки создается отчет: `/opt/my-network/installation-report.txt`
### Проблемы и решения:
**Ошибка клонирования репозиториев:**
```bash
# Проверьте доступность git.projscale.dev
ping git.projscale.dev
```
**Контейнеры не запускаются:**
```bash
# Проверьте логи
cd /opt/my-network/my-network
docker-compose logs
```
**SSL не работает:**
```bash
# Проверьте DNS записи
nslookup your-domain.com
# Проверьте nginx
nginx -t
systemctl status nginx
```
## 📝 Лицензия
MY Network v3.0 - Проект с открытым исходным кодом

View File

@ -238,7 +238,7 @@ cat > $INSTALL_DIR/bootstrap_main.json << EOF
}
EOF
# Docker Compose for Main Bootstrap
# Docker Compose for Main Bootstrap (только MY Network)
cat > $INSTALL_DIR/docker-compose.yml << EOF
version: '3.8'
@ -260,34 +260,6 @@ services:
- BOOTSTRAP_NODE=true
networks:
- mynetwork
depends_on:
- web2-client
- converter
# Web2 Client
web2-client:
build: ./web2-client
container_name: web2-client-main
restart: unless-stopped
ports:
- "$WEB_CLIENT_PORT:3000"
environment:
- NEXT_PUBLIC_API_URL=https://$DOMAIN
- NEXT_PUBLIC_WS_URL=wss://$DOMAIN
networks:
- mynetwork
# Converter Module
converter:
build: ./converter-module
container_name: converter-main
restart: unless-stopped
ports:
- "$CONVERTER_PORT:8080"
volumes:
- ./data/converter:/app/data
networks:
- mynetwork
networks:
mynetwork:
@ -312,8 +284,6 @@ ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw allow $MY_NETWORK_PORT/tcp # MY Network
ufw allow $WEB_CLIENT_PORT/tcp # Web Client
ufw allow $CONVERTER_PORT/tcp # Converter
ufw --force enable
@ -349,9 +319,9 @@ server {
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Main web interface (Web2 Client)
# Default location - MY Network API
location / {
proxy_pass http://localhost:$WEB_CLIENT_PORT;
proxy_pass http://localhost:$MY_NETWORK_PORT/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
@ -389,15 +359,6 @@ server {
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
}
# Converter API
location /convert/ {
proxy_pass http://localhost:$CONVERTER_PORT/;
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
@ -499,7 +460,6 @@ endpoints=(
"https://$DOMAIN/health"
"https://$DOMAIN/api/bootstrap"
"https://$DOMAIN/monitor/"
"https://$DOMAIN/"
)
for endpoint in "${endpoints[@]}"; do
@ -519,7 +479,6 @@ echo "🎉 MY NETWORK v2.0 MAIN BOOTSTRAP NODE DEPLOYMENT COMPLETE!"
echo "============================================================="
echo ""
echo "🌐 Main Access Points:"
echo " • Web Interface: https://$DOMAIN/"
echo " • Matrix Dashboard: https://$DOMAIN/monitor/"
echo " • Health Check: https://$DOMAIN/health"
echo " • Bootstrap API: https://$DOMAIN/api/bootstrap"
@ -528,8 +487,6 @@ echo " • API Docs: https://$DOMAIN/api/docs"
echo ""
echo "🔧 Service Ports:"
echo " • MY Network: $MY_NETWORK_PORT"
echo " • Web Client: $WEB_CLIENT_PORT"
echo " • Converter: $CONVERTER_PORT"
echo ""
echo "🛠️ Management Commands:"
echo " • View logs: docker-compose -f $INSTALL_DIR/docker-compose.yml logs -f"

771
docs/CONVERTER_MODULE.md Normal file
View File

@ -0,0 +1,771 @@
# MY Network v3.0 - Converter Module и Docker Integration
## 🔄 Обзор Converter Module
Converter Module представляет собой on-demand систему конвертации медиа файлов, работающую через Docker контейнеры. Модуль запускается только при необходимости обработки файлов и автоматически завершает работу после выполнения задачи.
## 🐳 Архитектура Docker Integration
### Принцип работы
```mermaid
graph TB
API[MY Network API] --> CM[Converter Manager]
CM --> DS[Docker Socket]
DS --> DI[Docker Image: my-converter]
subgraph "On-Demand Containers"
C1[Converter Container 1]
C2[Converter Container 2]
C3[Converter Container N]
end
DI -.->|Creates| C1
DI -.->|Creates| C2
DI -.->|Creates| C3
C1 --> FS[Shared File System]
C2 --> FS
C3 --> FS
C1 -.->|Auto-Remove| X1[❌]
C2 -.->|Auto-Remove| X2[❌]
C3 -.->|Auto-Remove| X3[❌]
```
### Docker Socket Integration
```python
class DockerConverterManager:
"""Управление converter контейнерами через Docker API"""
def __init__(self, docker_sock_path: str = "/var/run/docker.sock"):
self.docker_sock_path = docker_sock_path
self.docker_client = None
self.converter_image = "my-converter:latest"
self.max_parallel_jobs = 3
self.active_jobs = {}
async def initialize(self):
"""Инициализация Docker клиента"""
try:
# Проверка доступности Docker socket
if not os.path.exists(self.docker_sock_path):
raise DockerError(f"Docker socket not found: {self.docker_sock_path}")
# Создание Docker клиента
self.docker_client = docker.DockerClient(
base_url=f"unix://{self.docker_sock_path}"
)
# Проверка связи с Docker
await self._test_docker_connection()
# Проверка наличия converter образа
await self._ensure_converter_image()
logger.info("Docker converter manager initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Docker converter manager: {e}")
raise
async def _test_docker_connection(self):
"""Проверка подключения к Docker"""
try:
info = self.docker_client.info()
logger.info(f"Docker connected: {info['ServerVersion']}")
except Exception as e:
raise DockerError(f"Cannot connect to Docker: {e}")
async def _ensure_converter_image(self):
"""Проверка наличия converter образа"""
try:
image = self.docker_client.images.get(self.converter_image)
logger.info(f"Converter image found: {image.id[:12]}")
except docker.errors.ImageNotFound:
logger.error(f"Converter image not found: {self.converter_image}")
raise DockerError(f"Please build converter image: {self.converter_image}")
async def convert_file(self, input_file: str, output_file: str,
conversion_type: str, options: dict = None) -> str:
"""
Запуск конвертации файла в новом контейнере
Args:
input_file: Путь к входному файлу
output_file: Путь к выходному файлу
conversion_type: Тип конвертации (video_to_mp4, audio_to_mp3, etc.)
options: Дополнительные параметры конвертации
Returns:
str: ID задачи конвертации
"""
# Проверка лимита параллельных задач
if len(self.active_jobs) >= self.max_parallel_jobs:
raise ConverterError("Too many active conversion jobs")
# Генерация ID задачи
job_id = str(uuid4())
# Подготовка окружения для контейнера
container_config = await self._prepare_container_config(
job_id, input_file, output_file, conversion_type, options
)
try:
# Создание и запуск контейнера
container = self.docker_client.containers.run(
image=self.converter_image,
command=container_config["command"],
volumes=container_config["volumes"],
environment=container_config["environment"],
name=f"converter-{job_id}",
detach=True,
auto_remove=True, # Автоматическое удаление после завершения
cpu_count=1, # Ограничение ресурсов
mem_limit="1g"
)
# Регистрация активной задачи
self.active_jobs[job_id] = {
"container": container,
"start_time": datetime.utcnow(),
"input_file": input_file,
"output_file": output_file,
"conversion_type": conversion_type,
"status": "running"
}
logger.info(f"Conversion job {job_id} started in container {container.id[:12]}")
# Запуск мониторинга задачи в фоне
asyncio.create_task(self._monitor_conversion_job(job_id))
return job_id
except Exception as e:
logger.error(f"Failed to start conversion job {job_id}: {e}")
raise ConverterError(f"Conversion start failed: {e}")
async def _prepare_container_config(self, job_id: str, input_file: str,
output_file: str, conversion_type: str,
options: dict) -> dict:
"""Подготовка конфигурации контейнера"""
# Общая папка для файлов между хостом и контейнером
shared_volume = "/opt/my-network/shared"
container_input = f"/input/{os.path.basename(input_file)}"
container_output = f"/output/{os.path.basename(output_file)}"
# Копирование входного файла в shared папку
shared_input_path = f"{shared_volume}/input/{job_id}_{os.path.basename(input_file)}"
os.makedirs(os.path.dirname(shared_input_path), exist_ok=True)
shutil.copy2(input_file, shared_input_path)
# Команда для конвертации
command = self._build_conversion_command(
container_input, container_output, conversion_type, options
)
return {
"command": command,
"volumes": {
shared_volume: {
"bind": "/workspace",
"mode": "rw"
}
},
"environment": {
"JOB_ID": job_id,
"CONVERSION_TYPE": conversion_type,
"INPUT_FILE": container_input,
"OUTPUT_FILE": container_output
}
}
def _build_conversion_command(self, input_file: str, output_file: str,
conversion_type: str, options: dict) -> List[str]:
"""Построение команды конвертации"""
conversion_commands = {
"video_to_mp4": [
"ffmpeg", "-i", input_file,
"-c:v", "libx264", "-c:a", "aac",
"-preset", "medium", "-crf", "23",
output_file
],
"audio_to_mp3": [
"ffmpeg", "-i", input_file,
"-c:a", "libmp3lame", "-b:a", "192k",
output_file
],
"image_resize": [
"convert", input_file,
"-resize", options.get("size", "800x600"),
"-quality", str(options.get("quality", 85)),
output_file
],
"extract_thumbnail": [
"ffmpeg", "-i", input_file,
"-ss", "00:00:01", "-frames:v", "1",
"-q:v", "2", output_file
]
}
if conversion_type not in conversion_commands:
raise ConverterError(f"Unsupported conversion type: {conversion_type}")
return conversion_commands[conversion_type]
async def _monitor_conversion_job(self, job_id: str):
"""Мониторинг выполнения задачи конвертации"""
if job_id not in self.active_jobs:
return
job = self.active_jobs[job_id]
container = job["container"]
try:
# Ожидание завершения контейнера
result = container.wait(timeout=300) # 5 минут таймаут
# Получение логов
logs = container.logs().decode('utf-8')
# Обновление статуса задачи
if result["StatusCode"] == 0:
job["status"] = "completed"
job["end_time"] = datetime.utcnow()
# Перемещение выходного файла
await self._handle_conversion_success(job_id)
logger.info(f"Conversion job {job_id} completed successfully")
else:
job["status"] = "failed"
job["error"] = f"Container exited with code {result['StatusCode']}"
job["logs"] = logs
logger.error(f"Conversion job {job_id} failed: {job['error']}")
except docker.errors.ContainerError as e:
job["status"] = "failed"
job["error"] = str(e)
logger.error(f"Conversion job {job_id} container error: {e}")
except Exception as e:
job["status"] = "failed"
job["error"] = str(e)
logger.error(f"Conversion job {job_id} monitoring error: {e}")
finally:
# Очистка ресурсов
await self._cleanup_conversion_job(job_id)
async def _handle_conversion_success(self, job_id: str):
"""Обработка успешного завершения конвертации"""
job = self.active_jobs[job_id]
shared_volume = "/opt/my-network/shared"
# Путь к выходному файлу в shared папке
shared_output_path = f"{shared_volume}/output/{job_id}_{os.path.basename(job['output_file'])}"
if os.path.exists(shared_output_path):
# Перемещение файла в целевое расположение
os.makedirs(os.path.dirname(job["output_file"]), exist_ok=True)
shutil.move(shared_output_path, job["output_file"])
job["output_size"] = os.path.getsize(job["output_file"])
logger.info(f"Output file moved to: {job['output_file']}")
else:
job["status"] = "failed"
job["error"] = "Output file not found after conversion"
async def _cleanup_conversion_job(self, job_id: str):
"""Очистка ресурсов после завершения задачи"""
if job_id not in self.active_jobs:
return
job = self.active_jobs[job_id]
shared_volume = "/opt/my-network/shared"
# Удаление временных файлов
input_cleanup = f"{shared_volume}/input/{job_id}_*"
output_cleanup = f"{shared_volume}/output/{job_id}_*"
for pattern in [input_cleanup, output_cleanup]:
for file_path in glob.glob(pattern):
try:
os.remove(file_path)
except OSError:
pass
# Удаление задачи из активных (через некоторое время для логов)
await asyncio.sleep(60) # Сохраняем информацию 1 минуту
if job_id in self.active_jobs:
del self.active_jobs[job_id]
async def get_job_status(self, job_id: str) -> dict:
"""Получение статуса задачи конвертации"""
if job_id not in self.active_jobs:
return {"status": "not_found", "error": "Job not found"}
job = self.active_jobs[job_id]
status_info = {
"job_id": job_id,
"status": job["status"],
"conversion_type": job["conversion_type"],
"start_time": job["start_time"].isoformat(),
"input_file": job["input_file"],
"output_file": job["output_file"]
}
if "end_time" in job:
status_info["end_time"] = job["end_time"].isoformat()
duration = (job["end_time"] - job["start_time"]).total_seconds()
status_info["duration_seconds"] = duration
if "output_size" in job:
status_info["output_size"] = job["output_size"]
if "error" in job:
status_info["error"] = job["error"]
if "logs" in job:
status_info["logs"] = job["logs"]
return status_info
async def cancel_job(self, job_id: str) -> bool:
"""Отмена задачи конвертации"""
if job_id not in self.active_jobs:
return False
job = self.active_jobs[job_id]
container = job["container"]
try:
# Остановка контейнера
container.stop(timeout=10)
job["status"] = "cancelled"
job["end_time"] = datetime.utcnow()
logger.info(f"Conversion job {job_id} cancelled")
return True
except Exception as e:
logger.error(f"Failed to cancel job {job_id}: {e}")
return False
async def get_active_jobs(self) -> List[dict]:
"""Получение списка активных задач"""
active_jobs = []
for job_id, job in self.active_jobs.items():
if job["status"] == "running":
active_jobs.append({
"job_id": job_id,
"conversion_type": job["conversion_type"],
"start_time": job["start_time"].isoformat(),
"duration_seconds": (datetime.utcnow() - job["start_time"]).total_seconds()
})
return active_jobs
```
## 🔧 Docker Compose конфигурация
### Обновленный docker-compose.yml
```yaml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "15100:15100"
volumes:
# Доступ к Docker socket для управления converter контейнерами
- ${DOCKER_SOCK_PATH:-/var/run/docker.sock}:/var/run/docker.sock
# Общая папка для обмена файлами с converter контейнерами
- ./shared:/opt/my-network/shared
# Хранилище контента
- ./storage:/opt/my-network/storage
environment:
- DOCKER_SOCK_PATH=/var/run/docker.sock
- CONVERTER_ENABLED=true
- CONVERTER_MAX_PARALLEL=3
depends_on:
- postgres
- redis
networks:
- my-network
# Converter образ (только для сборки, не запускается)
converter-module:
build:
context: ./converter-module
dockerfile: Dockerfile
image: my-converter:latest
profiles:
- build-only # Не запускается в обычном режиме
postgres:
image: postgres:15
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- my-network
redis:
image: redis:7-alpine
networks:
- my-network
volumes:
postgres_data:
networks:
my-network:
driver: bridge
```
## 📊 API для управления конвертацией
### REST API endpoints
```python
@app.route('/api/v1/converter/convert', methods=['POST'])
async def start_conversion(request):
"""Запуск задачи конвертации"""
data = request.json
required_fields = ['input_file', 'output_file', 'conversion_type']
for field in required_fields:
if field not in data:
return response.json({'error': f'Missing required field: {field}'}, status=400)
try:
job_id = await converter_manager.convert_file(
input_file=data['input_file'],
output_file=data['output_file'],
conversion_type=data['conversion_type'],
options=data.get('options', {})
)
return response.json({
'job_id': job_id,
'status': 'started',
'message': 'Conversion job started successfully'
})
except ConverterError as e:
return response.json({'error': str(e)}, status=400)
@app.route('/api/v1/converter/status/<job_id>', methods=['GET'])
async def get_conversion_status(request, job_id):
"""Получение статуса задачи конвертации"""
status = await converter_manager.get_job_status(job_id)
return response.json(status)
@app.route('/api/v1/converter/cancel/<job_id>', methods=['POST'])
async def cancel_conversion(request, job_id):
"""Отмена задачи конвертации"""
cancelled = await converter_manager.cancel_job(job_id)
if cancelled:
return response.json({'message': 'Job cancelled successfully'})
else:
return response.json({'error': 'Failed to cancel job or job not found'}, status=404)
@app.route('/api/v1/converter/active', methods=['GET'])
async def get_active_conversions(request):
"""Список активных задач конвертации"""
active_jobs = await converter_manager.get_active_jobs()
return response.json({
'active_jobs': active_jobs,
'total_active': len(active_jobs)
})
@app.route('/api/v1/converter/stats', methods=['GET'])
async def get_converter_stats(request):
"""Статистика работы конвертера"""
return response.json({
'max_parallel_jobs': converter_manager.max_parallel_jobs,
'active_jobs_count': len(converter_manager.active_jobs),
'docker_sock_path': converter_manager.docker_sock_path,
'converter_image': converter_manager.converter_image,
'supported_conversions': [
'video_to_mp4',
'audio_to_mp3',
'image_resize',
'extract_thumbnail'
]
})
```
## 🛠️ Converter Module Dockerfile
### converter-module/Dockerfile
```dockerfile
FROM ubuntu:22.04
# Установка зависимостей
RUN apt-get update && apt-get install -y \
ffmpeg \
imagemagick \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Установка Python зависимостей
RUN pip3 install \
pillow \
opencv-python
# Создание рабочей директории
WORKDIR /workspace
# Копирование скриптов конвертации
COPY scripts/ /usr/local/bin/
RUN chmod +x /usr/local/bin/*
# Создание директорий для файлов
RUN mkdir -p /input /output
# Точка входа
ENTRYPOINT ["/usr/local/bin/convert.sh"]
```
### converter-module/scripts/convert.sh
```bash
#!/bin/bash
set -e
echo "Starting conversion job: $JOB_ID"
echo "Conversion type: $CONVERSION_TYPE"
echo "Input file: $INPUT_FILE"
echo "Output file: $OUTPUT_FILE"
# Функция логирования
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Проверка входного файла
if [ ! -f "/workspace$INPUT_FILE" ]; then
log "ERROR: Input file not found: $INPUT_FILE"
exit 1
fi
# Создание выходной директории
mkdir -p "$(dirname "/workspace$OUTPUT_FILE")"
# Выполнение конвертации в зависимости от типа
case "$CONVERSION_TYPE" in
"video_to_mp4")
log "Converting video to MP4..."
ffmpeg -i "/workspace$INPUT_FILE" \
-c:v libx264 -c:a aac \
-preset medium -crf 23 \
"/workspace$OUTPUT_FILE"
;;
"audio_to_mp3")
log "Converting audio to MP3..."
ffmpeg -i "/workspace$INPUT_FILE" \
-c:a libmp3lame -b:a 192k \
"/workspace$OUTPUT_FILE"
;;
"image_resize")
log "Resizing image..."
convert "/workspace$INPUT_FILE" \
-resize 800x600 \
-quality 85 \
"/workspace$OUTPUT_FILE"
;;
"extract_thumbnail")
log "Extracting thumbnail..."
ffmpeg -i "/workspace$INPUT_FILE" \
-ss 00:00:01 -frames:v 1 \
-q:v 2 \
"/workspace$OUTPUT_FILE"
;;
*)
log "ERROR: Unsupported conversion type: $CONVERSION_TYPE"
exit 1
;;
esac
# Проверка результата
if [ -f "/workspace$OUTPUT_FILE" ]; then
log "Conversion completed successfully"
log "Output file size: $(du -h "/workspace$OUTPUT_FILE" | cut -f1)"
exit 0
else
log "ERROR: Output file was not created"
exit 1
fi
```
## 🔍 Мониторинг и логирование
### Логирование converter операций
```python
class ConverterLogger:
"""Специализированная система логирования для converter операций"""
def __init__(self):
self.logger = logging.getLogger('converter')
self.converter_metrics = {
'total_jobs': 0,
'successful_jobs': 0,
'failed_jobs': 0,
'total_processing_time': 0
}
def log_job_start(self, job_id: str, conversion_type: str, input_file: str):
"""Логирование начала задачи"""
self.logger.info({
'event': 'job_started',
'job_id': job_id,
'conversion_type': conversion_type,
'input_file': os.path.basename(input_file),
'timestamp': datetime.utcnow().isoformat()
})
self.converter_metrics['total_jobs'] += 1
def log_job_completion(self, job_id: str, success: bool, duration: float, output_size: int = None):
"""Логирование завершения задачи"""
if success:
self.converter_metrics['successful_jobs'] += 1
event = 'job_completed'
else:
self.converter_metrics['failed_jobs'] += 1
event = 'job_failed'
self.converter_metrics['total_processing_time'] += duration
log_data = {
'event': event,
'job_id': job_id,
'duration_seconds': duration,
'timestamp': datetime.utcnow().isoformat()
}
if output_size:
log_data['output_size_bytes'] = output_size
self.logger.info(log_data)
def get_metrics(self) -> dict:
"""Получение метрик converter модуля"""
total_jobs = self.converter_metrics['total_jobs']
return {
'total_jobs': total_jobs,
'successful_jobs': self.converter_metrics['successful_jobs'],
'failed_jobs': self.converter_metrics['failed_jobs'],
'success_rate': self.converter_metrics['successful_jobs'] / total_jobs if total_jobs > 0 else 0,
'average_processing_time': self.converter_metrics['total_processing_time'] / total_jobs if total_jobs > 0 else 0
}
```
## 🚀 Интеграция с MY Network
### Автоматическая конвертация при загрузке контента
```python
class ContentProcessingPipeline:
"""Конвейер обработки контента с автоматической конвертацией"""
async def process_uploaded_content(self, content: StoredContent):
"""Обработка загруженного контента"""
# Определение типа контента и необходимых конвертаций
conversions_needed = self._determine_conversions(content)
for conversion in conversions_needed:
try:
job_id = await converter_manager.convert_file(
input_file=content.file_path,
output_file=conversion['output_path'],
conversion_type=conversion['type'],
options=conversion.get('options', {})
)
# Сохранение связи между контентом и задачей конвертации
await self._link_content_conversion(content.id, job_id, conversion['type'])
except ConverterError as e:
logger.error(f"Failed to start conversion for content {content.id}: {e}")
def _determine_conversions(self, content: StoredContent) -> List[dict]:
"""Определение необходимых конвертаций"""
conversions = []
if content.content_type.startswith('video/'):
# Для видео создаем preview и thumbnail
conversions.extend([
{
'type': 'video_to_mp4',
'output_path': content.file_path.replace('.', '_preview.') + 'mp4',
'options': {'quality': 'medium', 'resolution': '720p'}
},
{
'type': 'extract_thumbnail',
'output_path': content.file_path.replace('.', '_thumb.') + 'jpg'
}
])
elif content.content_type.startswith('audio/'):
# Для аудио создаем preview версию
conversions.append({
'type': 'audio_to_mp3',
'output_path': content.file_path.replace('.', '_preview.') + 'mp3',
'options': {'bitrate': '128k', 'duration': '30'} # 30 секунд preview
})
elif content.content_type.startswith('image/'):
# Для изображений создаем thumbnail
conversions.append({
'type': 'image_resize',
'output_path': content.file_path.replace('.', '_thumb.') + 'jpg',
'options': {'size': '300x300', 'quality': 80}
})
return conversions
```
Эта система обеспечивает гибкую и эффективную обработку медиа файлов с полной интеграцией в MY Network архитектуру.

826
docs/INSTALLATION_GUIDE.md Normal file
View File

@ -0,0 +1,826 @@
# MY Network v3.0 - Руководство по установке
## 🚀 Автоматическая установка одной командой
### Быстрая установка
```bash
curl -fsSL https://raw.githubusercontent.com/your-org/my-uploader-bot/main/start.sh | bash
```
### Установка с параметрами
```bash
curl -fsSL https://raw.githubusercontent.com/your-org/my-uploader-bot/main/start.sh | bash -s -- --domain=yourdomain.com --ssl=true
```
## 📋 Требования системы
### Минимальные требования
- **ОС**: Ubuntu 20.04+, Debian 11+, CentOS 8+
- **RAM**: 2GB (рекомендуется 4GB)
- **CPU**: 2 cores
- **Диск**: 20GB свободного места (рекомендуется 100GB для хранения контента)
- **Права**: Root доступ или sudo
### Рекомендуемые требования
- **RAM**: 8GB+
- **CPU**: 4+ cores
- **Диск**: 500GB+ SSD
- **Сеть**: Статический IP, доменное имя
## 🛠️ Подробное описание скрипта start.sh
### Этапы установки
```bash
#!/bin/bash
# MY Network v3.0 Installation Script
set -e
# Переменные по умолчанию
DOMAIN=""
DOCKER_SOCK_PATH="/var/run/docker.sock"
BOOTSTRAP_CONFIG="default"
TELEGRAM_API_KEY=""
CLIENT_TELEGRAM_API_KEY=""
SSL_ENABLED=false
NODE_TYPE="public"
INSTALL_DEPENDENCIES=true
# Функция вывода логов
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Этап 1: Проверка системы и прав
check_system() {
log "🔍 Проверка системы..."
# Проверка прав root
if [[ $EUID -ne 0 ]]; then
log "❌ Скрипт должен запускаться с правами root"
exit 1
fi
# Определение ОС
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
else
log "❌ Неподдерживаемая операционная система"
exit 1
fi
log "✅ Система: $OS $VER"
# Проверка архитектуры
ARCH=$(uname -m)
if [[ "$ARCH" != "x86_64" && "$ARCH" != "aarch64" ]]; then
log "❌ Неподдерживаемая архитектура: $ARCH"
exit 1
fi
log "✅ Архитектура: $ARCH"
}
# Этап 2: Интерактивная настройка
interactive_setup() {
log "🔧 Интерактивная настройка MY Network..."
# Тип ноды
echo ""
echo "Выберите тип ноды:"
echo "1) Основная нода (создает новую сеть)"
echo "2) Подключаемая нода (подключается к существующей сети)"
read -p "Введите номер [1-2]: " node_choice
case $node_choice in
1)
NODE_TYPE="bootstrap"
BOOTSTRAP_CONFIG="new"
log "✅ Выбрана основная нода (создание новой сети)"
;;
2)
NODE_TYPE="public"
BOOTSTRAP_CONFIG="existing"
log "✅ Выбрана подключаемая нода"
;;
*)
log "❌ Неверный выбор. Используется подключаемая нода по умолчанию"
NODE_TYPE="public"
BOOTSTRAP_CONFIG="existing"
;;
esac
# Bootstrap конфигурация
if [[ "$BOOTSTRAP_CONFIG" == "existing" ]]; then
echo ""
read -p "Путь до bootstrap.json [оставьте пустым для дефолтного]: " custom_bootstrap
if [[ -n "$custom_bootstrap" && -f "$custom_bootstrap" ]]; then
BOOTSTRAP_CONFIG="$custom_bootstrap"
log "✅ Использован кастомный bootstrap.json: $custom_bootstrap"
else
log "✅ Использован дефолтный bootstrap.json"
fi
fi
# Docker socket
echo ""
read -p "Путь до docker.sock [$DOCKER_SOCK_PATH]: " custom_docker_sock
if [[ -n "$custom_docker_sock" ]]; then
DOCKER_SOCK_PATH="$custom_docker_sock"
fi
if [[ -S "$DOCKER_SOCK_PATH" ]]; then
log "✅ Docker socket найден: $DOCKER_SOCK_PATH"
else
log "⚠️ Docker socket не найден: $DOCKER_SOCK_PATH (будет установлен Docker)"
fi
# Telegram API ключи
echo ""
read -p "TELEGRAM_API_KEY [оставьте пустым если не нужен бот]: " TELEGRAM_API_KEY
if [[ -n "$TELEGRAM_API_KEY" ]]; then
log "✅ TELEGRAM_API_KEY настроен"
read -p "CLIENT_TELEGRAM_API_KEY [оставьте пустым если не нужен]: " CLIENT_TELEGRAM_API_KEY
if [[ -n "$CLIENT_TELEGRAM_API_KEY" ]]; then
log "✅ CLIENT_TELEGRAM_API_KEY настроен"
fi
else
log " Telegram боты будут отключены"
fi
# SSL сертификат
echo ""
read -p "Настроить SSL сертификат? [y/N]: " ssl_choice
if [[ "$ssl_choice" =~ ^[Yy]$ ]]; then
read -p "Доменное имя: " DOMAIN
if [[ -n "$DOMAIN" ]]; then
SSL_ENABLED=true
log "✅ SSL будет настроен для домена: $DOMAIN"
else
log "❌ Домен не указан. SSL отключен"
fi
fi
# Приватная нода
echo ""
read -p "Создать приватную ноду (только исходящие соединения)? [y/N]: " private_choice
if [[ "$private_choice" =~ ^[Yy]$ ]]; then
NODE_TYPE="private"
log "✅ Создается приватная нода"
fi
}
# Этап 3: Установка зависимостей
install_dependencies() {
log "📦 Установка зависимостей..."
# Обновление системы
log "🔄 Обновление пакетов системы..."
apt update && apt upgrade -y
# Базовые пакеты
log "📦 Установка базовых пакетов..."
apt install -y curl wget git unzip htop nano ufw fail2ban python3 python3-pip
# Docker
if ! command -v docker &> /dev/null; then
log "🐳 Установка Docker..."
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
rm get-docker.sh
# Docker Compose
log "🐳 Установка Docker Compose..."
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# Запуск Docker
systemctl start docker
systemctl enable docker
log "✅ Docker установлен и запущен"
else
log "✅ Docker уже установлен"
fi
# Python зависимости
log "🐍 Установка Python зависимостей..."
pip3 install --upgrade pip
# Проверка версий
docker --version
docker-compose --version
python3 --version
}
# Этап 4: SSL сертификат
setup_ssl() {
if [[ "$SSL_ENABLED" == true && -n "$DOMAIN" ]]; then
log "🔒 Настройка SSL сертификата..."
# Установка certbot
apt install -y certbot python3-certbot-nginx
# Получение сертификата
log "🔒 Получение SSL сертификата для $DOMAIN..."
read -p "Email для уведомлений SSL: " ssl_email
certbot certonly --standalone --non-interactive --agree-tos \
--email "$ssl_email" -d "$DOMAIN"
if [[ $? -eq 0 ]]; then
log "✅ SSL сертификат успешно получен"
# Настройка автообновления
echo "0 2 * * * /usr/bin/certbot renew --quiet" | crontab -
log "✅ Автообновление SSL настроено"
else
log "❌ Ошибка получения SSL сертификата"
SSL_ENABLED=false
fi
fi
}
# Этап 5: Загрузка и настройка проекта
setup_project() {
log "📥 Загрузка MY Network..."
# Создание директории
PROJECT_DIR="/opt/my-network"
mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"
# Клонирование репозитория
if [[ -d "my-uploader-bot" ]]; then
log "🔄 Обновление существующего проекта..."
cd my-uploader-bot
git pull origin main
else
log "📥 Клонирование проекта..."
git clone https://github.com/your-org/my-uploader-bot.git
cd my-uploader-bot
fi
# Права доступа
chmod +x scripts/*.sh
chmod +x *.sh
log "✅ Проект загружен в $PROJECT_DIR/my-uploader-bot"
}
# Этап 6: Генерация конфигурации
generate_config() {
log "⚙️ Генерация конфигурации..."
# Создание .env файла
cat > .env << EOF
# MY Network v3.0 Configuration
# Generated: $(date)
# Node Configuration
NODE_TYPE=$NODE_TYPE
NODE_ID=node-$(date +%s)-$(shuf -i 1000-9999 -n 1)
MY_NETWORK_ENABLED=true
MY_NETWORK_VERSION=3.0.0
# Database (PostgreSQL only)
DATABASE_URL=postgresql://myuser:$(openssl rand -hex 16)@localhost:5432/mynetwork
POSTGRES_DB=mynetwork
POSTGRES_USER=myuser
POSTGRES_PASSWORD=$(openssl rand -hex 16)
# Redis
REDIS_URL=redis://localhost:6379/0
REDIS_ENABLED=true
# Security
SECRET_KEY=$(openssl rand -hex 32)
JWT_SECRET_KEY=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
# API Configuration
API_HOST=0.0.0.0
API_PORT=15100
# Telegram Bots
TELEGRAM_API_KEY=$TELEGRAM_API_KEY
CLIENT_TELEGRAM_API_KEY=$CLIENT_TELEGRAM_API_KEY
# Docker Configuration
DOCKER_SOCK_PATH=$DOCKER_SOCK_PATH
CONVERTER_ENABLED=true
CONVERTER_MAX_PARALLEL=3
# SSL Configuration
SSL_ENABLED=$SSL_ENABLED
DOMAIN=$DOMAIN
# Network Configuration
BOOTSTRAP_CONFIG=$BOOTSTRAP_CONFIG
ALLOW_INCOMING_CONNECTIONS=$([ "$NODE_TYPE" != "private" ] && echo "true" || echo "false")
# Storage Configuration
STORAGE_PATH=/opt/my-network/storage
CONTENT_RETENTION_DAYS=7
# Logging
LOG_LEVEL=INFO
LOG_FORMAT=json
EOF
# Bootstrap конфигурация
if [[ "$BOOTSTRAP_CONFIG" == "new" ]]; then
# Создание нового bootstrap.json для основной ноды
cat > bootstrap.json << EOF
{
"version": "3.0.0",
"network_id": "my-network-$(date +%s)",
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"bootstrap_nodes": [
{
"node_id": "bootstrap-main",
"address": "$([ "$SSL_ENABLED" == true ] && echo "https://$DOMAIN" || echo "http://$(curl -s ifconfig.me || echo localhost)")",
"public_key": "",
"trusted": true,
"node_type": "bootstrap"
}
],
"network_settings": {
"protocol_version": "3.0",
"max_peers": 50,
"sync_interval": 300
}
}
EOF
log "✅ Создан новый bootstrap.json для основной ноды"
elif [[ -f "$BOOTSTRAP_CONFIG" ]]; then
# Копирование кастомного bootstrap.json
cp "$BOOTSTRAP_CONFIG" bootstrap.json
log "✅ Использован кастомный bootstrap.json"
fi
log "✅ Конфигурация сгенерирована"
}
# Этап 7: Сборка и запуск контейнеров
setup_containers() {
log "🐳 Сборка и запуск контейнеров..."
# Сборка образов
log "🔨 Сборка Docker образов..."
docker-compose -f docker-compose.yml build
# Сборка converter image
log "🔄 Сборка converter module..."
docker-compose -f docker-compose.yml build converter-module
# Запуск сервисов
log "🚀 Запуск сервисов..."
docker-compose -f docker-compose.yml up -d
# Ожидание готовности сервисов
log "⏳ Ожидание готовности сервисов..."
sleep 30
# Проверка статуса
log "🔍 Проверка статуса сервисов..."
docker-compose -f docker-compose.yml ps
log "✅ Контейнеры запущены"
}
# Этап 8: Настройка системных сервисов
setup_system_services() {
log "⚙️ Настройка системных сервисов..."
# Создание systemd сервиса
cat > /etc/systemd/system/my-network.service << EOF
[Unit]
Description=MY Network v3.0
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=$PROJECT_DIR/my-uploader-bot
ExecStart=/usr/local/bin/docker-compose -f docker-compose.yml up -d
ExecStop=/usr/local/bin/docker-compose -f docker-compose.yml down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
EOF
# Активация сервиса
systemctl daemon-reload
systemctl enable my-network
# Настройка firewall (если нужно)
if [[ "$NODE_TYPE" != "private" ]]; then
log "🔥 Настройка firewall..."
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 15100/tcp # API порт
if [[ "$SSL_ENABLED" == true ]]; then
ufw allow 80/tcp
ufw allow 443/tcp
fi
ufw --force enable
log "✅ Firewall настроен"
fi
log "✅ Системные сервисы настроены"
}
# Этап 9: Инициализация базы данных
initialize_database() {
log "🗄️ Инициализация базы данных..."
# Ожидание готовности PostgreSQL
log "⏳ Ожидание готовности PostgreSQL..."
sleep 20
# Выполнение миграций
log "🔄 Выполнение миграций базы данных..."
docker-compose -f docker-compose.yml exec -T app alembic upgrade head
if [[ $? -eq 0 ]]; then
log "✅ База данных инициализирована"
else
log "❌ Ошибка инициализации базы данных"
return 1
fi
}
# Этап 10: Подключение к сети
connect_to_network() {
log "🌐 Подключение к MY Network..."
# Ожидание полной готовности
log "⏳ Ожидание готовности всех сервисов..."
sleep 60
# Проверка API
local api_url="http://localhost:15100"
if curl -f "$api_url/health" > /dev/null 2>&1; then
log "✅ API сервис готов"
# Статистика подключения к сети
local network_stats=$(curl -s "$api_url/api/v1/network/stats" || echo "{}")
log "📊 Статистика сети: $network_stats"
# Попытка подключения к bootstrap нодам (если не основная)
if [[ "$NODE_TYPE" != "bootstrap" ]]; then
log "🔗 Подключение к bootstrap нодам..."
curl -X POST "$api_url/api/v1/node/connect" \
-H "Content-Type: application/json" \
-d '{"auto_discover": true}' > /dev/null 2>&1
sleep 10
# Проверка подключений
local peers=$(curl -s "$api_url/api/v1/node/peers" | jq -r '.count // 0' 2>/dev/null || echo "0")
log "👥 Подключено пиров: $peers"
fi
else
log "❌ API сервис недоступен"
return 1
fi
}
# Этап 11: Финальная проверка и отчет
final_report() {
log "📋 Финальная проверка установки..."
# Проверка всех сервисов
echo ""
echo "=== СТАТУС УСТАНОВКИ ==="
# Docker контейнеры
echo "🐳 Docker контейнеры:"
docker-compose -f docker-compose.yml ps
# Системный сервис
echo ""
echo "⚙️ Системный сервис:"
systemctl is-active my-network && echo "✅ my-network: активен" || echo "❌ my-network: не активен"
# Сетевые подключения
echo ""
echo "🌐 Сетевые подключения:"
if [[ "$NODE_TYPE" != "private" ]]; then
netstat -tlnp | grep :15100 && echo "✅ API порт 15100: открыт" || echo "❌ API порт: недоступен"
fi
# Доступность API
echo ""
echo "📡 API доступность:"
local api_endpoint="http://localhost:15100"
if [[ "$SSL_ENABLED" == true ]]; then
api_endpoint="https://$DOMAIN"
fi
if curl -f "$api_endpoint/health" > /dev/null 2>&1; then
echo "✅ API: доступно на $api_endpoint"
echo "✅ Веб-интерфейс: $api_endpoint/api/my/monitor/"
else
echo "❌ API: недоступно"
fi
# Конфигурация
echo ""
echo "⚙️ Конфигурация:"
echo "📁 Проект: $PROJECT_DIR/my-uploader-bot"
echo "🔧 Тип ноды: $NODE_TYPE"
echo "🔒 SSL: $([ "$SSL_ENABLED" == true ] && echo "включен ($DOMAIN)" || echo "отключен")"
echo "🤖 Telegram боты: $([ -n "$TELEGRAM_API_KEY" ] && echo "включены" || echo "отключены")"
echo "🐳 Docker socket: $DOCKER_SOCK_PATH"
# Управление
echo ""
echo "=== КОМАНДЫ УПРАВЛЕНИЯ ==="
echo "🔄 Перезапуск: systemctl restart my-network"
echo "⏹️ Остановка: systemctl stop my-network"
echo "📊 Статус: systemctl status my-network"
echo "📋 Логи: docker-compose -f $PROJECT_DIR/my-uploader-bot/docker-compose.yml logs -f"
echo "🌐 Мониторинг: $api_endpoint/api/my/monitor/"
# Сохранение информации об установке
cat > /opt/my-network-install.log << EOF
MY Network v3.0 Installation Report
Generated: $(date)
Configuration:
- Node Type: $NODE_TYPE
- SSL Enabled: $SSL_ENABLED
- Domain: $DOMAIN
- API Endpoint: $api_endpoint
- Project Path: $PROJECT_DIR/my-uploader-bot
- Docker Socket: $DOCKER_SOCK_PATH
Services:
- API: $api_endpoint
- Monitoring: $api_endpoint/api/my/monitor/
- System Service: my-network
Management Commands:
- Start: systemctl start my-network
- Stop: systemctl stop my-network
- Restart: systemctl restart my-network
- Status: systemctl status my-network
- Logs: docker-compose -f $PROJECT_DIR/my-uploader-bot/docker-compose.yml logs -f
EOF
echo ""
echo "🎉 УСТАНОВКА ЗАВЕРШЕНА!"
echo "📄 Отчет сохранен: /opt/my-network-install.log"
echo ""
}
# Главная функция
main() {
echo "🚀 MY Network v3.0 - Автоматическая установка"
echo "=============================================="
echo ""
# Парсинг аргументов командной строки
while [[ $# -gt 0 ]]; do
case $1 in
--domain=*)
DOMAIN="${1#*=}"
SSL_ENABLED=true
shift
;;
--ssl=*)
SSL_ENABLED="${1#*=}"
shift
;;
--docker-sock=*)
DOCKER_SOCK_PATH="${1#*=}"
shift
;;
--bootstrap=*)
BOOTSTRAP_CONFIG="${1#*=}"
shift
;;
--node-type=*)
NODE_TYPE="${1#*=}"
shift
;;
--no-deps)
INSTALL_DEPENDENCIES=false
shift
;;
*)
echo "Неизвестный параметр: $1"
exit 1
;;
esac
done
# Выполнение этапов установки
check_system
interactive_setup
if [[ "$INSTALL_DEPENDENCIES" == true ]]; then
install_dependencies
fi
setup_ssl
setup_project
generate_config
setup_containers
setup_system_services
initialize_database
connect_to_network
final_report
}
# Обработка ошибок
error_handler() {
log "❌ Ошибка на строке $1. Код выхода: $2"
log "🔄 Попробуйте запустить скрипт заново или обратитесь за поддержкой"
exit $2
}
trap 'error_handler $LINENO $?' ERR
# Запуск установки
main "$@"
```
### Особенности скрипта
1. **Интерактивная настройка**: Задает только необходимые вопросы
2. **Автоматическая детекция**: Определяет ОС и архитектуру
3. **Безопасность**: Генерирует случайные пароли и ключи
4. **Логирование**: Подробные логи каждого этапа
5. **Откат при ошибках**: Информативные сообщения об ошибках
6. **SSL поддержка**: Автоматическое получение сертификатов через Certbot
## 🔧 Ручная установка
### Шаг 1: Подготовка системы
```bash
# Обновление системы
sudo apt update && sudo apt upgrade -y
# Установка базовых пакетов
sudo apt install -y curl wget git unzip htop nano ufw python3 python3-pip
# Установка Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Установка Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```
### Шаг 2: Клонирование проекта
```bash
# Создание директории
sudo mkdir -p /opt/my-network
cd /opt/my-network
# Клонирование репозитория
sudo git clone https://github.com/your-org/my-uploader-bot.git
cd my-uploader-bot
# Права доступа
sudo chmod +x scripts/*.sh
```
### Шаг 3: Настройка конфигурации
```bash
# Копирование примера конфигурации
cp .env.example .env
# Редактирование .env файла
nano .env
```
### Шаг 4: Запуск сервисов
```bash
# Сборка образов
docker-compose build
# Запуск сервисов
docker-compose up -d
# Проверка статуса
docker-compose ps
```
## 🔍 Проверка установки
### Проверка API
```bash
# Здоровье сервиса
curl http://localhost:15100/health
# Статус ноды
curl http://localhost:15100/api/v1/node/status
# Статистика сети
curl http://localhost:15100/api/v1/network/stats
```
### Веб-интерфейс
```
http://localhost:15100/api/my/monitor/
```
### Логи сервисов
```bash
# Все сервисы
docker-compose logs -f
# Конкретный сервис
docker-compose logs -f app
# Системный сервис
journalctl -u my-network -f
```
## 🛠️ Устранение неисправностей
### Частые проблемы
1. **Docker не запускается**
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
2. **Порты заняты**
```bash
sudo netstat -tlnp | grep :15100
sudo fuser -k 15100/tcp
```
3. **Проблемы с правами**
```bash
sudo chown -R $USER:$USER /opt/my-network
sudo usermod -aG docker $USER
```
4. **SSL сертификат не работает**
```bash
sudo certbot certificates
sudo certbot renew --dry-run
```
### Логи установки
Подробные логи установки сохраняются в:
- `/opt/my-network-install.log` - отчет об установке
- `/var/log/my-network/` - логи работы сервисов
## 🔄 Обновление
### Автоматическое обновление
```bash
cd /opt/my-network/my-uploader-bot
git pull origin main
docker-compose build
docker-compose up -d
```
### Ручное обновление
```bash
# Остановка сервисов
sudo systemctl stop my-network
# Обновление кода
cd /opt/my-network/my-uploader-bot
git pull origin main
# Пересборка образов
docker-compose build
# Запуск
sudo systemctl start my-network
```
Эта документация обеспечивает простую и надежную установку MY Network v3.0 на любом Linux сервере.

391
docs/NEW_ARCHITECTURE.md Normal file
View File

@ -0,0 +1,391 @@
# MY Network v3.0 - Децентрализованная архитектура контента
## 🎯 Обзор
MY Network v3.0 представляет собой полностью децентрализованную платформу дистрибьюции контента, где каждая нода принимает независимые решения о принятии и хранении контента. Система исключает централизованное управление и кворумное принятие решений.
## 🏗️ Принципы децентрализации
### 1. Автономность нод
- **Независимое принятие решений**: Каждая нода самостоятельно решает принимать контент или нет
- **Локальные правила**: Настройки фильтрации и политики хранения на уровне ноды
- **Отсутствие консенсуса**: Нет необходимости в голосовании или кворуме
### 2. Доступность без цензуры
- **Множественные источники**: Контент может быть доступен на разных нодах
- **Отсутствие единой точки отказа**: Недоступность одной ноды не влияет на сеть
- **Устойчивость к цензуре**: Контент остается доступным пока есть хотя бы одна нода
### 3. Гибкая топология сети
- **Публичные ноды**: С открытыми портами для входящих соединений
- **Приватные ноды**: Только исходящие соединения, получение контента
- **Bootstrap ноды**: Точки входа в сеть для новых участников
## 🔄 Архитектура синхронизации
### Замена кворумной системы
```python
class ContentAcceptanceManager:
"""Управление принятием контента без консенсуса"""
def __init__(self, node_config: dict):
self.node_config = node_config
self.filters = ContentFilters(node_config)
async def should_accept_content(self, content_metadata: dict) -> bool:
"""
Решение о принятии контента (заглушка для будущего расширения)
В текущей версии всегда возвращает True.
В будущем здесь будут фильтры по:
- Авторам (whitelist/blacklist)
- Типам контента
- Размерам файлов
- Тегам и категориям
"""
# TODO: Добавить логику фильтрации
return True
async def process_content_announcement(self, peer_id: str, announcement: dict):
"""Обработка анонса нового контента от пира"""
content_hash = announcement.get("content_hash")
# Проверка - нужен ли нам этот контент
if await self.should_accept_content(announcement):
# Добавляем в очередь синхронизации
await self.sync_manager.queue_content_sync(peer_id, content_hash)
else:
logger.info(f"Content {content_hash} rejected by local filters")
```
### Протокол синхронизации P2P
```python
class P2PSyncProtocol:
"""Децентрализованный протокол синхронизации"""
async def announce_new_content(self, content: StoredContent):
"""Анонс нового контента всем подключенным пирам"""
announcement = {
"type": "content_announcement",
"content_hash": content.hash,
"file_size": content.file_size,
"content_type": content.content_type,
"encrypted": content.encrypted,
"timestamp": datetime.utcnow().isoformat(),
"author_id": content.author_id,
"tags": content.tags
}
# Рассылаем анонс всем активным пирам
for peer_id in self.active_peers:
try:
await self.send_to_peer(peer_id, announcement)
except Exception as e:
logger.warning(f"Failed to announce to peer {peer_id}: {e}")
async def request_content_sync(self, peer_id: str, content_hash: str):
"""Запрос синхронизации конкретного контента"""
request = {
"type": "sync_request",
"content_hash": content_hash,
"requesting_node": self.node_id,
"timestamp": datetime.utcnow().isoformat()
}
response = await self.send_request_to_peer(peer_id, request)
if response.get("status") == "approved":
await self._sync_content_from_peer(peer_id, content_hash)
else:
logger.info(f"Sync request for {content_hash} denied by {peer_id}")
```
## 🔐 Система шифрования и хэширования
### Единые хэши encrypted_content
```python
class ContentHashManager:
"""Управление хэшированием контента для децентрализованной сети"""
@staticmethod
def calculate_content_hash(content_data: bytes) -> str:
"""
Вычисление единого хэша для encrypted_content
Использует SHA-256 для обеспечения одинакового хэша
на всех нодах для одного и того же контента
"""
return hashlib.sha256(content_data).hexdigest()
@staticmethod
def encrypt_content_for_storage(content_data: bytes) -> tuple[bytes, str]:
"""
Шифрование контента для хранения
Returns:
tuple: (encrypted_data, encryption_key)
"""
# Генерируем уникальный ключ для этого контента
encryption_key = secrets.token_hex(32) # 256-bit key
# Шифруем контент симметричным алгоритмом
cipher = AES.new(
key=bytes.fromhex(encryption_key),
mode=AES.MODE_GCM
)
encrypted_data, auth_tag = cipher.encrypt_and_digest(content_data)
# Добавляем nonce и auth_tag к зашифрованным данным
final_encrypted = cipher.nonce + auth_tag + encrypted_data
return final_encrypted, encryption_key
@staticmethod
def decrypt_content(encrypted_data: bytes, encryption_key: str) -> bytes:
"""Расшифровка контента"""
# Извлекаем nonce, auth_tag и данные
nonce = encrypted_data[:16]
auth_tag = encrypted_data[16:32]
ciphertext = encrypted_data[32:]
# Расшифровываем
cipher = AES.new(
key=bytes.fromhex(encryption_key),
mode=AES.MODE_GCM,
nonce=nonce
)
return cipher.decrypt_and_verify(ciphertext, auth_tag)
```
### Контроль доступа к контенту
```python
class ContentAccessControl:
"""Система контроля доступа к контенту"""
async def can_provide_content(self, requester_node: str, content_hash: str) -> bool:
"""
Решение о предоставлении контента запрашивающей ноде
В текущей версии разрешает доступ всем.
В будущем здесь будут проверки:
- Доверенность ноды
- Коммерческая лицензия
- Региональные ограничения
"""
# TODO: Добавить логику контроля доступа
return True
async def generate_preview_id(self, content_hash: str) -> str:
"""
Генерация ID для preview, не связанного с основным хэшем
Preview ID намеренно не связан с encrypted_content хэшем
для обеспечения безопасности
"""
# Используем случайный UUID для preview
preview_id = str(uuid4())
# Сохраняем связь в локальной базе
await self._store_preview_mapping(content_hash, preview_id)
return preview_id
async def get_content_by_preview_id(self, preview_id: str) -> Optional[bytes]:
"""Получение preview контента по preview_id"""
content_hash = await self._get_content_hash_by_preview_id(preview_id)
if not content_hash:
return None
# Загружаем preview версию (не основной контент!)
preview_content = await StoredContent.get_preview_by_hash(content_hash)
return preview_content.data if preview_content else None
```
## 🌐 Топология сети
### Типы нод
```python
class NodeTypes:
"""Типы нод в MY Network"""
BOOTSTRAP = "bootstrap" # Точки входа в сеть
PUBLIC = "public" # Открытые ноды с входящими соединениями
PRIVATE = "private" # Закрытые ноды, только исходящие соединения
SEED = "seed" # Ноды с большим объемом контента для новых участников
class NodeConfiguration:
"""Конфигурация ноды"""
def __init__(self, config: dict):
self.node_type = config.get("node_type", NodeTypes.PUBLIC)
self.allow_incoming = config.get("allow_incoming", True)
self.max_connections = config.get("max_connections", 50)
self.bootstrap_nodes = config.get("bootstrap_nodes", [])
def is_private_node(self) -> bool:
"""Проверка является ли нода приватной"""
return self.node_type == NodeTypes.PRIVATE or not self.allow_incoming
def can_accept_incoming_connections(self) -> bool:
"""Может ли нода принимать входящие соединения"""
return self.allow_incoming and self.node_type != NodeTypes.PRIVATE
```
### Обнаружение и подключение к нодам
```python
class NetworkDiscovery:
"""Обнаружение нод в децентрализованной сети"""
async def discover_network_nodes(self) -> List[NodeInfo]:
"""Обнаружение доступных нод в сети"""
discovered_nodes = []
# Получаем список нод от bootstrap серверов
for bootstrap_node in self.config.bootstrap_nodes:
try:
nodes = await self._get_nodes_from_bootstrap(bootstrap_node)
discovered_nodes.extend(nodes)
except Exception as e:
logger.warning(f"Failed to get nodes from bootstrap {bootstrap_node}: {e}")
# Получаем список нод от подключенных пиров
for peer_id in self.connected_peers:
try:
nodes = await self._get_nodes_from_peer(peer_id)
discovered_nodes.extend(nodes)
except Exception as e:
logger.warning(f"Failed to get nodes from peer {peer_id}: {e}")
# Убираем дубликаты и себя
unique_nodes = self._deduplicate_nodes(discovered_nodes)
return [node for node in unique_nodes if node.node_id != self.node_id]
async def connect_to_network(self):
"""Подключение к сети MY Network"""
if not self.config.bootstrap_nodes:
logger.error("No bootstrap nodes configured")
return
connected_count = 0
target_connections = min(10, len(self.config.bootstrap_nodes) * 2)
# Подключаемся к bootstrap нодам
for bootstrap_node in self.config.bootstrap_nodes:
try:
if await self._connect_to_node(bootstrap_node):
connected_count += 1
logger.info(f"Connected to bootstrap node: {bootstrap_node}")
except Exception as e:
logger.warning(f"Failed to connect to bootstrap {bootstrap_node}: {e}")
# Если недостаточно соединений, ищем дополнительные ноды
if connected_count < target_connections:
additional_nodes = await self.discover_network_nodes()
for node_info in additional_nodes:
if connected_count >= target_connections:
break
try:
if await self._connect_to_node(node_info):
connected_count += 1
logger.info(f"Connected to discovered node: {node_info.node_id}")
except Exception as e:
logger.warning(f"Failed to connect to node {node_info.node_id}: {e}")
logger.info(f"Network connection complete. Connected to {connected_count} nodes")
```
## 📊 Мониторинг децентрализованной сети
### Статистика сети
```python
class NetworkStatistics:
"""Сбор статистики децентрализованной сети"""
async def collect_network_stats(self) -> dict:
"""Сбор статистики о состоянии сети"""
return {
"node_info": {
"node_id": self.node_id,
"node_type": self.config.node_type,
"version": self.version,
"uptime": self.get_uptime_seconds()
},
"connections": {
"total_peers": len(self.connected_peers),
"bootstrap_peers": len(self.bootstrap_connections),
"incoming_connections": self.get_incoming_connection_count(),
"outgoing_connections": self.get_outgoing_connection_count()
},
"content": {
"total_items": await self.get_local_content_count(),
"encrypted_items": await self.get_encrypted_content_count(),
"public_items": await self.get_public_content_count(),
"storage_used": await self.get_storage_usage_bytes()
},
"sync_activity": {
"content_synced_24h": await self.get_sync_count_24h(),
"pending_sync_items": self.sync_manager.get_pending_count(),
"failed_sync_attempts": self.sync_manager.get_failed_count(),
"last_sync_time": self.sync_manager.last_sync_time
},
"network_health": {
"reachable_nodes": await self.count_reachable_nodes(),
"network_latency_avg": await self.calculate_avg_network_latency(),
"content_availability": await self.calculate_content_availability()
}
}
```
## 🔧 API для децентрализованной сети
### Новые эндпоинты
```python
# Управление нодой
GET /api/v1/node/status # Статус ноды и подключений
GET /api/v1/node/peers # Список подключенных пиров
POST /api/v1/node/connect # Подключение к новому пиру
POST /api/v1/node/disconnect # Отключение от пира
# Обнаружение сети
GET /api/v1/network/discover # Обнаружение доступных нод
GET /api/v1/network/stats # Статистика сети
GET /api/v1/network/topology # Топология подключений
# Синхронизация контента
GET /api/v1/sync/status # Статус синхронизации
POST /api/v1/sync/request # Запрос синхронизации контента
GET /api/v1/sync/pending # Список ожидающей синхронизации
POST /api/v1/sync/cancel # Отмена синхронизации
# Поиск контента в сети
GET /api/v1/content/search # Поиск контента по хэшу в сети
GET /api/v1/content/{hash}/nodes # Список нод с конкретным контентом
POST /api/v1/content/announce # Анонс нового контента в сеть
# Управление доступом
POST /api/v1/access/request # Запрос доступа к контенту
GET /api/v1/access/policies # Политики доступа к контенту
PUT /api/v1/access/policies # Обновление политик доступа
```
Эта архитектура обеспечивает полную децентрализацию при сохранении гибкости и безопасности системы.

602
docs/NEW_PROTOCOL_V3.md Normal file
View File

@ -0,0 +1,602 @@
# MY Network v3.0 - Новый протокол децентрализованной синхронизации
## 🎯 Обзор протокола v3.0
MY Network v3.0 полностью отказывается от кворумной системы в пользу децентрализованной архитектуры, где каждая нода принимает независимые решения о принятии и хранении контента.
## 🔄 Протокол индивидуальной синхронизации
### Принципы новой системы
1. **Автономность решений**: Каждая нода самостоятельно решает принимать контент или нет
2. **Отсутствие консенсуса**: Нет необходимости в голосовании других нод
3. **Гибкая фильтрация**: Настраиваемые правила принятия контента на уровне ноды
4. **Устойчивость к цензуре**: Контент доступен пока есть хотя бы одна нода
### Новый алгоритм синхронизации
```python
class DecentralizedSyncProtocol:
"""Протокол децентрализованной синхронизации v3.0"""
def __init__(self, node_config: dict):
self.node_id = node_config["node_id"]
self.content_filter = IndividualContentFilter(node_config)
self.sync_manager = SyncManager()
self.active_peers = {}
async def handle_content_announcement(self, peer_id: str, announcement: dict) -> bool:
"""
Обработка анонса нового контента от пира
Новая логика v3.0:
1. Каждая нода принимает решение индивидуально
2. Нет консенсуса или голосования
3. Простая заглушка для фильтрации (пока всегда True)
"""
content_hash = announcement.get("content_hash")
content_metadata = announcement.get("metadata", {})
logger.info(f"Received content announcement from {peer_id}: {content_hash}")
# Проверка - не дубликат ли это
if await self._content_already_exists(content_hash):
logger.debug(f"Content {content_hash} already exists locally")
return False
# Индивидуальное решение о принятии контента
should_accept = await self.content_filter.should_accept_content(
content_hash, content_metadata, peer_id
)
if should_accept:
logger.info(f"Accepting content {content_hash} from {peer_id}")
# Добавление в очередь синхронизации
await self.sync_manager.queue_content_sync(
peer_id=peer_id,
content_hash=content_hash,
metadata=content_metadata
)
return True
else:
logger.info(f"Rejecting content {content_hash} from {peer_id}")
return False
async def sync_content_from_peer(self, peer_id: str, content_hash: str,
metadata: dict) -> bool:
"""Синхронизация контента с пира (без консенсуса)"""
try:
# Запрос метаданных контента
content_info = await self._request_content_info(peer_id, content_hash)
if not content_info:
logger.error(f"Failed to get content info for {content_hash}")
return False
# Проверка возможности получения контента от пира
access_granted = await self._request_content_access(peer_id, content_hash)
if not access_granted:
logger.warning(f"Access denied for content {content_hash} from {peer_id}")
return False
# Загрузка контента
content_data = await self._download_content(peer_id, content_hash)
if not content_data:
logger.error(f"Failed to download content {content_hash}")
return False
# Проверка целостности
if not await self._verify_content_integrity(content_hash, content_data):
logger.error(f"Content integrity check failed: {content_hash}")
return False
# Сохранение контента локально
stored_content = await self._store_content_locally(
content_hash, content_data, metadata
)
if stored_content:
logger.info(f"Successfully synced content {content_hash} from {peer_id}")
# Анонс нового контента другим пирам (кроме источника)
await self._announce_content_to_peers(stored_content, exclude_peer=peer_id)
return True
except Exception as e:
logger.error(f"Failed to sync content {content_hash} from {peer_id}: {e}")
return False
class IndividualContentFilter:
"""Система индивидуальной фильтрации контента"""
def __init__(self, node_config: dict):
self.node_config = node_config
self.whitelist_authors = set(node_config.get("whitelist_authors", []))
self.blacklist_authors = set(node_config.get("blacklist_authors", []))
self.allowed_content_types = set(node_config.get("allowed_content_types", ["*"]))
self.max_file_size = node_config.get("max_file_size", 100 * 1024 * 1024) # 100MB
async def should_accept_content(self, content_hash: str, metadata: dict,
peer_id: str) -> bool:
"""
Решение о принятии контента (заглушка для будущего расширения)
В текущей версии v3.0 всегда возвращает True.
В будущем здесь будут фильтры по:
- Авторам (whitelist/blacklist)
- Типам контента
- Размерам файлов
- Тегам и категориям
- Репутации пира
"""
# TODO: Добавить реальную логику фильтрации
# Пока принимаем весь контент
return True
# Пример будущей логики фильтрации:
"""
author_id = metadata.get("author_id")
content_type = metadata.get("content_type")
file_size = metadata.get("file_size", 0)
# Проверка черного списка авторов
if author_id in self.blacklist_authors:
return False
# Проверка белого списка (если не пустой)
if self.whitelist_authors and author_id not in self.whitelist_authors:
return False
# Проверка типа контента
if "*" not in self.allowed_content_types:
if content_type not in self.allowed_content_types:
return False
# Проверка размера файла
if file_size > self.max_file_size:
return False
return True
"""
```
## 🔐 Новая система шифрования и безопасности
### Архитектура безопасности v3.0
```python
class ContentSecurityManager:
"""Управление безопасностью контента в MY Network v3.0"""
def __init__(self):
self.hash_algorithm = "sha256"
self.encryption_algorithm = "AES-256-GCM"
async def create_encrypted_content(self, content_data: bytes,
content_metadata: dict) -> dict:
"""
Создание зашифрованного контента с единым хэшем
Система v3.0:
1. Симметричное шифрование с уникальным ключом для каждого контента
2. Единый хэш encrypted_content на всех нодах
3. Отдельные preview_id не связанные с основным хэшем
"""
# Генерация уникального ключа шифрования для этого контента
encryption_key = self._generate_content_key()
# Шифрование контента
encrypted_data = await self._encrypt_content(content_data, encryption_key)
# Вычисление единого хэша зашифрованного контента
encrypted_content_hash = self._calculate_deterministic_hash(encrypted_data)
# Создание отдельного preview_id (не связанного с хэшем)
preview_id = str(uuid4())
return {
"encrypted_content_hash": encrypted_content_hash, # Одинаковый на всех нодах
"encrypted_data": encrypted_data,
"encryption_key": encryption_key, # Передается при продаже
"preview_id": preview_id, # Для публичного доступа к preview
"metadata": content_metadata
}
def _generate_content_key(self) -> str:
"""Генерация уникального ключа для контента"""
return secrets.token_hex(32) # 256-bit key
async def _encrypt_content(self, content_data: bytes, encryption_key: str) -> bytes:
"""Симметричное шифрование контента"""
# Преобразование ключа в байты
key_bytes = bytes.fromhex(encryption_key)
# Создание шифратора AES-256-GCM
cipher = AES.new(key_bytes, AES.MODE_GCM)
# Шифрование
ciphertext, auth_tag = cipher.encrypt_and_digest(content_data)
# Объединение nonce, auth_tag и ciphertext для детерминированного хэша
encrypted_data = cipher.nonce + auth_tag + ciphertext
return encrypted_data
def _calculate_deterministic_hash(self, encrypted_data: bytes) -> str:
"""
Вычисление детерминированного хэша зашифрованного контента
ВАЖНО: Этот хэш будет одинаковым на всех нодах для одного контента,
но через него нельзя получить доступ к самому контенту
"""
return hashlib.sha256(encrypted_data).hexdigest()
async def decrypt_content(self, encrypted_data: bytes, encryption_key: str) -> bytes:
"""Расшифровка контента с помощью ключа"""
# Извлечение компонентов
nonce = encrypted_data[:16] # AES-GCM nonce: 16 bytes
auth_tag = encrypted_data[16:32] # Auth tag: 16 bytes
ciphertext = encrypted_data[32:] # Остальное - зашифрованные данные
# Создание дешифратора
key_bytes = bytes.fromhex(encryption_key)
cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce)
# Расшифровка с проверкой подлинности
decrypted_data = cipher.decrypt_and_verify(ciphertext, auth_tag)
return decrypted_data
async def create_preview_content(self, original_content: bytes,
content_type: str) -> tuple[bytes, str]:
"""
Создание preview контента (не зашифрованного)
Preview:
- Не связан с основным хэшем контента
- Имеет собственный уникальный ID
- Доступен публично без ключей
"""
# Создание preview в зависимости от типа контента
if content_type.startswith("audio/"):
# Для аудио - первые 30 секунд
preview_content = await self._create_audio_preview(original_content)
elif content_type.startswith("video/"):
# Для видео - первые 30 секунд + watermark
preview_content = await self._create_video_preview(original_content)
elif content_type.startswith("image/"):
# Для изображений - уменьшенная версия с watermark
preview_content = await self._create_image_preview(original_content)
else:
# Для других типов - описание или thumbnail
preview_content = await self._create_generic_preview(original_content)
# Уникальный ID для preview (НЕ хэш!)
preview_id = str(uuid4())
return preview_content, preview_id
class ContentAccessControl:
"""Система контроля доступа к контенту"""
async def can_provide_content(self, requesting_node: str, content_hash: str,
access_type: str = "full") -> dict:
"""
Проверка возможности предоставления контента
В v3.0 пока разрешаем всем (заглушка для будущего расширения)
"""
# TODO: Добавить реальную логику контроля доступа
# Пока разрешаем всем
return {
"access_granted": True,
"access_type": access_type,
"reason": "Open access policy"
}
# Пример будущей логики:
"""
# Проверка репутации ноды
node_reputation = await self._get_node_reputation(requesting_node)
if node_reputation < 0.5:
return {"access_granted": False, "reason": "Low node reputation"}
# Проверка коммерческих лицензий
content = await StoredContent.get_by_hash(content_hash)
if content and content.commercial_license:
license_valid = await self._check_commercial_license(
requesting_node, content_hash
)
if not license_valid:
return {"access_granted": False, "reason": "No valid license"}
# Проверка региональных ограничений
node_region = await self._get_node_region(requesting_node)
if node_region in content.restricted_regions:
return {"access_granted": False, "reason": "Regional restrictions"}
return {"access_granted": True, "access_type": access_type}
"""
async def get_content_by_preview_id(self, preview_id: str) -> Optional[bytes]:
"""
Получение preview контента по preview_id
Preview доступен всем без ограничений
"""
# Поиск preview по ID в базе данных
preview_content = await PreviewContent.get_by_preview_id(preview_id)
if preview_content:
return preview_content.data
return None
async def request_content_key(self, content_hash: str, user_license: dict) -> Optional[str]:
"""
Запрос ключа расшифровки контента
Ключ выдается только при наличии соответствующих прав
"""
# Проверка лицензии пользователя
if await self._validate_user_license(content_hash, user_license):
# Получение ключа расшифровки
content = await StoredContent.get_by_hash(content_hash)
if content:
return content.encryption_key
return None
```
## 🌐 Обновленный протокол обмена сообщениями
### Новые типы сообщений v3.0
```python
class P2PMessageTypes:
"""Типы сообщений протокола MY Network v3.0"""
# Базовые сообщения
HANDSHAKE = "handshake"
HANDSHAKE_RESPONSE = "handshake_response"
# Объявления о контенте (без консенсуса)
CONTENT_ANNOUNCEMENT = "content_announcement"
CONTENT_REQUEST = "content_request"
CONTENT_RESPONSE = "content_response"
# Синхронизация
SYNC_REQUEST = "sync_request"
SYNC_RESPONSE = "sync_response"
SYNC_DATA = "sync_data"
# Доступ к контенту
ACCESS_REQUEST = "access_request"
ACCESS_RESPONSE = "access_response"
# Версионирование
VERSION_INFO = "version_info"
VERSION_WARNING = "version_warning"
class P2PMessageHandler:
"""Обработчик P2P сообщений протокола v3.0"""
async def handle_content_announcement(self, peer_id: str, message: dict):
"""Обработка анонса контента (без консенсуса)"""
content_hash = message.get("content_hash")
metadata = message.get("metadata", {})
# Индивидуальное решение о принятии
should_accept = await self.sync_protocol.handle_content_announcement(
peer_id, message
)
# Отправка ответа пиру
response = {
"type": P2PMessageTypes.CONTENT_RESPONSE,
"content_hash": content_hash,
"accepted": should_accept,
"node_id": self.node_id,
"timestamp": datetime.utcnow().isoformat()
}
await self.send_message_to_peer(peer_id, response)
async def handle_sync_request(self, peer_id: str, message: dict):
"""Обработка запроса синхронизации"""
content_hash = message.get("content_hash")
# Проверка доступа к контенту
access_check = await self.access_control.can_provide_content(
peer_id, content_hash
)
if access_check["access_granted"]:
# Предоставление контента
content_data = await self._get_content_data(content_hash)
response = {
"type": P2PMessageTypes.SYNC_RESPONSE,
"content_hash": content_hash,
"status": "approved",
"data_size": len(content_data) if content_data else 0
}
else:
# Отказ в доступе
response = {
"type": P2PMessageTypes.SYNC_RESPONSE,
"content_hash": content_hash,
"status": "denied",
"reason": access_check.get("reason", "Access denied")
}
await self.send_message_to_peer(peer_id, response)
# Если доступ разрешен, отправляем данные
if access_check["access_granted"] and content_data:
await self._send_content_data(peer_id, content_hash, content_data)
```
## 📋 Новые API эндпоинты v3.0
### Обновленные API для децентрализованной архитектуры
```python
# Индивидуальная синхронизация (без консенсуса)
POST /api/v3/sync/announce # Анонс контента в сеть
GET /api/v3/sync/pending # Ожидающие синхронизации
POST /api/v3/sync/accept/{hash} # Принять конкретный контент
POST /api/v3/sync/reject/{hash} # Отклонить конкретный контент
# Управление фильтрами контента
GET /api/v3/filters/content # Текущие фильтры
PUT /api/v3/filters/content # Обновить фильтры
POST /api/v3/filters/test # Тестирование фильтров
# Безопасность контента
GET /api/v3/content/{hash}/preview/{preview_id} # Получение preview
POST /api/v3/content/{hash}/request-key # Запрос ключа расшифровки
GET /api/v3/content/{hash}/access-info # Информация о доступе
# Статистика децентрализованной сети
GET /api/v3/network/nodes # Известные ноды в сети
GET /api/v3/network/content/distribution # Распределение контента
GET /api/v3/network/sync/stats # Статистика синхронизации
```
## 🔄 Алгоритм удаления неиспользуемого контента
### Система очистки контента
```python
class ContentCleanupManager:
"""Управление очисткой контента через 7 дней"""
def __init__(self):
self.retention_days = 7
self.cleanup_interval = 3600 # 1 час
async def start_cleanup_scheduler(self):
"""Запуск планировщика очистки"""
while True:
try:
await self.cleanup_expired_content()
await asyncio.sleep(self.cleanup_interval)
except Exception as e:
logger.error(f"Cleanup scheduler error: {e}")
await asyncio.sleep(60) # Retry in 1 minute
async def cleanup_expired_content(self):
"""Очистка просроченного контента"""
cutoff_date = datetime.utcnow() - timedelta(days=self.retention_days)
# Поиск контента без blockchain регистрации
expired_content = await self._find_expired_content(cutoff_date)
for content in expired_content:
try:
# Проверка отсутствия blockchain записи
blockchain_exists = await self._check_blockchain_registration(content.hash)
if not blockchain_exists:
# Удаление файла и записи
await self._remove_content(content)
logger.info(f"Removed expired content: {content.hash}")
else:
# Обновление статуса - контент зарегистрирован
content.blockchain_registered = True
await content.save()
except Exception as e:
logger.error(f"Failed to cleanup content {content.hash}: {e}")
async def _find_expired_content(self, cutoff_date: datetime) -> List[StoredContent]:
"""Поиск просроченного контента"""
return await StoredContent.filter(
created_at__lt=cutoff_date,
blockchain_registered=False
).all()
async def _check_blockchain_registration(self, content_hash: str) -> bool:
"""Проверка регистрации контента в блокчейне"""
# TODO: Реализовать проверку TON блокчейна
# Пока возвращаем False для тестирования
return False
async def _remove_content(self, content: StoredContent):
"""Удаление контента и связанных файлов"""
# Удаление основного файла
if os.path.exists(content.file_path):
os.remove(content.file_path)
# Удаление preview файлов
preview_files = await PreviewContent.filter(parent_content_id=content.id).all()
for preview in preview_files:
if os.path.exists(preview.file_path):
os.remove(preview.file_path)
await preview.delete()
# Удаление записи из базы данных
await content.delete()
```
## 📊 Мониторинг новой системы
### Расширенная система мониторинга
```python
class DecentralizedNetworkMonitor:
"""Мониторинг децентрализованной сети v3.0"""
async def get_network_health(self) -> dict:
"""Здоровье децентрализованной сети"""
return {
"decentralization_metrics": {
"total_known_nodes": await self._count_known_nodes(),
"active_connections": len(self.active_peers),
"content_distribution": await self._analyze_content_distribution(),
"network_redundancy": await self._calculate_network_redundancy()
},
"sync_metrics": {
"pending_sync_items": self.sync_manager.get_pending_count(),
"successful_syncs_24h": await self._get_sync_count(hours=24),
"failed_syncs_24h": await self._get_failed_sync_count(hours=24),
"avg_sync_time": await self._get_avg_sync_time()
},
"content_metrics": {
"total_content_items": await self._count_local_content(),
"content_accepted_rate": await self._get_acceptance_rate(),
"content_cleanup_stats": await self._get_cleanup_stats(),
"storage_usage": await self._get_storage_usage()
},
"security_metrics": {
"access_requests_24h": await self._count_access_requests(hours=24),
"denied_access_rate": await self._get_denied_access_rate(),
"encryption_status": "AES-256-GCM",
"version_compatibility": await self._get_version_compatibility_stats()
}
}
```
Этот новый протокол обеспечивает полную децентрализацию MY Network при сохранении безопасности и эффективности системы.

341
docs/README_V3.md Normal file
View File

@ -0,0 +1,341 @@
# MY Network v3.0 - Полная документация децентрализованной платформы
## 🎯 Обзор MY Network v3.0
MY Network v3.0 представляет собой **полностью децентрализованную платформу дистрибьюции контента**, где каждая нода принимает независимые решения о принятии и хранении контента без необходимости консенсуса или кворума.
## 📚 Структура документации
### 🏗️ Архитектурные документы
1. **[NEW_ARCHITECTURE.md](NEW_ARCHITECTURE.md)** - Основная архитектура v3.0
- Принципы децентрализации
- Замена кворумной системы
- Новая топология сети
- API для децентрализованной сети
2. **[NEW_PROTOCOL_V3.md](NEW_PROTOCOL_V3.md)** - Протокол синхронизации v3.0
- Индивидуальные решения нод
- Система шифрования контента
- Новые типы P2P сообщений
- Алгоритм очистки контента
### 🛠️ Техническая документация
3. **[INSTALLATION_GUIDE.md](INSTALLATION_GUIDE.md)** - Установка и развертывание
- Автоматическая установка через start.sh
- Интерактивная настройка параметров
- SSL сертификаты и безопасность
- Docker развертывание
4. **[CONVERTER_MODULE.md](CONVERTER_MODULE.md)** - Converter и Docker интеграция
- On-demand конвертация файлов
- Интеграция с docker.sock
- Параллельная обработка
- API управления конвертацией
5. **[VERSIONING_COMPATIBILITY.md](VERSIONING_COMPATIBILITY.md)** - Версионирование нод
- Семантическое версионирование
- Проверка совместимости
- Автоматические обновления
- Мониторинг версий в сети
### 📋 Аналитические документы
6. **[tasks.txt](../tasks.txt)** - Анализ готовности функций
- Готовые компоненты (65%)
- Требующие доработки
- Новые функции для реализации
- План приоритетных задач
## 🚀 Ключевые изменения в v3.0
### ❌ Удаленные компоненты
- **Кворумная система консенсуса** - полностью удалена
- **Голосование за принятие контента** - заменено индивидуальными решениями
- **Централизованное управление** - отсутствует
- **Обязательная репликация** - заменена добровольной
### ✅ Новые возможности
- **Автономность нод** - каждая нода решает самостоятельно
- **Устойчивость к цензуре** - контент доступен пока есть хотя бы одна нода
- **Гибкая фильтрация** - настраиваемые правила на уровне ноды
- **Приватные ноды** - поддержка нод без входящих соединений
- **Автоматическая установка** - единый скрипт start.sh
- **Версионная совместимость** - контроль совместимости протоколов
## 🔐 Система безопасности v3.0
### Шифрование контента
```
Контент -> AES-256-GCM шифрование -> Единый хэш на всех нодах
encrypted_content_hash (поиск в сети)
Отдельный preview_id (публичный доступ)
```
### Принципы безопасности
1. **Симметричное шифрование** - уникальный ключ для каждого контента
2. **Детерминированные хэши** - одинаковые на всех нодах для поиска
3. **Изолированные preview** - не связаны с основным контентом
4. **Контроль доступа** - гибкая система разрешений
## 🌐 Архитектура сети
### Типы нод
- **Bootstrap ноды** - точки входа в сеть
- **Публичные ноды** - принимают входящие соединения
- **Приватные ноды** - только исходящие соединения
- **Seed ноды** - с большим объемом контента
### Протокол синхронизации
```
1. Анонс контента -> 2. Индивидуальное решение -> 3. Синхронизация
↓ ↓ ↓
всем пирам фильтры ноды прямая загрузка
```
## 📦 Установка одной командой
### Быстрая установка
```bash
curl -fsSL https://raw.githubusercontent.com/your-org/my-uploader-bot/main/start.sh | bash
```
### Что устанавливается
- ✅ Python, Docker, все зависимости
- ✅ PostgreSQL база данных
- ✅ Redis кэширование
- ✅ Converter module для медиа
- ✅ SSL сертификаты (опционально)
- ✅ Firewall и безопасность
- ✅ Мониторинг и логирование
## 🔧 Конфигурация
### Основные параметры
```bash
# Тип ноды
NODE_TYPE=public|private|bootstrap
# Telegram интеграция
TELEGRAM_API_KEY=your_key
CLIENT_TELEGRAM_API_KEY=your_key
# Docker интеграция
DOCKER_SOCK_PATH=/var/run/docker.sock
# SSL настройки
SSL_ENABLED=true
DOMAIN=your-domain.com
# Фильтрация контента (заглушка)
CONTENT_FILTER_ENABLED=true
```
## 📊 API Endpoints v3.0
### Управление нодой
```
GET /api/v3/node/status # Статус ноды
GET /api/v3/node/peers # Подключенные пиры
POST /api/v3/node/connect # Подключение к пиру
```
### Синхронизация контента
```
POST /api/v3/sync/announce # Анонс контента
GET /api/v3/sync/pending # Ожидающие синхронизации
POST /api/v3/sync/accept/{hash} # Принять контент
```
### Безопасность
```
GET /api/v3/content/{hash}/preview/{id} # Получение preview
POST /api/v3/content/{hash}/request-key # Запрос ключа
```
### Мониторинг
```
GET /api/v3/network/stats # Статистика сети
GET /api/v3/system/version # Информация о версии
GET /api/v3/converter/active # Активные конвертации
```
## 🔄 Converter Module
### On-Demand обработка
- **Автоматический запуск** контейнеров при необходимости
- **Параллельная обработка** до N файлов одновременно
- **Автоудаление** контейнеров после завершения
- **Интеграция с docker.sock** для управления контейнерами
### Поддерживаемые форматы
- **Видео** → MP4 конвертация + thumbnail
- **Аудио** → MP3 конвертация + preview
- **Изображения** → resize + thumbnail
- **Документы** → preview + metadata
## 📈 Мониторинг
### Веб-интерфейс
```
https://your-domain.com/api/my/monitor/
```
### Ключевые метрики
- **Сетевые соединения** - количество пиров
- **Синхронизация** - статус и прогресс
- **Контент** - количество и распределение
- **Производительность** - CPU, память, диск
- **Версии** - совместимость в сети
## 🛡️ Безопасность
### Уровни защиты
1. **Сетевая безопасность** - firewall, SSL, rate limiting
2. **Контент безопасность** - шифрование, контроль доступа
3. **Нода безопасность** - версионная совместимость
4. **API безопасность** - аутентификация, валидация
### Автоматические меры
- **SSL сертификаты** через Let's Encrypt
- **Fail2ban** защита от брутфорса
- **UFW firewall** базовая защита
- **Автообновления** безопасности
## 🔄 Миграция с v2.x
### Совместимость
- ✅ **База данных** - полная совместимость
- ✅ **Контент** - сохранение всех файлов
- ✅ **API** - обратная совместимость
- ❌ **Протокол** - требуется обновление
### Процесс миграции
1. **Backup** существующих данных
2. **Обновление** кода до v3.0
3. **Миграция** базы данных
4. **Настройка** новых параметров
5. **Запуск** в новом режиме
## 📋 Roadmap развития
### Phase 1 (Текущая) - Основы v3.0
- [x] Архитектура децентрализации
- [x] Базовая система синхронизации
- [x] Автоматическая установка
- [x] Версионная совместимость
### Phase 2 - Расширенная функциональность
- [ ] Реализация фильтров контента
- [ ] Продвинутый контроль доступа
- [ ] Региональные ограничения
- [ ] Коммерческие лицензии
### Phase 3 - Оптимизация
- [ ] Производительность сети
- [ ] Улучшенная безопасность
- [ ] Расширенный мониторинг
- [ ] Автоматическое масштабирование
## 🤝 Участие в разработке
### Структура проекта
```
my-uploader-bot/
├── app/ # Основное приложение
│ ├── core/ # Ядро системы
│ │ ├── my_network/ # MY Network v3.0
│ │ ├── content/ # Управление контентом
│ │ └── security/ # Безопасность
│ ├── api/ # REST API
│ └── scripts/ # Утилиты
├── converter-module/ # Модуль конвертации
├── web2-client/ # React фронтенд
├── docs/ # Документация v3.0
└── start.sh # Скрипт установки
```
### Вклад в проект
1. **Fork** репозитория
2. **Create** feature branch
3. **Implement** изменения
4. **Test** функциональность
5. **Submit** pull request
## 📞 Поддержка
### Документация
- **Архитектура**: [NEW_ARCHITECTURE.md](NEW_ARCHITECTURE.md)
- **Установка**: [INSTALLATION_GUIDE.md](INSTALLATION_GUIDE.md)
- **Протокол**: [NEW_PROTOCOL_V3.md](NEW_PROTOCOL_V3.md)
### Команды управления
```bash
# Управление сервисом
systemctl start|stop|restart my-network
# Просмотр логов
journalctl -u my-network -f
docker-compose logs -f
# Мониторинг
curl https://your-domain.com/api/v3/network/stats
```
### Диагностика
```bash
# Проверка статуса
curl http://localhost:15100/health
# Проверка подключений
curl http://localhost:15100/api/v3/node/peers
# Проверка синхронизации
curl http://localhost:15100/api/v3/sync/pending
```
---
## 🎉 Заключение
MY Network v3.0 представляет собой **новое поколение децентрализованных платформ** для дистрибьюции контента. Система обеспечивает:
- 🌐 **Полную децентрализацию** без единых точек отказа
- 🔒 **Надежную безопасность** с современным шифрованием
- 🚀 **Простую установку** одной командой
- 📈 **Масштабируемость** для любого количества нод
- 🛡️ **Устойчивость к цензуре** через множественные источники
**MY Network v3.0 - Будущее децентрализованной дистрибьюции контента!**
---
*Документация MY Network v3.0 | 2025 | Версия 1.0*

View File

@ -0,0 +1,489 @@
# MY Network v3.0 - Система версионирования и совместимости
## 🏷️ Семантическое версионирование
MY Network использует семантическое версионирование в формате `MAJOR.MINOR.PATCH`:
- **MAJOR**: Критические изменения протокола, несовместимые изменения
- **MINOR**: Новая функциональность, обратно совместимая
- **PATCH**: Исправления ошибок, совместимые изменения
### Примеры версий
- `3.0.0` - Основная версия с децентрализованной архитектурой
- `3.1.0` - Добавление новых функций синхронизации
- `3.1.2` - Исправление ошибок в синхронизации
## 🔒 Правила совместимости
### Критические ограничения (MAJOR версии)
```python
class VersionCompatibility:
"""Система проверки совместимости версий нод"""
def __init__(self, current_version: str):
self.current_version = self.parse_version(current_version)
def parse_version(self, version_string: str) -> dict:
"""Парсинг версии в структурированный формат"""
try:
major, minor, patch = version_string.split('.')
return {
"major": int(major),
"minor": int(minor),
"patch": int(patch),
"string": version_string
}
except ValueError:
raise ValueError(f"Invalid version format: {version_string}")
def check_compatibility(self, peer_version: str) -> dict:
"""
Проверка совместимости с версией пира
Returns:
dict: {
"compatible": bool,
"level": "compatible|warning|blocked",
"message": str,
"allow_connection": bool
}
"""
peer_ver = self.parse_version(peer_version)
current_ver = self.current_version
# Проверка MAJOR версии
if peer_ver["major"] != current_ver["major"]:
return {
"compatible": False,
"level": "blocked",
"message": f"Incompatible major version: {peer_version} vs {current_ver['string']}. Connection blocked.",
"allow_connection": False
}
# Проверка MINOR версии
if peer_ver["minor"] != current_ver["minor"]:
newer_version = peer_ver if peer_ver["minor"] > current_ver["minor"] else current_ver
older_version = peer_ver if peer_ver["minor"] < current_ver["minor"] else current_ver
return {
"compatible": True,
"level": "warning",
"message": f"Different minor version: {peer_version} vs {current_ver['string']}. Some features may be unavailable.",
"allow_connection": True,
"recommendation": f"Consider updating to v{newer_version['major']}.{newer_version['minor']}.x"
}
# PATCH различия - полная совместимость
if peer_ver["patch"] != current_ver["patch"]:
return {
"compatible": True,
"level": "compatible",
"message": f"Compatible versions: {peer_version} vs {current_ver['string']}",
"allow_connection": True
}
# Идентичные версии
return {
"compatible": True,
"level": "compatible",
"message": f"Identical versions: {peer_version}",
"allow_connection": True
}
```
### Матрица совместимости
| Peer Version | Current Version | Result | Action |
|--------------|-----------------|--------|---------|
| `3.0.x` | `3.0.x` | ✅ Compatible | Allow connection |
| `3.1.x` | `3.0.x` | ⚠️ Warning | Allow with warning |
| `3.0.x` | `3.1.x` | ⚠️ Warning | Allow with warning |
| `4.0.x` | `3.x.x` | ❌ Blocked | Reject connection |
| `2.x.x` | `3.x.x` | ❌ Blocked | Reject connection |
## 🤝 Протокол handshake с проверкой версий
### Расширенный handshake
```python
class VersionAwareHandshake:
"""Handshake с проверкой версий"""
async def perform_handshake(self, peer_connection: PeerConnection) -> bool:
"""Выполнение handshake с проверкой совместимости версий"""
# Отправка handshake с информацией о версии
handshake_msg = {
"type": "handshake",
"node_id": self.node_id,
"public_key": self.public_key_hex,
"version": {
"protocol": MY_NETWORK_VERSION,
"node": NODE_VERSION,
"features": self.get_supported_features(),
"capabilities": self.get_node_capabilities()
},
"timestamp": datetime.utcnow().isoformat()
}
# Подписание сообщения
handshake_msg["signature"] = await self.sign_message(handshake_msg)
# Отправка handshake
await peer_connection.send_message(handshake_msg)
# Получение ответа
response = await peer_connection.receive_message(timeout=30)
if response.get("type") != "handshake_response":
logger.error("Invalid handshake response type")
return False
# Проверка версии пира
peer_version = response.get("version", {}).get("protocol")
if not peer_version:
logger.error("Peer did not provide version information")
return False
# Проверка совместимости
compatibility = self.version_checker.check_compatibility(peer_version)
if not compatibility["allow_connection"]:
logger.error(f"Version incompatibility: {compatibility['message']}")
await peer_connection.send_message({
"type": "handshake_error",
"reason": "version_incompatible",
"message": compatibility["message"]
})
return False
# Логирование предупреждений
if compatibility["level"] == "warning":
logger.warning(f"Version compatibility warning: {compatibility['message']}")
# Отправка предупреждения пиру
await peer_connection.send_message({
"type": "version_warning",
"message": compatibility["message"],
"recommendation": compatibility.get("recommendation")
})
# Сохранение информации о версии пира
peer_connection.version_info = response.get("version")
peer_connection.compatibility = compatibility
logger.info(f"Handshake successful with peer {response.get('node_id')} (v{peer_version})")
return True
def get_supported_features(self) -> List[str]:
"""Получение списка поддерживаемых функций"""
return [
"content_sync",
"p2p_discovery",
"content_filtering",
"version_negotiation",
"encrypted_transport"
]
def get_node_capabilities(self) -> dict:
"""Получение возможностей ноды"""
return {
"max_peers": self.config.max_connections,
"storage_capacity": self.get_available_storage(),
"node_type": self.config.node_type,
"content_types": self.get_supported_content_types()
}
```
## 📋 Версионная информация ноды
### API эндпоинт для версии
```python
@app.route('/api/v1/system/version', methods=['GET'])
async def get_version_info(request):
"""Получение информации о версии ноды"""
return response.json({
"version": {
"protocol": MY_NETWORK_VERSION,
"node": NODE_VERSION,
"api": API_VERSION,
"build": BUILD_VERSION,
"build_date": BUILD_DATE
},
"compatibility": {
"min_protocol_version": "3.0.0",
"max_protocol_version": "3.9.9",
"deprecated_versions": ["2.x.x"],
"supported_features": [
"content_sync",
"p2p_discovery",
"content_filtering",
"version_negotiation"
]
},
"node_info": {
"node_id": node_service.node_id,
"node_type": node_service.config.node_type,
"uptime": node_service.get_uptime_seconds(),
"capabilities": node_service.get_node_capabilities()
}
})
@app.route('/api/v1/system/compatibility', methods=['POST'])
async def check_version_compatibility(request):
"""Проверка совместимости с другой версией"""
data = request.json
peer_version = data.get("version")
if not peer_version:
return response.json({"error": "Version is required"}, status=400)
try:
compatibility = version_checker.check_compatibility(peer_version)
return response.json(compatibility)
except ValueError as e:
return response.json({"error": str(e)}, status=400)
```
## 🔄 Автоматическое обновление
### Проверка обновлений
```python
class UpdateChecker:
"""Система проверки и уведомления об обновлениях"""
def __init__(self, current_version: str, update_url: str):
self.current_version = current_version
self.update_url = update_url
self.last_check = None
async def check_for_updates(self) -> dict:
"""Проверка доступных обновлений"""
try:
# Запрос информации о последней версии
async with aiohttp.ClientSession() as session:
async with session.get(f"{self.update_url}/latest-version") as response:
if response.status == 200:
data = await response.json()
latest_version = data.get("version")
if latest_version:
update_info = self._compare_versions(latest_version)
self.last_check = datetime.utcnow()
return update_info
except Exception as e:
logger.error(f"Failed to check for updates: {e}")
return {"update_available": False, "error": "Failed to check updates"}
def _compare_versions(self, latest_version: str) -> dict:
"""Сравнение текущей и последней версии"""
current = self._parse_version(self.current_version)
latest = self._parse_version(latest_version)
if latest["major"] > current["major"]:
return {
"update_available": True,
"update_type": "major",
"current_version": self.current_version,
"latest_version": latest_version,
"critical": True,
"message": "Major update available with breaking changes"
}
elif latest["minor"] > current["minor"]:
return {
"update_available": True,
"update_type": "minor",
"current_version": self.current_version,
"latest_version": latest_version,
"critical": False,
"message": "Minor update available with new features"
}
elif latest["patch"] > current["patch"]:
return {
"update_available": True,
"update_type": "patch",
"current_version": self.current_version,
"latest_version": latest_version,
"critical": False,
"message": "Patch update available with bug fixes"
}
return {
"update_available": False,
"current_version": self.current_version,
"latest_version": latest_version,
"message": "You are running the latest version"
}
async def get_update_recommendations(self) -> List[str]:
"""Получение рекомендаций по обновлению"""
update_info = await self.check_for_updates()
if not update_info.get("update_available"):
return ["Your node is up to date"]
recommendations = []
if update_info.get("critical"):
recommendations.append("🔴 Critical update available - update immediately for compatibility")
if update_info.get("update_type") == "major":
recommendations.append("⚠️ Major version update - review breaking changes before updating")
recommendations.append("📋 Backup your data before major version updates")
elif update_info.get("update_type") == "minor":
recommendations.append("✨ New features available in minor update")
recommendations.append("🔄 Safe to update - backwards compatible")
elif update_info.get("update_type") == "patch":
recommendations.append("🐛 Bug fixes available - recommended to update")
return recommendations
```
## 📊 Мониторинг версий в сети
### Статистика версий
```python
class NetworkVersionMonitor:
"""Мониторинг версий нод в сети"""
def __init__(self):
self.peer_versions = {}
async def track_peer_version(self, peer_id: str, version_info: dict):
"""Отслеживание версии пира"""
self.peer_versions[peer_id] = {
"version": version_info,
"last_seen": datetime.utcnow(),
"compatible": True
}
async def get_network_version_stats(self) -> dict:
"""Статистика версий в сети"""
if not self.peer_versions:
return {"total_peers": 0, "version_distribution": {}}
version_counts = {}
total_peers = len(self.peer_versions)
compatible_peers = 0
for peer_id, peer_data in self.peer_versions.items():
version = peer_data["version"]["protocol"]
version_counts[version] = version_counts.get(version, 0) + 1
if peer_data["compatible"]:
compatible_peers += 1
return {
"total_peers": total_peers,
"compatible_peers": compatible_peers,
"compatibility_rate": compatible_peers / total_peers if total_peers > 0 else 0,
"version_distribution": version_counts,
"most_common_version": max(version_counts.items(), key=lambda x: x[1])[0] if version_counts else None
}
async def get_outdated_peers(self) -> List[dict]:
"""Список пиров с устаревшими версиями"""
current_major = self._parse_version(MY_NETWORK_VERSION)["major"]
outdated = []
for peer_id, peer_data in self.peer_versions.items():
peer_version = peer_data["version"]["protocol"]
peer_major = self._parse_version(peer_version)["major"]
if peer_major < current_major:
outdated.append({
"peer_id": peer_id,
"version": peer_version,
"age": (datetime.utcnow() - peer_data["last_seen"]).total_seconds()
})
return sorted(outdated, key=lambda x: x["age"], reverse=True)
```
## 🚨 Предупреждения и уведомления
### Система уведомлений о версиях
```python
class VersionNotificationManager:
"""Управление уведомлениями о версиях"""
async def notify_version_mismatch(self, peer_id: str, peer_version: str, compatibility: dict):
"""Уведомление о несоответствии версий"""
if compatibility["level"] == "blocked":
logger.error(f"🚫 Connection blocked: Peer {peer_id} version {peer_version} incompatible")
# Можно добавить отправку в мониторинг
await self._send_to_monitoring({
"type": "version_incompatibility",
"peer_id": peer_id,
"peer_version": peer_version,
"severity": "critical"
})
elif compatibility["level"] == "warning":
logger.warning(f"⚠️ Version warning: Peer {peer_id} version {peer_version} - {compatibility['message']}")
await self._send_to_monitoring({
"type": "version_warning",
"peer_id": peer_id,
"peer_version": peer_version,
"severity": "warning",
"recommendation": compatibility.get("recommendation")
})
async def notify_update_available(self, update_info: dict):
"""Уведомление о доступном обновлении"""
if update_info.get("critical"):
logger.error(f"🔴 Critical update available: {update_info['latest_version']}")
else:
logger.info(f"✨ Update available: {update_info['latest_version']}")
# Отправка в систему мониторинга
await self._send_to_monitoring({
"type": "update_available",
"current_version": update_info["current_version"],
"latest_version": update_info["latest_version"],
"update_type": update_info["update_type"],
"critical": update_info.get("critical", False)
})
```
## 📖 API эндпоинты для версионирования
```python
# Информация о версии
GET /api/v1/system/version # Полная информация о версии
GET /api/v1/system/compatibility # Информация о совместимости
POST /api/v1/system/compatibility/check # Проверка совместимости с версией
# Обновления
GET /api/v1/system/updates/check # Проверка доступных обновлений
GET /api/v1/system/updates/recommendations # Рекомендации по обновлению
# Статистика сети
GET /api/v1/network/versions # Статистика версий в сети
GET /api/v1/network/compatibility/stats # Статистика совместимости
```
Эта система обеспечивает надежную работу сети при наличии нод с разными версиями, предотвращая проблемы совместимости и обеспечивая плавные обновления.

1563
start.sh Executable file

File diff suppressed because it is too large Load Diff