REFACTORING DIRECTORIES
This commit is contained in:
1 parent
c645019380
commit
c8e1d5046c
54 files changed
+6193
-747
No files matched your search
@@ -0,0 +1,772 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Bootstrap Node - Автоматическое развертывание
|
||||
# Домен: my-public-node-3.projscale.dev
|
||||
# Сервер: 2.58.65.188
|
||||
# Единственная команда для полного развертывания
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network Bootstrap Node - Автоматическое развертывание"
|
||||
echo "=========================================================="
|
||||
echo "Домен: my-public-node-3.projscale.dev"
|
||||
echo "Режим: Bootstrap Node (Primary)"
|
||||
echo "Порты: Только 443 (HTTPS)"
|
||||
echo ""
|
||||
|
||||
# Проверка прав root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Запустите от root: sudo bash auto_deploy.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOMAIN="my-public-node-3.projscale.dev"
|
||||
EMAIL="admin@projscale.dev"
|
||||
PROJECT_DIR="/opt/my-network"
|
||||
|
||||
echo "📋 Конфигурация:"
|
||||
echo " Домен: $DOMAIN"
|
||||
echo " Директория: $PROJECT_DIR"
|
||||
echo " Внешний порт: 443 только"
|
||||
echo ""
|
||||
|
||||
# Обновление системы
|
||||
echo "🔄 Обновление системы..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Установка базовых пакетов
|
||||
echo "📦 Установка пакетов..."
|
||||
apt install -y \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
nginx \
|
||||
certbot \
|
||||
python3-certbot-nginx \
|
||||
ufw \
|
||||
fail2ban \
|
||||
htop \
|
||||
tree \
|
||||
nano \
|
||||
jq \
|
||||
net-tools \
|
||||
software-properties-common
|
||||
|
||||
# Настройка Docker
|
||||
echo "🐳 Настройка Docker..."
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
# Создание структуры проекта
|
||||
echo "📁 Создание структуры..."
|
||||
mkdir -p $PROJECT_DIR
|
||||
mkdir -p /opt/storage /opt/logs
|
||||
chmod 755 /opt/storage /opt/logs
|
||||
|
||||
cd $PROJECT_DIR
|
||||
|
||||
# Создание структуры проекта MY Network
|
||||
echo "🏗️ Создание структуры MY Network проекта..."
|
||||
mkdir -p my-uploader-bot/{app,static,templates}
|
||||
mkdir -p my-uploader-bot/app/{core,api}
|
||||
mkdir -p my-uploader-bot/app/core/{my_network,models,background}
|
||||
mkdir -p my-uploader-bot/app/api/routes
|
||||
mkdir -p my-uploader-bot/app/templates
|
||||
|
||||
cd my-uploader-bot
|
||||
|
||||
# Создание requirements.txt
|
||||
echo "📝 Создание requirements.txt..."
|
||||
cat > requirements_new.txt << 'EOF'
|
||||
fastapi==0.104.1
|
||||
sanic==23.6.0
|
||||
uvicorn==0.24.0
|
||||
pydantic==2.5.0
|
||||
sqlalchemy==2.0.23
|
||||
alembic==1.12.1
|
||||
pymysql==1.1.0
|
||||
cryptography==41.0.7
|
||||
redis==5.0.1
|
||||
requests==2.31.0
|
||||
aiohttp==3.9.1
|
||||
websockets==12.0
|
||||
python-multipart==0.0.6
|
||||
python-jose[cryptography]==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
jinja2==3.1.2
|
||||
python-dotenv==1.0.0
|
||||
aiomysql==0.2.0
|
||||
aioredis==2.0.1
|
||||
httpx==0.25.2
|
||||
schedule==1.2.0
|
||||
psutil==5.9.6
|
||||
netifaces==0.11.0
|
||||
matplotlib==3.8.2
|
||||
numpy==1.25.2
|
||||
pillow==10.1.0
|
||||
qrcode==7.4.2
|
||||
prometheus-client==0.19.0
|
||||
EOF
|
||||
|
||||
# Создание основной конфигурации
|
||||
echo "⚙️ Создание конфигурации..."
|
||||
cat > .env << EOF
|
||||
# MY Network Bootstrap Node Configuration
|
||||
NODE_ID=bootstrap-$(date +%s)
|
||||
NODE_TYPE=bootstrap
|
||||
NODE_PORT=15100
|
||||
DOMAIN=$DOMAIN
|
||||
EMAIL=$EMAIL
|
||||
|
||||
# Bootstrap Settings
|
||||
IS_BOOTSTRAP=true
|
||||
BOOTSTRAP_NODES=[]
|
||||
MAX_PEERS=100
|
||||
SYNC_INTERVAL=300
|
||||
PUBLIC_NODE=true
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=my_network_bootstrap
|
||||
DB_USER=my_network_user
|
||||
DB_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Security
|
||||
SECRET_KEY=$(openssl rand -base64 64)
|
||||
JWT_SECRET=$(openssl rand -base64 32)
|
||||
|
||||
# Paths
|
||||
STORAGE_PATH=/opt/storage
|
||||
LOG_PATH=/opt/logs
|
||||
|
||||
# API Settings
|
||||
API_RATE_LIMIT=100
|
||||
MONITOR_RATE_LIMIT=10
|
||||
ENABLE_PUBLIC_API=true
|
||||
ENABLE_CORS=true
|
||||
EOF
|
||||
|
||||
# Создание bootstrap.json
|
||||
echo "🌐 Создание bootstrap.json..."
|
||||
cat > bootstrap.json << EOF
|
||||
{
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": "bootstrap-primary",
|
||||
"host": "$DOMAIN",
|
||||
"port": 443,
|
||||
"ssl": true,
|
||||
"public": true,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "sync", "monitor", "storage"],
|
||||
"last_seen": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"sync_interval": 300,
|
||||
"max_peers": 100,
|
||||
"chunk_size": 1048576,
|
||||
"compression": true,
|
||||
"encryption": true,
|
||||
"heartbeat_interval": 60
|
||||
},
|
||||
"api_endpoints": {
|
||||
"base_url": "https://$DOMAIN",
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"peers": "/api/my/node/peers",
|
||||
"sync": "/api/my/sync/status",
|
||||
"monitor": "/api/my/monitor/",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Создание Docker Compose конфигурации
|
||||
echo "🐳 Создание Docker Compose..."
|
||||
cat > docker-compose.new.yml << EOF
|
||||
version: '3.8'
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:11.2
|
||||
container_name: my_network_db
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: \${DB_PASSWORD}
|
||||
MYSQL_DATABASE: \${DB_NAME}
|
||||
MYSQL_USER: \${DB_USER}
|
||||
MYSQL_PASSWORD: \${DB_PASSWORD}
|
||||
ports:
|
||||
- "127.0.0.1:3306:3306"
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: my_network_redis
|
||||
command: redis-server --requirepass \${REDIS_PASSWORD}
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
redis_data:
|
||||
EOF
|
||||
|
||||
# Создание основного приложения
|
||||
echo "🐍 Создание Python приложения..."
|
||||
|
||||
# app/main.py
|
||||
cat > app/main.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""MY Network Bootstrap Node - Main Application"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# Добавляем путь к приложению
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
try:
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import JSONResponse, HTMLResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
USE_FASTAPI = True
|
||||
except ImportError:
|
||||
try:
|
||||
from sanic import Sanic, response
|
||||
USE_FASTAPI = False
|
||||
except ImportError:
|
||||
print("Neither FastAPI nor Sanic available")
|
||||
sys.exit(1)
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/opt/logs/my-network.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Конфигурация
|
||||
NODE_ID = os.getenv('NODE_ID', f'bootstrap-{int(datetime.now().timestamp())}')
|
||||
DOMAIN = os.getenv('DOMAIN', 'my-public-node-3.projscale.dev')
|
||||
NODE_PORT = int(os.getenv('NODE_PORT', 15100))
|
||||
IS_BOOTSTRAP = os.getenv('IS_BOOTSTRAP', 'true').lower() == 'true'
|
||||
|
||||
if USE_FASTAPI:
|
||||
app = FastAPI(
|
||||
title="MY Network Bootstrap Node",
|
||||
description="MY Network v2.0 Bootstrap Node API",
|
||||
version="2.0.0"
|
||||
)
|
||||
|
||||
# CORS для публичного API
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/api/my/health")
|
||||
async def health_check():
|
||||
return JSONResponse({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "sync", "monitor", "storage"]
|
||||
})
|
||||
|
||||
@app.get("/api/my/node/info")
|
||||
async def node_info():
|
||||
return JSONResponse({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": 443,
|
||||
"ssl": True,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "sync", "monitor", "storage"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
|
||||
@app.get("/api/my/bootstrap/config")
|
||||
async def bootstrap_config():
|
||||
try:
|
||||
with open('bootstrap.json', 'r') as f:
|
||||
config = json.load(f)
|
||||
return JSONResponse(config)
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading bootstrap config: {e}")
|
||||
raise HTTPException(status_code=500, detail="Config not available")
|
||||
|
||||
@app.get("/api/my/monitor/")
|
||||
async def monitor_dashboard():
|
||||
html_content = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MY Network Bootstrap Monitor</title>
|
||||
<style>
|
||||
body { font-family: monospace; background: #000; color: #0f0; margin: 20px; }
|
||||
h1 { color: #ff0; text-align: center; }
|
||||
.status { margin: 10px 0; padding: 10px; border: 1px solid #0f0; }
|
||||
.healthy { border-color: #0f0; }
|
||||
.error { border-color: #f00; color: #f00; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚀 MY Network Bootstrap Node</h1>
|
||||
<div class="status healthy">
|
||||
<h3>Node Status: ACTIVE</h3>
|
||||
<p>Node ID: """ + NODE_ID + """</p>
|
||||
<p>Domain: """ + DOMAIN + """</p>
|
||||
<p>Type: Bootstrap Primary</p>
|
||||
<p>Services: API, Sync, Monitor, Storage</p>
|
||||
</div>
|
||||
<div class="status healthy">
|
||||
<h3>Network Status</h3>
|
||||
<p>Protocol Version: 2.0</p>
|
||||
<p>Max Peers: 100</p>
|
||||
<p>Public Access: Enabled</p>
|
||||
<p>SSL: Enabled</p>
|
||||
</div>
|
||||
<script>
|
||||
setInterval(() => location.reload(), 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info(f"Starting MY Network Bootstrap Node on port {NODE_PORT}")
|
||||
uvicorn.run(app, host="0.0.0.0", port=NODE_PORT)
|
||||
|
||||
else:
|
||||
# Sanic версия
|
||||
app = Sanic("MY_Network_Bootstrap")
|
||||
|
||||
@app.route("/api/my/health")
|
||||
async def health_check(request):
|
||||
return response.json({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "sync", "monitor", "storage"]
|
||||
})
|
||||
|
||||
@app.route("/api/my/node/info")
|
||||
async def node_info(request):
|
||||
return response.json({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": 443,
|
||||
"ssl": True,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "sync", "monitor", "storage"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info(f"Starting MY Network Bootstrap Node (Sanic) on port {NODE_PORT}")
|
||||
app.run(host="0.0.0.0", port=NODE_PORT)
|
||||
EOF
|
||||
|
||||
# Создание systemd сервиса
|
||||
echo "⚙️ Создание systemd сервиса..."
|
||||
cat > /etc/systemd/system/my-network-bootstrap.service << EOF
|
||||
[Unit]
|
||||
Description=MY Network Bootstrap Node
|
||||
After=network.target docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=$PROJECT_DIR/my-uploader-bot
|
||||
Environment=PATH=$PROJECT_DIR/my-uploader-bot/venv/bin
|
||||
ExecStart=$PROJECT_DIR/my-uploader-bot/venv/bin/python app/main.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Установка Python зависимостей
|
||||
echo "🐍 Установка Python зависимостей..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements_new.txt
|
||||
|
||||
# Запуск Docker сервисов
|
||||
echo "🐳 Запуск Docker сервисов..."
|
||||
docker-compose -f docker-compose.new.yml up -d
|
||||
|
||||
# Ожидание БД
|
||||
echo "⏳ Ожидание запуска базы данных..."
|
||||
sleep 30
|
||||
|
||||
# Настройка Nginx с Cloudflare
|
||||
echo "🌐 Настройка Nginx..."
|
||||
cat > /etc/nginx/sites-available/my-network-bootstrap << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# SSL будет настроен certbot'ом
|
||||
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
||||
|
||||
# Cloudflare IP ranges
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
add_header X-MY-Network-Node-Type "bootstrap";
|
||||
add_header X-MY-Network-Version "2.0";
|
||||
|
||||
server_tokens off;
|
||||
|
||||
# Rate limiting
|
||||
limit_req_zone \$binary_remote_addr zone=api:10m rate=100r/s;
|
||||
limit_req_zone \$binary_remote_addr zone=monitor:10m rate=10r/s;
|
||||
|
||||
# Bootstrap API endpoints (публичные)
|
||||
location /api/my/ {
|
||||
limit_req zone=api burst=200 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# CORS headers
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
if (\$request_method = 'OPTIONS') {
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
|
||||
# Monitor (ограниченный доступ)
|
||||
location /api/my/monitor {
|
||||
limit_req zone=monitor burst=10 nodelay;
|
||||
|
||||
allow 127.0.0.1;
|
||||
allow ::1;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Redirect root to health
|
||||
location = / {
|
||||
return 301 /api/my/health;
|
||||
}
|
||||
|
||||
# Block sensitive paths
|
||||
location ~ /\.(?!well-known) {
|
||||
deny all;
|
||||
}
|
||||
location ~ ^/(config|\.env|requirements|docker-compose) {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Активация сайта
|
||||
ln -sf /etc/nginx/sites-available/my-network-bootstrap /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Проверка конфигурации nginx
|
||||
nginx -t
|
||||
|
||||
# Запуск приложения
|
||||
echo "🚀 Запуск MY Network сервиса..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable my-network-bootstrap
|
||||
systemctl start my-network-bootstrap
|
||||
|
||||
# Ожидание запуска приложения
|
||||
echo "⏳ Ожидание запуска приложения..."
|
||||
sleep 10
|
||||
|
||||
# Получение SSL сертификата
|
||||
echo "🔐 Получение SSL сертификата..."
|
||||
certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect
|
||||
|
||||
# Настройка firewall (ТОЛЬКО 443!)
|
||||
echo "🔥 Настройка firewall - ТОЛЬКО порт 443..."
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# SSH (определяем текущий порт)
|
||||
SSH_PORT=$(ss -tlnp | grep sshd | grep -o ':[0-9]*' | head -1 | cut -d: -f2)
|
||||
if [[ -n "$SSH_PORT" ]]; then
|
||||
echo "🔑 Разрешение SSH на порту $SSH_PORT"
|
||||
ufw allow $SSH_PORT/tcp comment 'SSH Access'
|
||||
fi
|
||||
|
||||
# HTTP и HTTPS для Cloudflare
|
||||
ufw allow 80/tcp comment 'HTTP Redirect'
|
||||
ufw allow 443/tcp comment 'HTTPS Only'
|
||||
|
||||
# Блокируем прямой доступ к приложению
|
||||
ufw deny 15100 comment 'Block direct app access'
|
||||
ufw deny 3306 comment 'Block MySQL access'
|
||||
ufw deny 6379 comment 'Block Redis access'
|
||||
|
||||
ufw --force enable
|
||||
|
||||
# Настройка fail2ban
|
||||
echo "🚫 Настройка fail2ban..."
|
||||
cat > /etc/fail2ban/jail.local << EOF
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = $SSH_PORT
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
filter = nginx-http-auth
|
||||
logpath = /var/log/nginx/error.log
|
||||
|
||||
[nginx-limit-req]
|
||||
enabled = true
|
||||
filter = nginx-limit-req
|
||||
logpath = /var/log/nginx/error.log
|
||||
maxretry = 10
|
||||
EOF
|
||||
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Запуск nginx
|
||||
systemctl enable nginx
|
||||
systemctl start nginx
|
||||
|
||||
# Настройка мониторинга
|
||||
echo "📊 Настройка мониторинга..."
|
||||
cat > /opt/bootstrap-monitor.sh << 'MONITOR_EOF'
|
||||
#!/bin/bash
|
||||
LOG_FILE="/opt/logs/bootstrap-monitor.log"
|
||||
DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Check services
|
||||
BOOTSTRAP_STATUS=$(systemctl is-active my-network-bootstrap)
|
||||
NGINX_STATUS=$(systemctl is-active nginx)
|
||||
DOCKER_STATUS=$(systemctl is-active docker)
|
||||
|
||||
# Check API
|
||||
API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://my-public-node-3.projscale.dev/api/my/health 2>/dev/null || echo "FAIL")
|
||||
|
||||
# System stats
|
||||
DISK_USAGE=$(df -h /opt | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
MEM_USAGE=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
|
||||
CPU_LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
|
||||
|
||||
# Log status
|
||||
echo "[$DATE] Bootstrap: $BOOTSTRAP_STATUS, Nginx: $NGINX_STATUS, Docker: $DOCKER_STATUS, API: $API_STATUS, Disk: ${DISK_USAGE}%, Mem: ${MEM_USAGE}%, Load: $CPU_LOAD" >> $LOG_FILE
|
||||
|
||||
# Alert if critical
|
||||
if [[ "$BOOTSTRAP_STATUS" != "active" || "$NGINX_STATUS" != "active" || "$DOCKER_STATUS" != "active" || "$API_STATUS" != "200" ]]; then
|
||||
echo "[$DATE] ALERT: Bootstrap node critical issue detected!" >> $LOG_FILE
|
||||
fi
|
||||
MONITOR_EOF
|
||||
|
||||
chmod +x /opt/bootstrap-monitor.sh
|
||||
|
||||
# Добавление в cron
|
||||
(crontab -l 2>/dev/null; echo "*/2 * * * * /opt/bootstrap-monitor.sh") | crontab -
|
||||
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
|
||||
|
||||
# Финальная проверка
|
||||
echo "🔍 Финальная проверка системы..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "📊 Статус сервисов:"
|
||||
systemctl status my-network-bootstrap --no-pager -l | head -10
|
||||
echo ""
|
||||
systemctl status nginx --no-pager -l | head -10
|
||||
echo ""
|
||||
|
||||
echo "🔥 Firewall статус:"
|
||||
ufw status numbered
|
||||
|
||||
echo ""
|
||||
echo "🌐 Проверка API:"
|
||||
curl -s -I https://$DOMAIN/api/my/health || echo "API недоступен"
|
||||
|
||||
echo ""
|
||||
echo "🔐 SSL сертификаты:"
|
||||
certbot certificates
|
||||
|
||||
echo ""
|
||||
echo "🌍 Открытые порты:"
|
||||
netstat -tlnp | grep LISTEN
|
||||
|
||||
# Создание итогового отчета
|
||||
cat > /opt/bootstrap-deployment-report.txt << EOF
|
||||
MY Network Bootstrap Node - Отчет о развертывании
|
||||
===============================================
|
||||
Время развертывания: $(date)
|
||||
Домен: $DOMAIN
|
||||
Сервер: $(hostname -I | awk '{print $1}')
|
||||
|
||||
✅ УСПЕШНО РАЗВЕРНУТО:
|
||||
- MY Network Bootstrap Node (версия 2.0)
|
||||
- Nginx proxy с SSL (порт 443 только)
|
||||
- MariaDB и Redis в Docker
|
||||
- UFW Firewall (SSH + 443 только)
|
||||
- Fail2ban защита
|
||||
- Автоматический мониторинг
|
||||
- SSL сертификат Let's Encrypt
|
||||
|
||||
🌐 ДОСТУПНЫЕ ENDPOINTS:
|
||||
- Health Check: https://$DOMAIN/api/my/health
|
||||
- Node Info: https://$DOMAIN/api/my/node/info
|
||||
- Bootstrap Config: https://$DOMAIN/api/my/bootstrap/config
|
||||
- Monitor Dashboard: https://$DOMAIN/api/my/monitor/ (localhost only)
|
||||
|
||||
🔧 УПРАВЛЕНИЕ:
|
||||
- Статус: systemctl status my-network-bootstrap nginx docker
|
||||
- Логи: journalctl -u my-network-bootstrap -f
|
||||
- Монитор: tail -f /opt/logs/bootstrap-monitor.log
|
||||
- Firewall: ufw status
|
||||
- SSL: certbot certificates
|
||||
|
||||
🔒 БЕЗОПАСНОСТЬ:
|
||||
- Открыт только порт 443 (HTTPS)
|
||||
- Все внутренние сервисы заблокированы
|
||||
- SSL шифрование обязательно
|
||||
- Rate limiting активен
|
||||
- Fail2ban защита от атак
|
||||
|
||||
📂 ФАЙЛЫ КОНФИГУРАЦИИ:
|
||||
- $PROJECT_DIR/my-uploader-bot/.env
|
||||
- $PROJECT_DIR/my-uploader-bot/bootstrap.json
|
||||
- /etc/nginx/sites-available/my-network-bootstrap
|
||||
- /etc/systemd/system/my-network-bootstrap.service
|
||||
|
||||
Bootstrap узел готов к работе как основной узел сети!
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node развернут успешно!"
|
||||
echo "=============================================="
|
||||
echo "🌐 Домен: https://$DOMAIN"
|
||||
echo "🔐 SSL: Активен и настроен"
|
||||
echo "🔥 Firewall: Только порт 443 открыт"
|
||||
echo "🚀 Статус: Bootstrap узел активен"
|
||||
echo "📊 Мониторинг: Каждые 2 минуты"
|
||||
echo ""
|
||||
echo "🔍 Проверьте работу:"
|
||||
echo " curl https://$DOMAIN/api/my/health"
|
||||
echo " curl https://$DOMAIN/api/my/node/info"
|
||||
echo " curl https://$DOMAIN/api/my/bootstrap/config"
|
||||
echo ""
|
||||
echo "📄 Полный отчет: /opt/bootstrap-deployment-report.txt"
|
||||
echo ""
|
||||
echo "🎯 Bootstrap узел готов принимать подключения других узлов!"
|
||||
|
||||
# Конец скрипта
|
||||
exit 0
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/expect -f
|
||||
|
||||
set timeout 1800
|
||||
set password "DMUEjmnh6mDs/qlzhpjDzQ"
|
||||
set server "2.58.65.188"
|
||||
|
||||
log_user 1
|
||||
|
||||
spawn ssh -o StrictHostKeyChecking=no service@$server
|
||||
|
||||
expect {
|
||||
"password:" {
|
||||
send "$password\r"
|
||||
exp_continue
|
||||
}
|
||||
"$ " {
|
||||
# Переход в root
|
||||
send "sudo su -\r"
|
||||
expect {
|
||||
"password:" {
|
||||
send "$password\r"
|
||||
expect "# "
|
||||
}
|
||||
"# " {
|
||||
# Уже root
|
||||
}
|
||||
}
|
||||
|
||||
# Проверка и запуск скрипта
|
||||
send "ls -la /tmp/auto_deploy.sh\r"
|
||||
expect "# "
|
||||
|
||||
send "chmod +x /tmp/auto_deploy.sh\r"
|
||||
expect "# "
|
||||
|
||||
send "echo 'Starting MY Network Bootstrap deployment...'\r"
|
||||
expect "# "
|
||||
|
||||
# Запуск скрипта развертывания
|
||||
send "/tmp/auto_deploy.sh\r"
|
||||
|
||||
# Ожидаем завершения установки (до 30 минут)
|
||||
expect {
|
||||
"Bootstrap узел готов к работе!" {
|
||||
send "echo 'Deployment completed successfully!'\r"
|
||||
expect "# "
|
||||
|
||||
# Проверка результата
|
||||
send "systemctl status my-network-bootstrap --no-pager\r"
|
||||
expect "# "
|
||||
|
||||
send "curl -s https://my-public-node-3.projscale.dev/api/my/health || echo 'API check failed'\r"
|
||||
expect "# "
|
||||
|
||||
send "ufw status numbered\r"
|
||||
expect "# "
|
||||
|
||||
send "echo 'MY Network Bootstrap Node deployed successfully!'\r"
|
||||
expect "# "
|
||||
|
||||
interact
|
||||
}
|
||||
"error" {
|
||||
send "echo 'Deployment error occurred'\r"
|
||||
interact
|
||||
}
|
||||
timeout {
|
||||
send "echo 'Deployment timeout - checking status...'\r"
|
||||
send "systemctl status my-network-bootstrap nginx docker\r"
|
||||
interact
|
||||
}
|
||||
}
|
||||
}
|
||||
timeout {
|
||||
puts "Connection timeout"
|
||||
exit 1
|
||||
}
|
||||
eof {
|
||||
puts "Connection closed"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Bootstrap Node Deployment Script
|
||||
# Домен: my-public-node-3.projscale.dev
|
||||
# Сервер: 2.58.65.188
|
||||
# Только порт 443 наружу через nginx proxy
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network Bootstrap Node Deployment"
|
||||
echo "======================================="
|
||||
echo "Домен: my-public-node-3.projscale.dev"
|
||||
echo "Сервер: 2.58.65.188"
|
||||
echo "Режим: Bootstrap Node (Primary)"
|
||||
echo ""
|
||||
|
||||
# Проверка прав root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Этот скрипт должен запускаться от root"
|
||||
echo "Используйте: sudo bash deploy_bootstrap_node.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOMAIN="my-public-node-3.projscale.dev"
|
||||
EMAIL="admin@projscale.dev"
|
||||
NODE_TYPE="bootstrap"
|
||||
|
||||
echo "📋 Конфигурация Bootstrap узла:"
|
||||
echo " Домен: $DOMAIN"
|
||||
echo " Email: $EMAIL"
|
||||
echo " Тип: $NODE_TYPE"
|
||||
echo " Внешний порт: 443 (HTTPS только)"
|
||||
echo " Внутренний порт: 15100"
|
||||
echo ""
|
||||
|
||||
# Обновление системы
|
||||
echo "🔄 Обновление системы..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Установка пакетов
|
||||
echo "📦 Установка необходимых пакетов..."
|
||||
apt install -y \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
nginx \
|
||||
certbot \
|
||||
python3-certbot-nginx \
|
||||
ufw \
|
||||
fail2ban \
|
||||
htop \
|
||||
tree \
|
||||
nano \
|
||||
jq
|
||||
|
||||
# Настройка Docker
|
||||
echo "🐳 Настройка Docker..."
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
usermod -aG docker service
|
||||
|
||||
# Создание структуры проекта
|
||||
echo "📁 Создание структуры проекта..."
|
||||
PROJECT_DIR="/opt/my-network"
|
||||
mkdir -p $PROJECT_DIR
|
||||
cd $PROJECT_DIR
|
||||
|
||||
# Создание директорий
|
||||
mkdir -p /opt/storage /opt/logs
|
||||
chmod 755 /opt/storage /opt/logs
|
||||
chown service:service /opt/storage /opt/logs
|
||||
|
||||
# Клонирование проекта (тут будет запрос git pull)
|
||||
echo "📥 Настройка проекта..."
|
||||
echo "ТРЕБУЕТСЯ: Выполните команду git clone или скопируйте проект в $PROJECT_DIR/my-uploader-bot/"
|
||||
echo "После этого продолжите выполнение скрипта"
|
||||
read -p "Нажмите Enter когда проект будет готов..."
|
||||
|
||||
cd my-uploader-bot
|
||||
|
||||
# Создание конфигурации
|
||||
echo "📝 Создание конфигурации bootstrap узла..."
|
||||
cat > .env << EOF
|
||||
# MY Network Bootstrap Node Configuration
|
||||
NODE_ID=bootstrap-node-$(date +%s)
|
||||
NODE_TYPE=bootstrap
|
||||
NODE_PORT=15100
|
||||
DOMAIN=$DOMAIN
|
||||
EMAIL=$EMAIL
|
||||
|
||||
# Network Configuration
|
||||
IS_BOOTSTRAP=true
|
||||
BOOTSTRAP_NODES=[]
|
||||
MAX_PEERS=50
|
||||
SYNC_INTERVAL=300
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=my_network_bootstrap
|
||||
DB_USER=my_network_user
|
||||
DB_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Security
|
||||
SECRET_KEY=$(openssl rand -base64 64)
|
||||
JWT_SECRET=$(openssl rand -base64 32)
|
||||
|
||||
# Storage
|
||||
STORAGE_PATH=/opt/storage
|
||||
LOG_PATH=/opt/logs
|
||||
|
||||
# API Settings
|
||||
API_RATE_LIMIT=100
|
||||
MONITOR_RATE_LIMIT=10
|
||||
ENABLE_PUBLIC_API=true
|
||||
EOF
|
||||
|
||||
# Создание bootstrap.json конфигурации
|
||||
echo "🌐 Создание bootstrap конфигурации..."
|
||||
cat > bootstrap.json << EOF
|
||||
{
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": "bootstrap-node-primary",
|
||||
"host": "$DOMAIN",
|
||||
"port": 443,
|
||||
"ssl": true,
|
||||
"public": true,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "sync", "monitor", "storage"]
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"sync_interval": 300,
|
||||
"max_peers": 50,
|
||||
"chunk_size": 1048576,
|
||||
"compression": true,
|
||||
"encryption": true
|
||||
},
|
||||
"api_endpoints": {
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"peers": "/api/my/node/peers",
|
||||
"sync": "/api/my/sync/status",
|
||||
"monitor": "/api/my/monitor/",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Установка Python зависимостей
|
||||
echo "🐍 Установка Python зависимостей..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements_new.txt
|
||||
|
||||
# Настройка базы данных
|
||||
echo "🗄️ Запуск Docker сервисов..."
|
||||
docker-compose -f docker-compose.new.yml up -d
|
||||
|
||||
# Ожидание запуска БД
|
||||
echo "⏳ Ожидание запуска базы данных..."
|
||||
sleep 30
|
||||
|
||||
# Создание systemd сервиса
|
||||
echo "⚙️ Создание systemd сервиса..."
|
||||
cat > /etc/systemd/system/my-network-bootstrap.service << EOF
|
||||
[Unit]
|
||||
Description=MY Network Bootstrap Node
|
||||
After=network.target docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=service
|
||||
Group=service
|
||||
WorkingDirectory=$PROJECT_DIR/my-uploader-bot
|
||||
Environment=PATH=$PROJECT_DIR/my-uploader-bot/venv/bin
|
||||
ExecStart=$PROJECT_DIR/my-uploader-bot/venv/bin/python app/main.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Настройка Nginx для Cloudflare
|
||||
echo "🌐 Настройка Nginx для Cloudflare..."
|
||||
cat > /etc/nginx/sites-available/my-network-bootstrap << EOF
|
||||
# MY Network Bootstrap Node - Cloudflare Compatible
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# SSL Configuration для Cloudflare
|
||||
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
||||
|
||||
# Cloudflare IP ranges
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
|
||||
# Bootstrap Node specific headers
|
||||
add_header X-MY-Network-Node-Type "bootstrap";
|
||||
add_header X-MY-Network-Version "2.0";
|
||||
|
||||
server_tokens off;
|
||||
|
||||
# Rate limiting для bootstrap узла
|
||||
limit_req_zone \$binary_remote_addr zone=bootstrap_api:10m rate=100r/s;
|
||||
limit_req_zone \$binary_remote_addr zone=bootstrap_monitor:10m rate=10r/s;
|
||||
limit_req_zone \$binary_remote_addr zone=bootstrap_sync:10m rate=50r/s;
|
||||
|
||||
# Bootstrap configuration endpoint (публичный)
|
||||
location /api/my/bootstrap/ {
|
||||
limit_req zone=bootstrap_api burst=50 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-MY-Network-Bootstrap "true";
|
||||
|
||||
# CORS для bootstrap API
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
}
|
||||
|
||||
# Health check endpoint (публичный)
|
||||
location /api/my/health {
|
||||
limit_req zone=bootstrap_api burst=20 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
}
|
||||
|
||||
# Node info (публичный для discovery)
|
||||
location /api/my/node/info {
|
||||
limit_req zone=bootstrap_api burst=30 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
}
|
||||
|
||||
# Sync endpoints (для других узлов)
|
||||
location /api/my/sync/ {
|
||||
limit_req zone=bootstrap_sync burst=100 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# Увеличенные таймауты для синхронизации
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# Main API
|
||||
location /api/my/ {
|
||||
limit_req zone=bootstrap_api burst=200 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Monitoring interface (защищенный)
|
||||
location /api/my/monitor {
|
||||
limit_req zone=bootstrap_monitor burst=10 nodelay;
|
||||
|
||||
# Разрешить только определенным IP (настроить по необходимости)
|
||||
allow 127.0.0.1;
|
||||
allow ::1;
|
||||
# allow YOUR_ADMIN_IP;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Static files
|
||||
location /static/ {
|
||||
alias $PROJECT_DIR/my-uploader-bot/static/;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Блокировка чувствительных путей
|
||||
location ~ /\.(?!well-known) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ ^/(config|\.env|requirements|docker-compose) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Root redirect to monitor
|
||||
location = / {
|
||||
return 301 /api/my/monitor/;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Активация nginx конфигурации
|
||||
ln -sf /etc/nginx/sites-available/my-network-bootstrap /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Проверка nginx конфигурации
|
||||
nginx -t
|
||||
|
||||
# Получение SSL сертификата
|
||||
echo "🔐 Получение SSL сертификата..."
|
||||
certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect
|
||||
|
||||
# Настройка firewall (ТОЛЬКО порт 443!)
|
||||
echo "🔥 Настройка firewall (только порт 443)..."
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# SSH порт (узнаем текущий)
|
||||
SSH_PORT=$(ss -tlnp | grep sshd | grep -o ':[0-9]*' | head -1 | cut -d: -f2)
|
||||
if [[ -n "$SSH_PORT" ]]; then
|
||||
echo "🔑 Разрешение SSH на порту $SSH_PORT..."
|
||||
ufw allow $SSH_PORT/tcp comment 'SSH'
|
||||
fi
|
||||
|
||||
# ТОЛЬКО HTTP и HTTPS для Cloudflare
|
||||
ufw allow 80/tcp comment 'HTTP for Cloudflare'
|
||||
ufw allow 443/tcp comment 'HTTPS for Cloudflare'
|
||||
|
||||
# Блокируем прямой доступ к приложению
|
||||
ufw deny 15100 comment 'Block direct app access'
|
||||
|
||||
ufw --force enable
|
||||
|
||||
# Настройка fail2ban
|
||||
echo "🚫 Настройка fail2ban..."
|
||||
apt install -y fail2ban
|
||||
|
||||
cat > /etc/fail2ban/jail.local << EOF
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = $SSH_PORT
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
filter = nginx-http-auth
|
||||
logpath = /var/log/nginx/error.log
|
||||
|
||||
[nginx-limit-req]
|
||||
enabled = true
|
||||
filter = nginx-limit-req
|
||||
logpath = /var/log/nginx/error.log
|
||||
maxretry = 10
|
||||
EOF
|
||||
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Запуск сервисов
|
||||
echo "🚀 Запуск сервисов..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable my-network-bootstrap
|
||||
systemctl start my-network-bootstrap
|
||||
systemctl enable nginx
|
||||
systemctl start nginx
|
||||
|
||||
# Создание мониторинг скрипта
|
||||
echo "📊 Настройка мониторинга..."
|
||||
cat > /opt/bootstrap-monitor.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
LOG_FILE="/opt/logs/bootstrap-monitor.log"
|
||||
DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Check services
|
||||
BOOTSTRAP_STATUS=$(systemctl is-active my-network-bootstrap)
|
||||
NGINX_STATUS=$(systemctl is-active nginx)
|
||||
DOCKER_STATUS=$(systemctl is-active docker)
|
||||
|
||||
# Check API
|
||||
API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://my-public-node-3.projscale.dev/api/my/health || echo "FAIL")
|
||||
|
||||
# Check disk space
|
||||
DISK_USAGE=$(df -h /opt | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
|
||||
# Log status
|
||||
echo "[$DATE] Bootstrap: $BOOTSTRAP_STATUS, Nginx: $NGINX_STATUS, Docker: $DOCKER_STATUS, API: $API_STATUS, Disk: ${DISK_USAGE}%" >> $LOG_FILE
|
||||
|
||||
# Alert if critical
|
||||
if [[ "$BOOTSTRAP_STATUS" != "active" || "$NGINX_STATUS" != "active" || "$DOCKER_STATUS" != "active" || "$API_STATUS" != "200" ]]; then
|
||||
echo "[$DATE] ALERT: Bootstrap node has critical issues!" >> $LOG_FILE
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /opt/bootstrap-monitor.sh
|
||||
|
||||
# Добавить в cron
|
||||
(crontab -l 2>/dev/null; echo "*/2 * * * * /opt/bootstrap-monitor.sh") | crontab -
|
||||
|
||||
# Настройка автообновления SSL
|
||||
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
|
||||
|
||||
# Финальная проверка
|
||||
echo "🔍 Финальная проверка bootstrap узла..."
|
||||
sleep 10
|
||||
|
||||
echo "📊 Статус сервисов:"
|
||||
systemctl status my-network-bootstrap --no-pager -l
|
||||
systemctl status nginx --no-pager -l
|
||||
systemctl status docker --no-pager -l
|
||||
|
||||
echo "🔥 Статус firewall:"
|
||||
ufw status numbered
|
||||
|
||||
echo "🌐 Проверка API:"
|
||||
curl -s https://$DOMAIN/api/my/health || echo "API недоступен"
|
||||
|
||||
echo "🔐 SSL сертификаты:"
|
||||
certbot certificates
|
||||
|
||||
# Сохранение конфигурации
|
||||
cat > /opt/bootstrap-node-config.txt << EOF
|
||||
MY Network Bootstrap Node - Конфигурация
|
||||
=======================================
|
||||
Домен: $DOMAIN
|
||||
Тип: Bootstrap Node (Primary)
|
||||
Внешний порт: 443 (HTTPS only)
|
||||
Внутренний порт: 15100
|
||||
Cloudflare: Enabled
|
||||
|
||||
Статус сервисов:
|
||||
systemctl status my-network-bootstrap nginx docker fail2ban
|
||||
|
||||
Логи:
|
||||
journalctl -u my-network-bootstrap -f
|
||||
tail -f /opt/logs/bootstrap-monitor.log
|
||||
|
||||
API Endpoints:
|
||||
https://$DOMAIN/api/my/health
|
||||
https://$DOMAIN/api/my/node/info
|
||||
https://$DOMAIN/api/my/bootstrap/config
|
||||
https://$DOMAIN/api/my/monitor/ (restricted)
|
||||
|
||||
Конфигурационные файлы:
|
||||
$PROJECT_DIR/my-uploader-bot/.env
|
||||
$PROJECT_DIR/my-uploader-bot/bootstrap.json
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node развернут!"
|
||||
echo "======================================="
|
||||
echo "🌐 Домен: https://$DOMAIN"
|
||||
echo "🔐 SSL: Активен"
|
||||
echo "🔥 Firewall: Только порт 443"
|
||||
echo "🌍 Cloudflare: Совместимость включена"
|
||||
echo "📊 Мониторинг: Каждые 2 минуты"
|
||||
echo ""
|
||||
echo "🔍 Проверка работы:"
|
||||
echo " curl https://$DOMAIN/api/my/health"
|
||||
echo " curl https://$DOMAIN/api/my/node/info"
|
||||
echo " curl https://$DOMAIN/api/my/bootstrap/config"
|
||||
echo ""
|
||||
echo "📚 Конфигурация: /opt/bootstrap-node-config.txt"
|
||||
echo ""
|
||||
echo "🎯 Bootstrap узел готов к работе!"
|
||||
@@ -0,0 +1,718 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Production Deployment Script
|
||||
# Скрипт для развертывания MY Network с nginx, SSL и полной инфраструктурой
|
||||
|
||||
set -e # Выход при ошибке
|
||||
|
||||
# Цвета для вывода
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ASCII Art заставка
|
||||
print_header() {
|
||||
echo -e "${CYAN}"
|
||||
cat << "EOF"
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ MY NETWORK v2.0 ║
|
||||
║ Production Deployment Script ║
|
||||
║ Distributed Content Protocol Installer ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
}
|
||||
|
||||
# Логирование
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Конфигурационные переменные
|
||||
DOMAIN=${DOMAIN:-"my-network.local"}
|
||||
EMAIL=${EMAIL:-"admin@${DOMAIN}"}
|
||||
HTTP_PORT=${HTTP_PORT:-80}
|
||||
HTTPS_PORT=${HTTPS_PORT:-443}
|
||||
APP_PORT=${APP_PORT:-15100}
|
||||
REDIS_PORT=${REDIS_PORT:-6379}
|
||||
DB_PORT=${DB_PORT:-3306}
|
||||
|
||||
# Проверка прав root
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Проверка операционной системы
|
||||
check_os() {
|
||||
log_step "Checking operating system..."
|
||||
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS=$NAME
|
||||
VER=$VERSION_ID
|
||||
log_info "Detected OS: $OS $VER"
|
||||
else
|
||||
log_error "Cannot detect operating system"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Установка зависимостей
|
||||
install_dependencies() {
|
||||
log_step "Installing system dependencies..."
|
||||
|
||||
if [[ "$OS" == *"Ubuntu"* ]] || [[ "$OS" == *"Debian"* ]]; then
|
||||
apt update
|
||||
apt install -y \
|
||||
nginx \
|
||||
certbot \
|
||||
python3-certbot-nginx \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
htop \
|
||||
ufw \
|
||||
fail2ban
|
||||
|
||||
elif [[ "$OS" == *"CentOS"* ]] || [[ "$OS" == *"Red Hat"* ]]; then
|
||||
yum update -y
|
||||
yum install -y \
|
||||
nginx \
|
||||
certbot \
|
||||
python3-certbot-nginx \
|
||||
docker \
|
||||
docker-compose \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
htop \
|
||||
firewalld
|
||||
|
||||
else
|
||||
log_warn "Unsupported OS, attempting generic installation..."
|
||||
fi
|
||||
|
||||
# Запустить Docker
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Dependencies installed successfully"
|
||||
}
|
||||
|
||||
# Настройка файрвола
|
||||
setup_firewall() {
|
||||
log_step "Configuring firewall..."
|
||||
|
||||
if command -v ufw &> /dev/null; then
|
||||
# Ubuntu/Debian firewall
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# Разрешить SSH
|
||||
ufw allow 22/tcp
|
||||
|
||||
# Разрешить HTTP/HTTPS
|
||||
ufw allow $HTTP_PORT/tcp
|
||||
ufw allow $HTTPS_PORT/tcp
|
||||
|
||||
# Разрешить порт приложения
|
||||
ufw allow $APP_PORT/tcp
|
||||
|
||||
# MY Network P2P порты
|
||||
ufw allow 8000:8010/tcp
|
||||
ufw allow 8000:8010/udp
|
||||
|
||||
ufw --force enable
|
||||
|
||||
elif command -v firewall-cmd &> /dev/null; then
|
||||
# CentOS/RHEL firewall
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
|
||||
firewall-cmd --permanent --add-service=ssh
|
||||
firewall-cmd --permanent --add-service=http
|
||||
firewall-cmd --permanent --add-service=https
|
||||
firewall-cmd --permanent --add-port=$APP_PORT/tcp
|
||||
firewall-cmd --permanent --add-port=8000-8010/tcp
|
||||
firewall-cmd --permanent --add-port=8000-8010/udp
|
||||
|
||||
firewall-cmd --reload
|
||||
fi
|
||||
|
||||
log_info "Firewall configured successfully"
|
||||
}
|
||||
|
||||
# Создание пользователя для приложения
|
||||
create_app_user() {
|
||||
log_step "Creating application user..."
|
||||
|
||||
if ! id "my-network" &>/dev/null; then
|
||||
useradd -r -s /bin/false -d /opt/my-network -m my-network
|
||||
usermod -aG docker my-network
|
||||
log_info "User 'my-network' created"
|
||||
else
|
||||
log_info "User 'my-network' already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
# Настройка директорий
|
||||
setup_directories() {
|
||||
log_step "Setting up directories..."
|
||||
|
||||
# Создать основные директории
|
||||
mkdir -p /opt/my-network/{app,data,logs,storage,config,ssl}
|
||||
mkdir -p /var/log/my-network
|
||||
|
||||
# Создать директории для хранения
|
||||
mkdir -p /opt/my-network/storage/{uploads,previews,encrypted,my-network}
|
||||
|
||||
# Права доступа
|
||||
chown -R my-network:my-network /opt/my-network
|
||||
chown -R my-network:my-network /var/log/my-network
|
||||
|
||||
chmod 755 /opt/my-network
|
||||
chmod 750 /opt/my-network/config
|
||||
chmod 700 /opt/my-network/ssl
|
||||
|
||||
log_info "Directories configured successfully"
|
||||
}
|
||||
|
||||
# Копирование файлов приложения
|
||||
deploy_application() {
|
||||
log_step "Deploying MY Network application..."
|
||||
|
||||
# Копировать исходники
|
||||
cp -r . /opt/my-network/app/
|
||||
|
||||
# Установить права
|
||||
chown -R my-network:my-network /opt/my-network/app
|
||||
|
||||
# Создать .env файл для продакшена
|
||||
cat > /opt/my-network/app/.env << EOF
|
||||
# MY Network Production Configuration
|
||||
PROJECT_NAME=MY-Network
|
||||
PROJECT_VERSION=2.0.0
|
||||
DEBUG=False
|
||||
ENVIRONMENT=production
|
||||
|
||||
# Database Configuration
|
||||
DATABASE_URL=mysql://mymusic:mymusic_password@localhost:$DB_PORT/mymusic
|
||||
DATABASE_POOL_SIZE=20
|
||||
DATABASE_MAX_OVERFLOW=30
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL=redis://localhost:$REDIS_PORT/0
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# Application Settings
|
||||
SECRET_KEY=$(openssl rand -hex 32)
|
||||
MAX_FILE_SIZE=5368709120
|
||||
STORAGE_PATH=/opt/my-network/storage
|
||||
|
||||
# MY Network Settings
|
||||
MY_NETWORK_ENABLED=True
|
||||
MY_NETWORK_NODE_ID=$(uuidgen)
|
||||
MY_NETWORK_BOOTSTRAP_NODES=[]
|
||||
MY_NETWORK_P2P_PORT=8001
|
||||
MY_NETWORK_API_PORT=$APP_PORT
|
||||
|
||||
# SSL Settings
|
||||
SSL_ENABLED=True
|
||||
SSL_CERT_PATH=/opt/my-network/ssl/fullchain.pem
|
||||
SSL_KEY_PATH=/opt/my-network/ssl/privkey.pem
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=INFO
|
||||
LOG_FILE=/var/log/my-network/app.log
|
||||
|
||||
# Monitoring
|
||||
METRICS_ENABLED=True
|
||||
PROMETHEUS_PORT=9090
|
||||
EOF
|
||||
|
||||
log_info "Application deployed successfully"
|
||||
}
|
||||
|
||||
# Настройка nginx
|
||||
setup_nginx() {
|
||||
log_step "Configuring nginx..."
|
||||
|
||||
# Создать конфигурацию nginx
|
||||
cat > /etc/nginx/sites-available/my-network << EOF
|
||||
# MY Network Nginx Configuration
|
||||
upstream my_network_backend {
|
||||
server 127.0.0.1:$APP_PORT;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS redirect
|
||||
server {
|
||||
listen $HTTP_PORT;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# Для Let's Encrypt challenge
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
# Редирект на HTTPS
|
||||
location / {
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS сервер
|
||||
server {
|
||||
listen $HTTPS_PORT ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# SSL сертификаты
|
||||
ssl_certificate /opt/my-network/ssl/fullchain.pem;
|
||||
ssl_certificate_key /opt/my-network/ssl/privkey.pem;
|
||||
|
||||
# SSL настройки
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Безопасность
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Основное приложение
|
||||
location / {
|
||||
proxy_pass http://my_network_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_cache_bypass \$http_upgrade;
|
||||
proxy_read_timeout 86400;
|
||||
|
||||
# Ограничения
|
||||
client_max_body_size 5G;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# MY Network мониторинг
|
||||
location /api/my/monitor/ {
|
||||
proxy_pass http://my_network_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host \$host;
|
||||
proxy_cache_bypass \$http_upgrade;
|
||||
|
||||
# Разрешить для всех
|
||||
allow all;
|
||||
}
|
||||
|
||||
# Статические файлы
|
||||
location /static/ {
|
||||
alias /opt/my-network/storage/static/;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Логи
|
||||
access_log /var/log/nginx/my-network-access.log;
|
||||
error_log /var/log/nginx/my-network-error.log;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Включить сайт
|
||||
ln -sf /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Проверить конфигурацию
|
||||
nginx -t
|
||||
|
||||
log_info "Nginx configured successfully"
|
||||
}
|
||||
|
||||
# Получение SSL сертификата
|
||||
setup_ssl() {
|
||||
log_step "Setting up SSL certificate..."
|
||||
|
||||
# Запустить nginx для получения сертификата
|
||||
systemctl start nginx
|
||||
|
||||
# Получить сертификат Let's Encrypt
|
||||
if certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect; then
|
||||
log_info "SSL certificate obtained successfully"
|
||||
|
||||
# Копировать сертификаты в нашу директорию
|
||||
cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem /opt/my-network/ssl/
|
||||
cp /etc/letsencrypt/live/$DOMAIN/privkey.pem /opt/my-network/ssl/
|
||||
chown my-network:my-network /opt/my-network/ssl/*
|
||||
|
||||
# Настроить автообновление
|
||||
echo "0 3 * * * /usr/bin/certbot renew --quiet && systemctl reload nginx" | crontab -
|
||||
|
||||
else
|
||||
log_warn "Failed to obtain SSL certificate, generating self-signed..."
|
||||
|
||||
# Создать самоподписанный сертификат
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /opt/my-network/ssl/privkey.pem \
|
||||
-out /opt/my-network/ssl/fullchain.pem \
|
||||
-subj "/C=US/ST=State/L=City/O=Organization/CN=$DOMAIN"
|
||||
|
||||
chown my-network:my-network /opt/my-network/ssl/*
|
||||
fi
|
||||
}
|
||||
|
||||
# Создание docker-compose для продакшена
|
||||
create_docker_compose() {
|
||||
log_step "Creating production docker-compose..."
|
||||
|
||||
cat > /opt/my-network/docker-compose.prod.yml << EOF
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# MariaDB Database
|
||||
mariadb:
|
||||
image: mariadb:11.2
|
||||
container_name: my-network-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: \${MYSQL_ROOT_PASSWORD:-root_password}
|
||||
MYSQL_DATABASE: mymusic
|
||||
MYSQL_USER: mymusic
|
||||
MYSQL_PASSWORD: \${MYSQL_PASSWORD:-mymusic_password}
|
||||
volumes:
|
||||
- /opt/my-network/data/mysql:/var/lib/mysql
|
||||
- /opt/my-network/storage:/Storage
|
||||
ports:
|
||||
- "127.0.0.1:$DB_PORT:3306"
|
||||
networks:
|
||||
- my-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: my-network-redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- /opt/my-network/data/redis:/data
|
||||
ports:
|
||||
- "127.0.0.1:$REDIS_PORT:6379"
|
||||
networks:
|
||||
- my-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# MY Network Application
|
||||
my-network-app:
|
||||
build:
|
||||
context: /opt/my-network/app
|
||||
dockerfile: Dockerfile.prod
|
||||
container_name: my-network-app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- ENVIRONMENT=production
|
||||
volumes:
|
||||
- /opt/my-network/app:/app
|
||||
- /opt/my-network/storage:/Storage
|
||||
- /opt/my-network/ssl:/ssl:ro
|
||||
- /var/log/my-network:/var/log/my-network
|
||||
ports:
|
||||
- "127.0.0.1:$APP_PORT:$APP_PORT"
|
||||
- "$((APP_PORT + 1)):$((APP_PORT + 1))" # P2P порт
|
||||
networks:
|
||||
- my-network
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:$APP_PORT/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
networks:
|
||||
my-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
redis_data:
|
||||
EOF
|
||||
|
||||
# Создать Dockerfile для продакшена
|
||||
cat > /opt/my-network/app/Dockerfile.prod << EOF
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Установить системные зависимости
|
||||
RUN apt-get update && apt-get install -y \\
|
||||
gcc \\
|
||||
g++ \\
|
||||
libmariadb-dev \\
|
||||
pkg-config \\
|
||||
curl \\
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Создать пользователя приложения
|
||||
RUN useradd -r -s /bin/false -d /app mynetwork
|
||||
|
||||
# Установить зависимости Python
|
||||
WORKDIR /app
|
||||
COPY requirements_new.txt .
|
||||
RUN pip install --no-cache-dir -r requirements_new.txt
|
||||
|
||||
# Копировать приложение
|
||||
COPY . .
|
||||
RUN chown -R mynetwork:mynetwork /app
|
||||
|
||||
# Создать директории
|
||||
RUN mkdir -p /var/log/my-network && \\
|
||||
chown mynetwork:mynetwork /var/log/my-network
|
||||
|
||||
USER mynetwork
|
||||
|
||||
# Порты
|
||||
EXPOSE $APP_PORT $((APP_PORT + 1))
|
||||
|
||||
# Команда запуска
|
||||
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "$APP_PORT"]
|
||||
EOF
|
||||
|
||||
log_info "Docker configuration created successfully"
|
||||
}
|
||||
|
||||
# Создание systemd сервиса
|
||||
create_systemd_service() {
|
||||
log_step "Creating systemd service..."
|
||||
|
||||
cat > /etc/systemd/system/my-network.service << EOF
|
||||
[Unit]
|
||||
Description=MY Network Distributed Protocol Service
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
WorkingDirectory=/opt/my-network
|
||||
ExecStart=/usr/bin/docker-compose -f docker-compose.prod.yml up -d
|
||||
ExecStop=/usr/bin/docker-compose -f docker-compose.prod.yml down
|
||||
User=my-network
|
||||
Group=my-network
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Перезагрузить systemd и запустить сервис
|
||||
systemctl daemon-reload
|
||||
systemctl enable my-network
|
||||
|
||||
log_info "Systemd service created successfully"
|
||||
}
|
||||
|
||||
# Настройка мониторинга
|
||||
setup_monitoring() {
|
||||
log_step "Setting up monitoring..."
|
||||
|
||||
# Создать скрипт проверки здоровья
|
||||
cat > /opt/my-network/health_check.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Health Check Script
|
||||
|
||||
DOMAIN="localhost"
|
||||
PORT="15100"
|
||||
LOG_FILE="/var/log/my-network/health.log"
|
||||
|
||||
# Функция логирования
|
||||
log_message() {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
|
||||
}
|
||||
|
||||
# Проверка HTTP эндпоинта
|
||||
check_http() {
|
||||
if curl -f -s "http://$DOMAIN:$PORT/health" > /dev/null; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Проверка MY Network
|
||||
check_my_network() {
|
||||
if curl -f -s "http://$DOMAIN:$PORT/api/my/health" > /dev/null; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Основная проверка
|
||||
if check_http && check_my_network; then
|
||||
log_message "Health check PASSED"
|
||||
exit 0
|
||||
else
|
||||
log_message "Health check FAILED"
|
||||
|
||||
# Попытаться перезапустить сервис
|
||||
systemctl restart my-network
|
||||
log_message "Service restart attempted"
|
||||
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /opt/my-network/health_check.sh
|
||||
chown my-network:my-network /opt/my-network/health_check.sh
|
||||
|
||||
# Добавить в cron для мониторинга каждые 5 минут
|
||||
echo "*/5 * * * * /opt/my-network/health_check.sh" | crontab -u my-network -
|
||||
|
||||
log_info "Monitoring configured successfully"
|
||||
}
|
||||
|
||||
# Запуск всех сервисов
|
||||
start_services() {
|
||||
log_step "Starting all services..."
|
||||
|
||||
# Запустить nginx
|
||||
systemctl enable nginx
|
||||
systemctl restart nginx
|
||||
|
||||
# Запустить MY Network
|
||||
systemctl start my-network
|
||||
|
||||
# Подождать запуска
|
||||
sleep 10
|
||||
|
||||
# Проверить статус
|
||||
if systemctl is-active --quiet my-network; then
|
||||
log_info "MY Network service is running"
|
||||
else
|
||||
log_error "MY Network service failed to start"
|
||||
systemctl status my-network
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet nginx; then
|
||||
log_info "Nginx service is running"
|
||||
else
|
||||
log_error "Nginx service failed to start"
|
||||
systemctl status nginx
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Финальная информация
|
||||
print_summary() {
|
||||
echo -e "${GREEN}"
|
||||
cat << EOF
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ MY NETWORK DEPLOYMENT COMPLETED ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
🌐 Web Interface: https://$DOMAIN
|
||||
📊 Monitoring: https://$DOMAIN/api/my/monitor/
|
||||
🔧 API Documentation: https://$DOMAIN/api/docs
|
||||
❤️ Health Check: https://$DOMAIN/health
|
||||
|
||||
📝 Configuration Files:
|
||||
• Application: /opt/my-network/app/.env
|
||||
• Nginx: /etc/nginx/sites-available/my-network
|
||||
• Docker: /opt/my-network/docker-compose.prod.yml
|
||||
• SSL: /opt/my-network/ssl/
|
||||
|
||||
📋 Management Commands:
|
||||
• Start service: systemctl start my-network
|
||||
• Stop service: systemctl stop my-network
|
||||
• Restart service: systemctl restart my-network
|
||||
• View logs: journalctl -u my-network -f
|
||||
• Health check: /opt/my-network/health_check.sh
|
||||
|
||||
🔒 Security Features:
|
||||
✅ SSL/TLS encryption
|
||||
✅ Firewall configured
|
||||
✅ Fail2ban protection
|
||||
✅ Security headers
|
||||
✅ Rate limiting
|
||||
|
||||
🚀 MY Network Features:
|
||||
✅ Distributed content protocol
|
||||
✅ P2P networking
|
||||
✅ Content synchronization
|
||||
✅ Load balancing
|
||||
✅ Real-time monitoring
|
||||
|
||||
The system is now ready for production use!
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
}
|
||||
|
||||
# Главная функция
|
||||
main() {
|
||||
print_header
|
||||
|
||||
log_info "Starting MY Network production deployment..."
|
||||
|
||||
check_root
|
||||
check_os
|
||||
install_dependencies
|
||||
setup_firewall
|
||||
create_app_user
|
||||
setup_directories
|
||||
deploy_application
|
||||
setup_nginx
|
||||
setup_ssl
|
||||
create_docker_compose
|
||||
create_systemd_service
|
||||
setup_monitoring
|
||||
start_services
|
||||
|
||||
print_summary
|
||||
|
||||
log_info "Deployment completed successfully!"
|
||||
}
|
||||
|
||||
# Запуск скрипта
|
||||
main "$@"
|
||||
Executable
+190
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Uploader Bot - Диагностика 502 ошибки
|
||||
# Автоматическая диагностика проблем с доступностью
|
||||
|
||||
DOMAIN="$1"
|
||||
if [ -z "$DOMAIN" ]; then
|
||||
echo "Использование: $0 <domain>"
|
||||
echo "Пример: $0 my-public-node-3.projscale.dev"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MAIN_PROJECT_DIR="/home/myuploader/uploader-bot"
|
||||
COMPOSE_FILE="docker-compose.production.yml"
|
||||
|
||||
# Цвета
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}🔍 MY UPLOADER BOT - ДИАГНОСТИКА 502${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
cd "$MAIN_PROJECT_DIR" 2>/dev/null || {
|
||||
echo -e "${RED}❌ Не найдена директория проекта: $MAIN_PROJECT_DIR${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo -e "${YELLOW}1. 🐳 Проверка статуса Docker контейнеров${NC}"
|
||||
echo "----------------------------------------"
|
||||
docker-compose -f "$COMPOSE_FILE" ps
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}2. 🔍 Проверка работающих контейнеров${NC}"
|
||||
echo "----------------------------------------"
|
||||
RUNNING_CONTAINERS=$(docker-compose -f "$COMPOSE_FILE" ps --services --filter "status=running")
|
||||
if [ -z "$RUNNING_CONTAINERS" ]; then
|
||||
echo -e "${RED}❌ НЕТ РАБОТАЮЩИХ КОНТЕЙНЕРОВ!${NC}"
|
||||
echo -e "${YELLOW}Попробуйте запустить: sudo ./start.sh${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}✅ Работающие контейнеры:${NC}"
|
||||
echo "$RUNNING_CONTAINERS"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}3. 🌐 Проверка доступности локальных портов${NC}"
|
||||
echo "----------------------------------------"
|
||||
echo -n "Порт 15100 (API): "
|
||||
if curl -f -s http://localhost:15100/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Доступен${NC}"
|
||||
API_STATUS="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Недоступен${NC}"
|
||||
API_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
echo -n "Порт 3000 (Web2-client): "
|
||||
if curl -f -s http://localhost:3000/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Доступен${NC}"
|
||||
WEB2_STATUS="OK"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Недоступен (возможно, обычная нода)${NC}"
|
||||
WEB2_STATUS="SKIP"
|
||||
fi
|
||||
|
||||
echo -n "Порт 80 (Nginx): "
|
||||
if curl -f -s http://localhost > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Доступен${NC}"
|
||||
NGINX_STATUS="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Недоступен${NC}"
|
||||
NGINX_STATUS="FAIL"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}4. 🗄️ Проверка базы данных${NC}"
|
||||
echo "----------------------------------------"
|
||||
echo -n "PostgreSQL: "
|
||||
if docker-compose -f "$COMPOSE_FILE" exec -T postgres pg_isready -U my_user -d my_uploader_db 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ Доступна${NC}"
|
||||
DB_STATUS="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Недоступна${NC}"
|
||||
DB_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
echo -n "Redis: "
|
||||
if docker-compose -f "$COMPOSE_FILE" exec -T redis redis-cli ping 2>/dev/null | grep -q PONG; then
|
||||
echo -e "${GREEN}✅ Доступен${NC}"
|
||||
REDIS_STATUS="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Недоступен${NC}"
|
||||
REDIS_STATUS="FAIL"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}5. 🔍 Последние логи основного приложения${NC}"
|
||||
echo "----------------------------------------"
|
||||
echo "Последние 10 строк логов app контейнера:"
|
||||
docker-compose -f "$COMPOSE_FILE" logs --tail=10 app
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}6. 🔍 Проверка nginx конфигурации${NC}"
|
||||
echo "----------------------------------------"
|
||||
echo -n "Nginx config test: "
|
||||
if docker-compose -f "$COMPOSE_FILE" exec -T nginx nginx -t 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ Конфигурация корректна${NC}"
|
||||
NGINX_CONFIG="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Ошибка в конфигурации${NC}"
|
||||
NGINX_CONFIG="FAIL"
|
||||
echo "Детали ошибки:"
|
||||
docker-compose -f "$COMPOSE_FILE" exec -T nginx nginx -t
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}7. 🌐 Проверка внешней доступности${NC}"
|
||||
echo "----------------------------------------"
|
||||
echo -n "HTTPS доступность ($DOMAIN): "
|
||||
if curl -f -s -k "https://$DOMAIN/health" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Доступен${NC}"
|
||||
HTTPS_STATUS="OK"
|
||||
else
|
||||
echo -e "${RED}❌ Недоступен (502 ошибка)${NC}"
|
||||
HTTPS_STATUS="FAIL"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}📊 СВОДКА ДИАГНОСТИКИ${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
|
||||
echo -e "API (localhost:15100): $([ "$API_STATUS" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $API_STATUS${NC}"
|
||||
echo -e "Web2-client (localhost:3000): $([ "$WEB2_STATUS" = "OK" ] && echo -e "${GREEN}✅" || [ "$WEB2_STATUS" = "SKIP" ] && echo -e "${YELLOW}⚠️" || echo -e "${RED}❌") $WEB2_STATUS${NC}"
|
||||
echo -e "Nginx (localhost:80): $([ "$NGINX_STATUS" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $NGINX_STATUS${NC}"
|
||||
echo -e "PostgreSQL: $([ "$DB_STATUS" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $DB_STATUS${NC}"
|
||||
echo -e "Redis: $([ "$REDIS_STATUS" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $REDIS_STATUS${NC}"
|
||||
echo -e "Nginx Config: $([ "$NGINX_CONFIG" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $NGINX_CONFIG${NC}"
|
||||
echo -e "HTTPS ($DOMAIN): $([ "$HTTPS_STATUS" = "OK" ] && echo -e "${GREEN}✅" || echo -e "${RED}❌") $HTTPS_STATUS${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}🛠️ РЕКОМЕНДАЦИИ:${NC}"
|
||||
|
||||
if [ "$API_STATUS" != "OK" ]; then
|
||||
echo -e "${RED}❌ Основная проблема: API приложение не работает${NC}"
|
||||
echo -e "${YELLOW}Решение:${NC}"
|
||||
echo "1. Проверьте логи приложения: sudo docker-compose -f $COMPOSE_FILE logs app"
|
||||
echo "2. Перезапустите приложение: sudo docker-compose -f $COMPOSE_FILE restart app"
|
||||
echo "3. Проверьте переменные окружения в .env файле"
|
||||
echo ""
|
||||
elif [ "$NGINX_CONFIG" != "OK" ]; then
|
||||
echo -e "${RED}❌ Проблема в конфигурации Nginx${NC}"
|
||||
echo -e "${YELLOW}Решение:${NC}"
|
||||
echo "1. Проверьте конфигурацию nginx"
|
||||
echo "2. Перезапустите nginx: sudo docker-compose -f $COMPOSE_FILE restart nginx"
|
||||
echo ""
|
||||
elif [ "$DB_STATUS" != "OK" ] || [ "$REDIS_STATUS" != "OK" ]; then
|
||||
echo -e "${RED}❌ Проблема с базой данных или Redis${NC}"
|
||||
echo -e "${YELLOW}Решение:${NC}"
|
||||
echo "1. Перезапустите сервисы: sudo docker-compose -f $COMPOSE_FILE restart postgres redis"
|
||||
echo "2. Проверьте логи: sudo docker-compose -f $COMPOSE_FILE logs postgres redis"
|
||||
echo ""
|
||||
else
|
||||
echo -e "${GREEN}🔍 Все локальные сервисы работают корректно${NC}"
|
||||
echo -e "${YELLOW}Возможные причины 502 ошибки:${NC}"
|
||||
echo "1. Приложение еще запускается (подождите 2-3 минуты)"
|
||||
echo "2. Проблема с SSL сертификатом"
|
||||
echo "3. Неправильная конфигурация домена"
|
||||
echo ""
|
||||
echo -e "${BLUE}Дополнительная диагностика:${NC}"
|
||||
echo "sudo docker-compose -f $COMPOSE_FILE logs -f app"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}🔧 Быстрые команды для исправления:${NC}"
|
||||
echo "# Полная перезагрузка всех сервисов:"
|
||||
echo "sudo docker-compose -f $COMPOSE_FILE down && sudo docker-compose -f $COMPOSE_FILE up -d"
|
||||
echo ""
|
||||
echo "# Просмотр логов в реальном времени:"
|
||||
echo "sudo docker-compose -f $COMPOSE_FILE logs -f"
|
||||
echo ""
|
||||
echo "# Перезапуск только основного приложения:"
|
||||
echo "sudo docker-compose -f $COMPOSE_FILE restart app"
|
||||
echo ""
|
||||
echo "# Проверка статуса через 2 минуты:"
|
||||
echo "sleep 120 && curl -v https://$DOMAIN/health"
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Server Issues Fix Script
|
||||
# Исправляет проблемы с nginx и контейнерами для проекта в /home/uploader-bot
|
||||
|
||||
set -e
|
||||
|
||||
echo "=============================================="
|
||||
echo "MY NETWORK SERVER FIX SCRIPT"
|
||||
echo "Время: $(date)"
|
||||
echo "=============================================="
|
||||
|
||||
PROJECT_DIR="/home/uploader-bot"
|
||||
|
||||
echo "🔧 Проверяю проект в: $PROJECT_DIR"
|
||||
|
||||
if [ ! -d "$PROJECT_DIR" ]; then
|
||||
echo "❌ Директория проекта не найдена: $PROJECT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
echo "✅ Работаю в: $(pwd)"
|
||||
|
||||
if [ ! -f "docker-compose.production.yml" ]; then
|
||||
echo "❌ docker-compose.production.yml не найден!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Docker compose файл найден"
|
||||
|
||||
echo ""
|
||||
echo "=== 1. СОЗДАНИЕ NGINX КОНФИГУРАЦИИ ==="
|
||||
echo "🔧 Создаю правильную nginx конфигурацию..."
|
||||
|
||||
cat > /tmp/nginx_uploader.conf << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# API проксирование на приложение
|
||||
location /api/ {
|
||||
proxy_pass http://localhost:3000/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
|
||||
# Альтернативные порты для API
|
||||
location /api5000/ {
|
||||
proxy_pass http://localhost:5000/;
|
||||
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;
|
||||
}
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
proxy_pass http://localhost:3000/api/health;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# Основная страница - пробуем API
|
||||
location / {
|
||||
proxy_pass http://localhost:3000/;
|
||||
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
|
||||
|
||||
sudo mv /tmp/nginx_uploader.conf /etc/nginx/sites-available/default
|
||||
sudo ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
|
||||
|
||||
echo "✅ Nginx конфигурация создана"
|
||||
|
||||
echo ""
|
||||
echo "=== 2. ПРОВЕРКА ТЕКУЩИХ КОНТЕЙНЕРОВ ==="
|
||||
echo "Текущий статус контейнеров:"
|
||||
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
echo ""
|
||||
echo "=== 3. АНАЛИЗ ЛОГОВ ПРИЛОЖЕНИЯ ==="
|
||||
# Найти контейнер приложения
|
||||
APP_CONTAINER=$(docker ps --format "{{.Names}}" | grep -E "(app|uploader)" | head -1)
|
||||
if [ -n "$APP_CONTAINER" ]; then
|
||||
echo "📄 Логи контейнера $APP_CONTAINER (последние 15 строк):"
|
||||
docker logs --tail=15 "$APP_CONTAINER" 2>&1 || echo "Не удалось получить логи"
|
||||
else
|
||||
echo "❌ Контейнер приложения не найден"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 4. ПЕРЕЗАПУСК ПРОБЛЕМНЫХ КОНТЕЙНЕРОВ ==="
|
||||
|
||||
# Остановить только перезапускающиеся контейнеры
|
||||
RESTARTING_CONTAINERS=$(docker ps -a --filter "status=restarting" --format "{{.Names}}")
|
||||
if [ -n "$RESTARTING_CONTAINERS" ]; then
|
||||
echo "🛑 Останавливаю перезапускающиеся контейнеры:"
|
||||
echo "$RESTARTING_CONTAINERS"
|
||||
echo "$RESTARTING_CONTAINERS" | xargs docker stop 2>/dev/null || true
|
||||
echo "$RESTARTING_CONTAINERS" | xargs docker rm 2>/dev/null || true
|
||||
else
|
||||
echo "✅ Нет перезапускающихся контейнеров"
|
||||
fi
|
||||
|
||||
# Удалить старые MariaDB контейнеры если есть
|
||||
MARIADB_CONTAINERS=$(docker ps -a --format "{{.Names}}" | grep maria || true)
|
||||
if [ -n "$MARIADB_CONTAINERS" ]; then
|
||||
echo "🗑️ Удаляю старые MariaDB контейнеры:"
|
||||
echo "$MARIADB_CONTAINERS" | xargs docker stop 2>/dev/null || true
|
||||
echo "$MARIADB_CONTAINERS" | xargs docker rm 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 5. ЗАПУСК СЕРВИСОВ ==="
|
||||
echo "🚀 Запуск Docker Compose..."
|
||||
|
||||
# Запуск основных сервисов
|
||||
docker-compose -f docker-compose.production.yml up -d postgres redis
|
||||
|
||||
echo "⏳ Ждем запуска БД (10 секунд)..."
|
||||
sleep 10
|
||||
|
||||
# Запуск приложения
|
||||
echo "🚀 Запуск приложения..."
|
||||
docker-compose -f docker-compose.production.yml up -d app
|
||||
|
||||
echo "⏳ Ждем запуска приложения (20 секунд)..."
|
||||
sleep 20
|
||||
|
||||
echo ""
|
||||
echo "=== 6. ПРОВЕРКА СТАТУСА ==="
|
||||
echo "Новый статус контейнеров:"
|
||||
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
echo ""
|
||||
echo "=== 7. ТЕСТИРОВАНИЕ NGINX ==="
|
||||
sudo nginx -t
|
||||
if [ $? -eq 0 ]; then
|
||||
sudo systemctl reload nginx
|
||||
echo "✅ Nginx конфигурация корректна и перезагружена"
|
||||
else
|
||||
echo "❌ Ошибка в nginx конфигурации"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 8. ПРОВЕРКА ПРИЛОЖЕНИЯ ==="
|
||||
echo "Тест локальных соединений:"
|
||||
sleep 5
|
||||
|
||||
# Проверка портов приложения
|
||||
for port in 3000 5000; do
|
||||
if timeout 5 curl -s http://localhost:$port/api/health > /dev/null 2>&1; then
|
||||
echo "✅ localhost:$port/api/health - РАБОТАЕТ"
|
||||
else
|
||||
echo "❌ localhost:$port/api/health - НЕ РАБОТАЕТ"
|
||||
fi
|
||||
done
|
||||
|
||||
# Проверка через nginx
|
||||
if timeout 5 curl -s http://localhost/api/health > /dev/null 2>&1; then
|
||||
echo "✅ nginx proxy (/api/health) - РАБОТАЕТ"
|
||||
else
|
||||
echo "❌ nginx proxy (/api/health) - НЕ РАБОТАЕТ"
|
||||
fi
|
||||
|
||||
if timeout 5 curl -s http://localhost/health > /dev/null 2>&1; then
|
||||
echo "✅ nginx proxy (/health) - РАБОТАЕТ"
|
||||
else
|
||||
echo "❌ nginx proxy (/health) - НЕ РАБОТАЕТ"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 9. ФИНАЛЬНЫЕ ЛОГИ ==="
|
||||
APP_CONTAINER=$(docker ps --format "{{.Names}}" | grep -E "(app|uploader)" | head -1)
|
||||
if [ -n "$APP_CONTAINER" ]; then
|
||||
echo "📄 Финальные логи $APP_CONTAINER:"
|
||||
docker logs --tail=10 "$APP_CONTAINER" 2>&1 || echo "Логи недоступны"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "ИСПРАВЛЕНИЕ ЗАВЕРШЕНО!"
|
||||
echo "Время: $(date)"
|
||||
echo "=============================================="
|
||||
|
||||
echo ""
|
||||
echo "📊 РЕЗУЛЬТАТ:"
|
||||
echo "Проект: $PROJECT_DIR"
|
||||
echo "Активные контейнеры:"
|
||||
docker ps --format "{{.Names}}: {{.Status}}" | head -10
|
||||
|
||||
echo ""
|
||||
echo "🌐 ВНЕШНИЙ ТЕСТ (выполните вручную):"
|
||||
echo "curl -I http://$(curl -s ifconfig.me)/api/health"
|
||||
echo "curl -I http://$(curl -s ifconfig.me)/health"
|
||||
|
||||
echo ""
|
||||
echo "🔍 ДЛЯ ОТЛАДКИ:"
|
||||
echo "docker logs \$(docker ps --format \"{{.Names}}\" | grep app | head -1)"
|
||||
echo "sudo journalctl -u nginx -f"
|
||||
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Server Full Diagnosis Script
|
||||
# Собирает всю информацию о состоянии сервера для удаленной диагностики
|
||||
|
||||
echo "=============================================="
|
||||
echo "MY NETWORK SERVER DIAGNOSIS REPORT"
|
||||
echo "Время: $(date)"
|
||||
echo "Сервер: $(hostname)"
|
||||
echo "=============================================="
|
||||
|
||||
echo ""
|
||||
echo "=== 1. СИСТЕМНАЯ ИНФОРМАЦИЯ ==="
|
||||
echo "OS: $(lsb_release -d 2>/dev/null | cut -f2 || cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"
|
||||
echo "Kernel: $(uname -r)"
|
||||
echo "Uptime: $(uptime)"
|
||||
echo "Disk space:"
|
||||
df -h | grep -E "(/$|/home)"
|
||||
echo "Memory:"
|
||||
free -h
|
||||
echo "Load average: $(cat /proc/loadavg)"
|
||||
|
||||
echo ""
|
||||
echo "=== 2. СЕТЕВЫЕ ПОРТЫ И СОЕДИНЕНИЯ ==="
|
||||
echo "Активные порты (LISTEN):"
|
||||
sudo netstat -tulpn | grep LISTEN | sort -k4
|
||||
echo ""
|
||||
echo "Проверка ключевых портов:"
|
||||
for port in 22 80 443 3000 5000 8080; do
|
||||
if sudo netstat -tulpn | grep -q ":${port} "; then
|
||||
echo "✅ Порт $port: АКТИВЕН"
|
||||
else
|
||||
echo "❌ Порт $port: НЕ АКТИВЕН"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== 3. СТАТУС СИСТЕМНЫХ СЛУЖБ ==="
|
||||
echo "Docker service:"
|
||||
sudo systemctl status docker --no-pager -l
|
||||
echo ""
|
||||
echo "SSH service:"
|
||||
sudo systemctl status ssh --no-pager -l
|
||||
echo ""
|
||||
echo "Nginx service:"
|
||||
sudo systemctl status nginx --no-pager -l
|
||||
echo ""
|
||||
echo "MyUploader service:"
|
||||
sudo systemctl status myuploader --no-pager -l 2>/dev/null || echo "MyUploader service не найден"
|
||||
|
||||
echo ""
|
||||
echo "=== 4. DOCKER ИНФОРМАЦИЯ ==="
|
||||
echo "Docker version:"
|
||||
docker --version 2>/dev/null || echo "Docker не установлен"
|
||||
echo ""
|
||||
echo "Docker-compose version:"
|
||||
docker-compose --version 2>/dev/null || echo "Docker-compose не установлен"
|
||||
echo ""
|
||||
echo "Запущенные контейнеры:"
|
||||
sudo docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo "Контейнеры не найдены"
|
||||
echo ""
|
||||
echo "Docker networks:"
|
||||
sudo docker network ls 2>/dev/null || echo "Docker networks недоступны"
|
||||
|
||||
echo ""
|
||||
echo "=== 5. MY-UPLOADER-BOT ПРОЕКТ ==="
|
||||
PROJECT_DIR="/home/myuploader/my-uploader-bot"
|
||||
if [ -d "$PROJECT_DIR" ]; then
|
||||
echo "✅ Директория проекта найдена: $PROJECT_DIR"
|
||||
echo "Содержимое директории:"
|
||||
ls -la "$PROJECT_DIR"
|
||||
echo ""
|
||||
|
||||
if [ -f "$PROJECT_DIR/docker-compose.production.yml" ]; then
|
||||
echo "✅ Docker-compose файл найден"
|
||||
echo "Статус compose проекта:"
|
||||
cd "$PROJECT_DIR"
|
||||
sudo docker-compose -f docker-compose.production.yml ps 2>/dev/null || echo "Ошибка получения статуса compose"
|
||||
else
|
||||
echo "❌ Docker-compose.production.yml не найден"
|
||||
fi
|
||||
else
|
||||
echo "❌ Директория проекта не найдена: $PROJECT_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 6. ЛОГИ КОНТЕЙНЕРОВ (последние 10 строк) ==="
|
||||
for container in uploader-bot-app-1 uploader-bot-postgres-1 uploader-bot-nginx-1; do
|
||||
echo "--- Логи $container ---"
|
||||
sudo docker logs --tail=10 "$container" 2>/dev/null || echo "Контейнер $container не найден или не запущен"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== 7. NGINX КОНФИГУРАЦИЯ ==="
|
||||
echo "Nginx конфигурация:"
|
||||
sudo nginx -t 2>&1
|
||||
echo ""
|
||||
echo "Default site config:"
|
||||
if [ -f "/etc/nginx/sites-enabled/default" ]; then
|
||||
sudo cat /etc/nginx/sites-enabled/default
|
||||
else
|
||||
echo "❌ /etc/nginx/sites-enabled/default не найден"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 8. SSH КОНФИГУРАЦИЯ ==="
|
||||
echo "SSH config (основные параметры):"
|
||||
sudo cat /etc/ssh/sshd_config | grep -E "^(Port|PasswordAuthentication|PubkeyAuthentication|PermitRootLogin)" || echo "SSH config недоступен"
|
||||
|
||||
echo ""
|
||||
echo "=== 9. ФАЙРВОЛ (UFW) ==="
|
||||
echo "UFW статус:"
|
||||
sudo ufw status verbose 2>/dev/null || echo "UFW не установлен или недоступен"
|
||||
|
||||
echo ""
|
||||
echo "=== 10. ЛОКАЛЬНЫЕ ТЕСТЫ СОЕДИНЕНИЙ ==="
|
||||
echo "Тест локальных соединений:"
|
||||
for port in 3000 5000 8080; do
|
||||
if curl -s --max-time 3 http://localhost:$port/api/health > /dev/null 2>&1; then
|
||||
echo "✅ localhost:$port/api/health - ДОСТУПЕН"
|
||||
else
|
||||
echo "❌ localhost:$port/api/health - НЕ ДОСТУПЕН"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Тест nginx:"
|
||||
curl -I http://localhost/ 2>/dev/null | head -5 || echo "❌ Nginx недоступен на localhost"
|
||||
|
||||
echo ""
|
||||
echo "=== 11. ПРОЦЕССЫ ==="
|
||||
echo "Процессы Python/Docker:"
|
||||
ps aux | grep -E "(python|docker|nginx)" | grep -v grep
|
||||
|
||||
echo ""
|
||||
echo "=== 12. RECENT SYSTEM LOGS ==="
|
||||
echo "Последние системные ошибки:"
|
||||
sudo journalctl --since "1 hour ago" --priority=err --no-pager -n 10 2>/dev/null || echo "Системные логи недоступны"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "ДИАГНОСТИКА ЗАВЕРШЕНА"
|
||||
echo "Время завершения: $(date)"
|
||||
echo "=============================================="
|
||||
|
||||
echo ""
|
||||
echo "📋 КРАТКАЯ СВОДКА:"
|
||||
echo "Скопируйте весь вывод этого скрипта и отправьте для анализа"
|
||||
echo ""
|
||||
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network - Установка сервиса (Шаг 1 из 2)
|
||||
# Устанавливает базовый сервис без защиты
|
||||
# Для защиты используйте: bash secure_service.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network - Установка сервиса"
|
||||
echo "================================="
|
||||
|
||||
# Проверка прав root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Этот скрипт должен запускаться от root"
|
||||
echo "Используйте: sudo bash install_service.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получение домена
|
||||
read -p "🌐 Введите домен (например: my-network.example.com): " DOMAIN
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
echo "❌ Домен обязателен!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получение email для SSL
|
||||
read -p "📧 Введите email для SSL сертификата: " EMAIL
|
||||
if [[ -z "$EMAIL" ]]; then
|
||||
echo "❌ Email обязателен для SSL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📋 Настройки:"
|
||||
echo " Домен: $DOMAIN"
|
||||
echo " Email: $EMAIL"
|
||||
echo " Порт: 15100"
|
||||
echo ""
|
||||
|
||||
# Обновление системы
|
||||
echo "🔄 Обновление системы..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Установка основных пакетов
|
||||
echo "📦 Установка пакетов..."
|
||||
apt install -y \
|
||||
docker.io \
|
||||
docker-compose \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
htop \
|
||||
tree \
|
||||
nano \
|
||||
ufw
|
||||
|
||||
# Запуск Docker
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
# Добавление пользователя в группу docker
|
||||
usermod -aG docker $USER
|
||||
|
||||
# Создание директории проекта
|
||||
PROJECT_DIR="/opt/my-network"
|
||||
mkdir -p $PROJECT_DIR
|
||||
cd $PROJECT_DIR
|
||||
|
||||
# Клонирование или копирование проекта
|
||||
if [[ -d "my-uploader-bot" ]]; then
|
||||
echo "📁 Проект уже существует, обновляем..."
|
||||
cd my-uploader-bot
|
||||
git pull 2>/dev/null || echo "Git pull failed, продолжаем..."
|
||||
cd ..
|
||||
else
|
||||
echo "📥 Копирование проекта..."
|
||||
# Если запускается из директории проекта, копируем
|
||||
if [[ -f "../pyproject.toml" ]]; then
|
||||
cp -r ../ ./my-uploader-bot/
|
||||
else
|
||||
echo "❌ Не найден файл проекта. Убедитесь что скрипт запускается из директории проекта."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd my-uploader-bot
|
||||
|
||||
# Создание .env файла
|
||||
echo "📝 Создание конфигурации..."
|
||||
cat > .env << EOF
|
||||
# MY Network Configuration
|
||||
NODE_ID=node-$(date +%s)
|
||||
NODE_PORT=15100
|
||||
DOMAIN=$DOMAIN
|
||||
EMAIL=$EMAIL
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=my_network
|
||||
DB_USER=my_network_user
|
||||
DB_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
# Security
|
||||
SECRET_KEY=$(openssl rand -base64 64)
|
||||
JWT_SECRET=$(openssl rand -base64 32)
|
||||
|
||||
# Paths
|
||||
STORAGE_PATH=/opt/storage
|
||||
LOG_PATH=/opt/logs
|
||||
|
||||
# Network
|
||||
BOOTSTRAP_NODES=[]
|
||||
SYNC_INTERVAL=300
|
||||
MAX_PEERS=10
|
||||
EOF
|
||||
|
||||
# Создание директорий
|
||||
echo "📁 Создание директорий..."
|
||||
mkdir -p /opt/storage /opt/logs
|
||||
chmod 755 /opt/storage /opt/logs
|
||||
|
||||
# Установка Python зависимостей
|
||||
echo "🐍 Установка Python зависимостей..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements_new.txt
|
||||
|
||||
# Создание systemd сервиса
|
||||
echo "⚙️ Создание systemd сервиса..."
|
||||
cat > /etc/systemd/system/my-network.service << EOF
|
||||
[Unit]
|
||||
Description=MY Network Service
|
||||
After=network.target docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=root
|
||||
WorkingDirectory=$PROJECT_DIR/my-uploader-bot
|
||||
Environment=PATH=$PROJECT_DIR/my-uploader-bot/venv/bin
|
||||
ExecStart=/bin/bash -c 'source venv/bin/activate && python app/main.py'
|
||||
ExecStop=/bin/kill -TERM \$MAINPID
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Запуск Docker Compose
|
||||
echo "🐳 Запуск Docker сервисов..."
|
||||
docker-compose -f docker-compose.new.yml up -d
|
||||
|
||||
# Активация сервиса
|
||||
echo "🔧 Активация сервиса..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable my-network
|
||||
systemctl start my-network
|
||||
|
||||
# Проверка статуса
|
||||
sleep 5
|
||||
echo "📊 Проверка статуса..."
|
||||
systemctl status my-network --no-pager
|
||||
|
||||
# Проверка портов
|
||||
echo "🌐 Проверка портов..."
|
||||
netstat -tlnp | grep :15100 || echo "⚠️ Порт 15100 не слушается"
|
||||
|
||||
# Сохранение конфигурации
|
||||
echo "💾 Сохранение конфигурации..."
|
||||
cat > /opt/my-network-config.txt << EOF
|
||||
MY Network Service - Конфигурация
|
||||
===============================
|
||||
Домен: $DOMAIN
|
||||
Email: $EMAIL
|
||||
Порт: 15100
|
||||
Директория: $PROJECT_DIR
|
||||
Логи: /opt/logs
|
||||
Хранилище: /opt/storage
|
||||
|
||||
Статус сервиса: systemctl status my-network
|
||||
Логи сервиса: journalctl -u my-network -f
|
||||
Перезапуск: systemctl restart my-network
|
||||
|
||||
Для защиты сервера запустите:
|
||||
sudo bash secure_service.sh
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network сервис установлен!"
|
||||
echo "================================"
|
||||
echo "📍 Конфигурация сохранена в: /opt/my-network-config.txt"
|
||||
echo "🌐 Домен: $DOMAIN"
|
||||
echo "🔌 Порт: 15100"
|
||||
echo "📁 Директория: $PROJECT_DIR"
|
||||
echo ""
|
||||
echo "🔍 Проверка работы:"
|
||||
echo " curl http://localhost:15100/api/my/health"
|
||||
echo " systemctl status my-network"
|
||||
echo ""
|
||||
echo "⚠️ ВАЖНО: Для защиты сервера выполните:"
|
||||
echo " sudo bash secure_service.sh"
|
||||
echo ""
|
||||
echo "📚 Документация: /opt/my-network/my-uploader-bot/MY_NETWORK_README.md"
|
||||
@@ -0,0 +1,353 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Healthcheck script for MariaDB
|
||||
#
|
||||
# Runs various tests on the MariaDB server to check its health. Pass the tests
|
||||
# to run as arguments. If all tests succeed, the server is considered healthy,
|
||||
# otherwise it's not.
|
||||
#
|
||||
# Arguments are processed in strict order. Set replication_* options before
|
||||
# the --replication option. This allows a different set of replication checks
|
||||
# on different connections.
|
||||
#
|
||||
# --su{=|-mysql} is option to run the healthcheck as a different unix user.
|
||||
# Useful if mysql@localhost user exists with unix socket authentication
|
||||
# Using this option disregards previous options set, so should usually be the
|
||||
# first option.
|
||||
#
|
||||
# Some tests require SQL privileges.
|
||||
#
|
||||
# TEST MINIMUM GRANTS REQUIRED
|
||||
# connect none*
|
||||
# innodb_initialized USAGE
|
||||
# innodb_buffer_pool_loaded USAGE
|
||||
# galera_online USAGE
|
||||
# galera_ready USAGE
|
||||
# replication REPLICATION_CLIENT (<10.5)or REPLICA MONITOR (10.5+)
|
||||
# mariadbupgrade none, however unix user permissions on datadir
|
||||
#
|
||||
# The SQL user used is the default for the mysql client. This can be the unix user
|
||||
# if no user(or password) is set in the [mariadb-client] section of a configuration
|
||||
# file. --defaults-{file,extra-file,group-suffix} can specify a file/configuration
|
||||
# different from elsewhere.
|
||||
#
|
||||
# Note * though denied error message will result in error log without
|
||||
# any permissions.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
_process_sql()
|
||||
{
|
||||
mysql ${nodefaults:+--no-defaults} \
|
||||
${def['file']:+--defaults-file=${def['file']}} \
|
||||
${def['extra_file']:+--defaults-extra-file=${def['extra_file']}} \
|
||||
${def['group_suffix']:+--defaults-group-suffix=${def['group_suffix']}} \
|
||||
-B "$@"
|
||||
}
|
||||
|
||||
# TESTS
|
||||
|
||||
|
||||
# CONNECT
|
||||
#
|
||||
# Tests that a connection can be made over TCP, the final state
|
||||
# of the entrypoint and is listening. The authentication used
|
||||
# isn't tested.
|
||||
connect()
|
||||
{
|
||||
set +e +o pipefail
|
||||
# (on second extra_file)
|
||||
# shellcheck disable=SC2086
|
||||
mysql ${nodefaults:+--no-defaults} \
|
||||
${def['file']:+--defaults-file=${def['file']}} \
|
||||
${def['extra_file']:+--defaults-extra-file=${def['extra_file']}} \
|
||||
${def['group_suffix']:+--defaults-group-suffix=${def['group_suffix']}} \
|
||||
-h localhost --protocol tcp -e 'select 1' 2>&1 \
|
||||
| grep -qF "Can't connect"
|
||||
local ret=${PIPESTATUS[1]}
|
||||
set -eo pipefail
|
||||
if (( "$ret" == 0 )); then
|
||||
# grep Matched "Can't connect" so we fail
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# INNODB_INITIALIZED
|
||||
#
|
||||
# This tests that the crash recovery of InnoDB has completed
|
||||
# along with all the other things required to make it to a healthy
|
||||
# operational state. Note this may return true in the early
|
||||
# states of initialization. Use with a connect test to avoid
|
||||
# these false positives.
|
||||
innodb_initialized()
|
||||
{
|
||||
local s
|
||||
s=$(_process_sql --skip-column-names -e "select 1 from information_schema.ENGINES WHERE engine='innodb' AND support in ('YES', 'DEFAULT', 'ENABLED')")
|
||||
[ "$s" == 1 ]
|
||||
}
|
||||
|
||||
# INNODB_BUFFER_POOL_LOADED
|
||||
#
|
||||
# Tests the load of the innodb buffer pool as been complete
|
||||
# implies innodb_buffer_pool_load_at_startup=1 (default), or if
|
||||
# manually SET innodb_buffer_pool_load_now=1
|
||||
innodb_buffer_pool_loaded()
|
||||
{
|
||||
local s
|
||||
s=$(_process_sql --skip-column-names -e "select VARIABLE_VALUE from information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Innodb_buffer_pool_load_status'")
|
||||
if [[ $s =~ 'load completed' ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# GALERA_ONLINE
|
||||
#
|
||||
# Tests that the galera node is in the SYNCed state
|
||||
galera_online()
|
||||
{
|
||||
local s
|
||||
s=$(_process_sql --skip-column-names -e "select VARIABLE_VALUE from information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='WSREP_LOCAL_STATE'")
|
||||
# 4 from https://galeracluster.com/library/documentation/node-states.html#node-state-changes
|
||||
# not https://xkcd.com/221/
|
||||
if [[ $s -eq 4 ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# GALERA_READY
|
||||
#
|
||||
# Tests that the Galera provider is ready.
|
||||
galera_ready()
|
||||
{
|
||||
local s
|
||||
s=$(_process_sql --skip-column-names -e "select VARIABLE_VALUE from information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='WSREP_READY'")
|
||||
if [ "$s" = "ON" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# REPLICATION
|
||||
#
|
||||
# Tests the replication has the required set of functions:
|
||||
# --replication_all -> Checks all replication sources
|
||||
# --replication_name=n -> sets the multisource connection name tested
|
||||
# --replication_io -> IO thread is running
|
||||
# --replication_sql -> SQL thread is running
|
||||
# --replication_seconds_behind_master=n -> less than or equal this seconds of delay
|
||||
# --replication_sql_remaining_delay=n -> less than or equal this seconds of remaining delay
|
||||
# (ref: https://mariadb.com/kb/en/delayed-replication/)
|
||||
replication()
|
||||
{
|
||||
# SHOW REPLICA available 10.5+
|
||||
# https://github.com/koalaman/shellcheck/issues/2383
|
||||
# shellcheck disable=SC2016,SC2026
|
||||
_process_sql -e "SHOW ${repl['all']:+all} REPLICA${repl['all']:+S} ${repl['name']:+'${repl['name']}'} STATUS\G" | \
|
||||
{
|
||||
# required for trim of leading space.
|
||||
shopt -s extglob
|
||||
# Row header
|
||||
read -t 5 -r
|
||||
# read timeout
|
||||
[ $? -gt 128 ] && return 1
|
||||
while IFS=":" read -t 1 -r n v; do
|
||||
# Trim leading space
|
||||
n=${n##+([[:space:]])}
|
||||
# Leading space on all values by the \G format needs to be trimmed.
|
||||
v=${v:1}
|
||||
case "$n" in
|
||||
Slave_IO_Running)
|
||||
if [ -n "${repl['io']}" ] && [ "$v" = 'No' ]; then
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
Slave_SQL_Running)
|
||||
if [ -n "${repl['sql']}" ] && [ "$v" = 'No' ]; then
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
Seconds_Behind_Master)
|
||||
# A NULL value is the IO thread not running:
|
||||
if [ -n "${repl['seconds_behind_master']}" ] &&
|
||||
{ [ "$v" = NULL ] ||
|
||||
(( "${repl['seconds_behind_master']}" < "$v" )); }; then
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
SQL_Remaining_Delay)
|
||||
# Unlike Seconds_Behind_Master, sql_remaining_delay will hit NULL
|
||||
# once replication is caught up - https://mariadb.com/kb/en/delayed-replication/
|
||||
if [ -n "${repl['sql_remaining_delay']}" ] &&
|
||||
[ "$v" != NULL ] &&
|
||||
(( "${repl['sql_remaining_delay']}" < "$v" )); then
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# read timeout
|
||||
[ $? -gt 128 ] && return 1
|
||||
return 0
|
||||
}
|
||||
# reachable in command not found(?)
|
||||
# shellcheck disable=SC2317
|
||||
return $?
|
||||
}
|
||||
|
||||
# mariadbupgrade
|
||||
#
|
||||
# Test the lock on the file $datadir/mysql_upgrade_info
|
||||
# https://jira.mariadb.org/browse/MDEV-27068
|
||||
mariadbupgrade()
|
||||
{
|
||||
local f="$datadir/mysql_upgrade_info"
|
||||
if [ -r "$f" ]; then
|
||||
flock --exclusive --nonblock -n 9 9<"$f"
|
||||
return $?
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# MAIN
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "At least one argument required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#ENDOFSUBSTITUTIONS
|
||||
# Marks the end of mysql -> mariadb name changes in 10.6+
|
||||
# Global variables used by tests
|
||||
declare -A repl
|
||||
declare -A def
|
||||
nodefaults=
|
||||
datadir=/var/lib/mysql
|
||||
if [ -f $datadir/.my-healthcheck.cnf ]; then
|
||||
def['extra_file']=$datadir/.my-healthcheck.cnf
|
||||
fi
|
||||
|
||||
_repl_param_check()
|
||||
{
|
||||
case "$1" in
|
||||
seconds_behind_master) ;&
|
||||
sql_remaining_delay)
|
||||
if [ -z "${repl['io']}" ]; then
|
||||
repl['io']=1
|
||||
echo "Forcing --replication_io=1, $1 requires IO thread to be running" >&2
|
||||
fi
|
||||
;;
|
||||
all)
|
||||
if [ -n "${repl['name']}" ]; then
|
||||
unset 'repl[name]'
|
||||
echo "Option --replication_all incompatible with specified source --replication_name, clearing replication_name" >&2
|
||||
fi
|
||||
;;
|
||||
name)
|
||||
if [ -n "${repl['all']}" ]; then
|
||||
unset 'repl[all]'
|
||||
echo "Option --replication_name incompatible with --replication_all, clearing replication_all" >&2
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_test_exists() {
|
||||
declare -F "$1" > /dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--su=*)
|
||||
u="${1#*=}"
|
||||
shift
|
||||
exec gosu "${u}" "${BASH_SOURCE[0]}" "$@"
|
||||
;;
|
||||
--su)
|
||||
shift
|
||||
u=$1
|
||||
shift
|
||||
exec gosu "$u" "${BASH_SOURCE[0]}" "$@"
|
||||
;;
|
||||
--su-mysql)
|
||||
shift
|
||||
exec gosu mysql "${BASH_SOURCE[0]}" "$@"
|
||||
;;
|
||||
--replication_*=*)
|
||||
# Change the n to what is between _ and = and make lower case
|
||||
n=${1#*_}
|
||||
n=${n%%=*}
|
||||
n=${n,,*}
|
||||
# v is after the =
|
||||
v=${1#*=}
|
||||
repl[$n]=$v
|
||||
_repl_param_check "$n"
|
||||
;;
|
||||
--replication_*)
|
||||
# Without =, look for a non --option next as the value,
|
||||
# otherwise treat it as an "enable", just equate to 1.
|
||||
# Clearing option is possible with "--replication_X="
|
||||
n=${1#*_}
|
||||
n=${n,,*}
|
||||
if [ "${2:0:2}" == '--' ]; then
|
||||
repl[$n]=1
|
||||
else
|
||||
repl[$n]=$2
|
||||
shift
|
||||
fi
|
||||
_repl_param_check "$n"
|
||||
;;
|
||||
--datadir=*)
|
||||
datadir=${1#*=}
|
||||
;;
|
||||
--datadir)
|
||||
shift
|
||||
datadir=${1}
|
||||
;;
|
||||
--no-defaults)
|
||||
def=()
|
||||
nodefaults=1
|
||||
;;
|
||||
--defaults-file=*|--defaults-extra-file=*|--defaults-group-suffix=*)
|
||||
n=${1:11} # length --defaults-
|
||||
n=${n%%=*}
|
||||
n=${n//-/_}
|
||||
# v is after the =
|
||||
v=${1#*=}
|
||||
def[$n]=$v
|
||||
nodefaults=
|
||||
;;
|
||||
--defaults-file|--defaults-extra-file|--defaults-group-suffix)
|
||||
n=${1:11} # length --defaults-
|
||||
n=${n//-/_}
|
||||
if [ "${2:0:2}" == '--' ]; then
|
||||
def[$n]=""
|
||||
else
|
||||
def[$n]=$2
|
||||
shift
|
||||
fi
|
||||
nodefaults=
|
||||
;;
|
||||
--*)
|
||||
test=${1#--}
|
||||
;;
|
||||
*)
|
||||
echo "Unknown healthcheck option $1" >&2
|
||||
exit 1
|
||||
esac
|
||||
if [ -n "$test" ]; then
|
||||
if ! _test_exists "$test" ; then
|
||||
echo "healthcheck unknown option or test '$test'" >&2
|
||||
exit 1
|
||||
elif ! "$test"; then
|
||||
echo "healthcheck $test failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
test=
|
||||
fi
|
||||
shift
|
||||
done
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Bootstrap Deployment Monitor
|
||||
# Скрипт для наблюдения за процессом развертывания
|
||||
|
||||
echo "🚀 MY Network Bootstrap Deployment Monitor"
|
||||
echo "=========================================="
|
||||
echo "Сервер: 2.58.65.188"
|
||||
echo "Домен: my-public-node-3.projscale.dev"
|
||||
echo "Время запуска: $(date)"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
clear
|
||||
echo "🚀 MY Network Bootstrap - Live Monitor"
|
||||
echo "====================================="
|
||||
echo "⏰ $(date '+%H:%M:%S') | Обновляется каждые 30 секунд"
|
||||
echo ""
|
||||
|
||||
# Проверка локальных процессов
|
||||
echo "📡 Локальные процессы:"
|
||||
SCP_COUNT=$(ps aux | grep 'scp.*auto_deploy.sh' | grep -v grep | wc -l | tr -d ' ')
|
||||
SSH_COUNT=$(ps aux | grep 'auto_ssh_deploy.exp' | grep -v grep | wc -l | tr -d ' ')
|
||||
|
||||
if [[ $SCP_COUNT -gt 0 ]]; then
|
||||
echo " ✅ SCP Transfer: Активен (передача файлов)"
|
||||
else
|
||||
echo " ✅ SCP Transfer: Завершен"
|
||||
fi
|
||||
|
||||
if [[ $SSH_COUNT -gt 0 ]]; then
|
||||
echo " 🔄 SSH Deploy: Активен (установка на сервере)"
|
||||
else
|
||||
echo " ⚠️ SSH Deploy: Завершен или прерван"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Проверка доступности домена
|
||||
echo "🌐 Проверка домена:"
|
||||
DOMAIN_STATUS=$(curl -s --connect-timeout 3 -o /dev/null -w "%{http_code}" https://my-public-node-3.projscale.dev/api/my/health 2>/dev/null || echo "TIMEOUT")
|
||||
|
||||
case $DOMAIN_STATUS in
|
||||
"200")
|
||||
echo " ✅ API доступен: https://my-public-node-3.projscale.dev/api/my/health"
|
||||
echo " 🎉 РАЗВЕРТЫВАНИЕ ЗАВЕРШЕНО УСПЕШНО!"
|
||||
;;
|
||||
"521")
|
||||
echo " ⏳ Error 521: Сервер недоступен (установка в процессе)"
|
||||
;;
|
||||
"522")
|
||||
echo " ⏳ Error 522: Connection timeout (сервис запускается)"
|
||||
;;
|
||||
"TIMEOUT")
|
||||
echo " ⏳ Timeout: Проверяем соединение..."
|
||||
;;
|
||||
*)
|
||||
echo " ⚠️ HTTP $DOMAIN_STATUS: Проверяем статус..."
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
|
||||
# Проверка прямого подключения к серверу
|
||||
echo "🔍 Проверка сервера:"
|
||||
SERVER_PING=$(ping -c 1 -W 1000 2.58.65.188 2>/dev/null | grep "1 received" | wc -l | tr -d ' ')
|
||||
if [[ $SERVER_PING -gt 0 ]]; then
|
||||
echo " ✅ Сервер 2.58.65.188 доступен"
|
||||
else
|
||||
echo " ⚠️ Сервер 2.58.65.188 недоступен"
|
||||
fi
|
||||
|
||||
# Попытка проверить порты
|
||||
echo ""
|
||||
echo "🔌 Проверка портов:"
|
||||
|
||||
# Проверяем SSH (22)
|
||||
SSH_CHECK=$(timeout 3 bash -c "</dev/tcp/2.58.65.188/22" 2>/dev/null && echo "open" || echo "closed")
|
||||
echo " SSH (22): $SSH_CHECK"
|
||||
|
||||
# Проверяем HTTP (80)
|
||||
HTTP_CHECK=$(timeout 3 bash -c "</dev/tcp/2.58.65.188/80" 2>/dev/null && echo "open" || echo "closed")
|
||||
echo " HTTP (80): $HTTP_CHECK"
|
||||
|
||||
# Проверяем HTTPS (443)
|
||||
HTTPS_CHECK=$(timeout 3 bash -c "</dev/tcp/2.58.65.188/443" 2>/dev/null && echo "open" || echo "closed")
|
||||
echo " HTTPS (443): $HTTPS_CHECK"
|
||||
|
||||
echo ""
|
||||
echo "📊 Статус развертывания:"
|
||||
|
||||
if [[ $DOMAIN_STATUS == "200" ]]; then
|
||||
echo " 🎯 Статус: ЗАВЕРШЕНО"
|
||||
echo " ✅ MY Network Bootstrap Node готов к работе!"
|
||||
echo ""
|
||||
echo "🔗 Доступные endpoints:"
|
||||
echo " • Health: https://my-public-node-3.projscale.dev/api/my/health"
|
||||
echo " • Node Info: https://my-public-node-3.projscale.dev/api/my/node/info"
|
||||
echo " • Bootstrap: https://my-public-node-3.projscale.dev/api/my/bootstrap/config"
|
||||
echo ""
|
||||
echo "🎉 Развертывание успешно завершено!"
|
||||
break
|
||||
elif [[ $SSH_COUNT -gt 0 ]]; then
|
||||
echo " 🔄 Статус: В ПРОЦЕССЕ"
|
||||
echo " ⏳ Установка компонентов на сервере..."
|
||||
if [[ $HTTPS_CHECK == "open" ]]; then
|
||||
echo " ✅ Порт 443 открыт - nginx работает"
|
||||
fi
|
||||
elif [[ $SCP_COUNT -gt 0 ]]; then
|
||||
echo " 📤 Статус: ПЕРЕДАЧА ФАЙЛОВ"
|
||||
echo " ⏳ Копирование скриптов на сервер..."
|
||||
else
|
||||
echo " ⚠️ Статус: НЕИЗВЕСТНО"
|
||||
echo " 🔍 Проверьте терминалы или перезапустите развертывание"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "💡 Для детального мониторинга:"
|
||||
echo " Terminal 1: SCP процесс"
|
||||
echo " Terminal 2: SSH развертывание"
|
||||
echo ""
|
||||
echo "⏹️ Нажмите Ctrl+C для выхода"
|
||||
echo "🔄 Следующее обновление через 30 секунд..."
|
||||
|
||||
sleep 30
|
||||
done
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Быстрая проверка статуса MY Network на сервере
|
||||
|
||||
DOMAIN="my-public-node-3.projscale.dev"
|
||||
SERVER="2.58.65.188"
|
||||
|
||||
echo "🔍 Проверка статуса MY Network Bootstrap Node"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Проверка подключения к серверу
|
||||
echo "📡 Проверка подключения к серверу $SERVER..."
|
||||
if ping -c 1 $SERVER >/dev/null 2>&1; then
|
||||
echo "✅ Сервер доступен"
|
||||
else
|
||||
echo "❌ Сервер недоступен"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Проверка портов
|
||||
echo ""
|
||||
echo "🔌 Проверка портов на сервере..."
|
||||
for port in 22 80 443; do
|
||||
if nc -z -w3 $SERVER $port 2>/dev/null; then
|
||||
echo "✅ Порт $port: открыт"
|
||||
else
|
||||
echo "❌ Порт $port: закрыт"
|
||||
fi
|
||||
done
|
||||
|
||||
# Проверка домена
|
||||
echo ""
|
||||
echo "🌐 Проверка домена $DOMAIN..."
|
||||
if curl -s -o /dev/null -w "%{http_code}" "https://$DOMAIN" | grep -q "200\|404\|302"; then
|
||||
echo "✅ Домен отвечает"
|
||||
echo "📊 HTTP код: $(curl -s -o /dev/null -w "%{http_code}" "https://$DOMAIN")"
|
||||
else
|
||||
echo "❌ Домен не отвечает (521 - сервер недоступен)"
|
||||
fi
|
||||
|
||||
# Проверка API
|
||||
echo ""
|
||||
echo "🎯 Проверка MY Network API..."
|
||||
API_RESPONSE=$(curl -s -w "%{http_code}" "https://$DOMAIN/api/my/health" -o /tmp/api_response.json)
|
||||
if [[ "$API_RESPONSE" == "200" ]]; then
|
||||
echo "✅ MY Network API работает"
|
||||
echo "📄 Ответ API:"
|
||||
cat /tmp/api_response.json | jq . 2>/dev/null || cat /tmp/api_response.json
|
||||
else
|
||||
echo "❌ MY Network API не работает (код: $API_RESPONSE)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🏁 Проверка завершена"
|
||||
@@ -0,0 +1,351 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network - Защита сервиса (Шаг 2 из 2)
|
||||
# Настройка безопасности, nginx, SSL, firewall
|
||||
# Запускать после install_service.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔒 MY Network - Защита сервиса"
|
||||
echo "==============================="
|
||||
|
||||
# Проверка прав root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Этот скрипт должен запускаться от root"
|
||||
echo "Используйте: sudo bash secure_service.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Проверка конфигурации
|
||||
CONFIG_FILE="/opt/my-network-config.txt"
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Конфигурация не найдена!"
|
||||
echo "Сначала запустите: sudo bash install_service.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Извлечение конфигурации
|
||||
DOMAIN=$(grep "Домен:" $CONFIG_FILE | cut -d' ' -f2)
|
||||
EMAIL=$(grep "Email:" $CONFIG_FILE | cut -d' ' -f2)
|
||||
|
||||
if [[ -z "$DOMAIN" || -z "$EMAIL" ]]; then
|
||||
echo "❌ Не удалось прочитать конфигурацию!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📋 Настройки безопасности:"
|
||||
echo " Домен: $DOMAIN"
|
||||
echo " Email: $EMAIL"
|
||||
echo " SSL: Let's Encrypt"
|
||||
echo " Firewall: UFW"
|
||||
echo ""
|
||||
|
||||
# Установка nginx и certbot
|
||||
echo "🌐 Установка nginx и certbot..."
|
||||
apt install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
# Настройка nginx
|
||||
echo "⚙️ Настройка nginx..."
|
||||
cat > /etc/nginx/sites-available/my-network << EOF
|
||||
# MY Network - nginx configuration
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# SSL Configuration (will be updated by certbot)
|
||||
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
|
||||
# Hide nginx version
|
||||
server_tokens off;
|
||||
|
||||
# Rate limiting
|
||||
limit_req_zone \$binary_remote_addr zone=api:10m rate=10r/s;
|
||||
limit_req_zone \$binary_remote_addr zone=monitor:10m rate=2r/s;
|
||||
|
||||
# Main application
|
||||
location / {
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# Websocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Monitoring interface (restricted)
|
||||
location /api/my/monitor {
|
||||
limit_req zone=monitor burst=5 nodelay;
|
||||
|
||||
# IP whitelist (localhost only by default)
|
||||
allow 127.0.0.1;
|
||||
allow ::1;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Static files
|
||||
location /static/ {
|
||||
alias /opt/my-network/my-uploader-bot/static/;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Block sensitive paths
|
||||
location ~ /\.(?!well-known) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ ^/(config|\.env|requirements|docker-compose) {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Включение сайта
|
||||
ln -sf /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Проверка конфигурации nginx
|
||||
nginx -t
|
||||
|
||||
# Получение SSL сертификата
|
||||
echo "🔐 Получение SSL сертификата..."
|
||||
certbot --nginx -d $DOMAIN --email $EMAIL --agree-tos --non-interactive --redirect
|
||||
|
||||
# Настройка автообновления сертификата
|
||||
echo "🔄 Настройка автообновления SSL..."
|
||||
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
|
||||
|
||||
# Настройка firewall
|
||||
echo "🔥 Настройка firewall..."
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# Разрешение SSH (проверяем текущий порт)
|
||||
SSH_PORT=$(ss -tlnp | grep sshd | grep -o ':[0-9]*' | head -1 | cut -d: -f2)
|
||||
if [[ -n "$SSH_PORT" ]]; then
|
||||
echo "🔑 Разрешение SSH на порту $SSH_PORT..."
|
||||
ufw allow $SSH_PORT/tcp comment 'SSH'
|
||||
fi
|
||||
|
||||
# Разрешение HTTP/HTTPS
|
||||
ufw allow 80/tcp comment 'HTTP'
|
||||
ufw allow 443/tcp comment 'HTTPS'
|
||||
|
||||
# Разрешение внутренних соединений
|
||||
ufw allow from 127.0.0.1 to any port 15100 comment 'MY Network internal'
|
||||
|
||||
# Включение firewall
|
||||
ufw --force enable
|
||||
|
||||
# Дополнительная защита системы
|
||||
echo "🛡️ Дополнительная защита..."
|
||||
|
||||
# Отключение ненужных сервисов
|
||||
systemctl disable --now apache2 2>/dev/null || true
|
||||
systemctl disable --now nginx 2>/dev/null || true
|
||||
|
||||
# Запуск nginx
|
||||
systemctl enable nginx
|
||||
systemctl start nginx
|
||||
|
||||
# Настройка fail2ban
|
||||
echo "🚫 Установка fail2ban..."
|
||||
apt install -y fail2ban
|
||||
|
||||
cat > /etc/fail2ban/jail.local << EOF
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = $SSH_PORT
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
filter = nginx-http-auth
|
||||
logpath = /var/log/nginx/error.log
|
||||
|
||||
[nginx-limit-req]
|
||||
enabled = true
|
||||
filter = nginx-limit-req
|
||||
logpath = /var/log/nginx/error.log
|
||||
maxretry = 3
|
||||
EOF
|
||||
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Настройка logrotate
|
||||
echo "📜 Настройка ротации логов..."
|
||||
cat > /etc/logrotate.d/my-network << EOF
|
||||
/opt/logs/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
EOF
|
||||
|
||||
# Настройка мониторинга
|
||||
echo "📊 Настройка мониторинга..."
|
||||
cat > /opt/monitor.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# MY Network monitoring script
|
||||
|
||||
LOG_FILE="/opt/logs/monitor.log"
|
||||
DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Check service status
|
||||
if systemctl is-active --quiet my-network; then
|
||||
SERVICE_STATUS="OK"
|
||||
else
|
||||
SERVICE_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
# Check nginx status
|
||||
if systemctl is-active --quiet nginx; then
|
||||
NGINX_STATUS="OK"
|
||||
else
|
||||
NGINX_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
DISK_USAGE=$(df -h /opt | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [[ $DISK_USAGE -gt 90 ]]; then
|
||||
DISK_STATUS="CRITICAL"
|
||||
elif [[ $DISK_USAGE -gt 80 ]]; then
|
||||
DISK_STATUS="WARNING"
|
||||
else
|
||||
DISK_STATUS="OK"
|
||||
fi
|
||||
|
||||
# Check memory
|
||||
MEM_USAGE=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
|
||||
if [[ $MEM_USAGE -gt 90 ]]; then
|
||||
MEM_STATUS="CRITICAL"
|
||||
elif [[ $MEM_USAGE -gt 80 ]]; then
|
||||
MEM_STATUS="WARNING"
|
||||
else
|
||||
MEM_STATUS="OK"
|
||||
fi
|
||||
|
||||
# Log status
|
||||
echo "[$DATE] Service: $SERVICE_STATUS, Nginx: $NGINX_STATUS, Disk: $DISK_STATUS ($DISK_USAGE%), Memory: $MEM_STATUS ($MEM_USAGE%)" >> $LOG_FILE
|
||||
|
||||
# Alert if critical
|
||||
if [[ "$SERVICE_STATUS" == "FAIL" || "$NGINX_STATUS" == "FAIL" || "$DISK_STATUS" == "CRITICAL" || "$MEM_STATUS" == "CRITICAL" ]]; then
|
||||
echo "[$DATE] ALERT: Critical status detected!" >> $LOG_FILE
|
||||
# Here you can add email notification or webhook
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /opt/monitor.sh
|
||||
|
||||
# Добавление в cron
|
||||
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/monitor.sh") | crontab -
|
||||
|
||||
# Обновление конфигурации
|
||||
echo "💾 Обновление конфигурации..."
|
||||
cat >> /opt/my-network-config.txt << EOF
|
||||
|
||||
Безопасность настроена:
|
||||
======================
|
||||
SSL: Включен (Let's Encrypt)
|
||||
Firewall: Включен (UFW)
|
||||
Nginx: Включен с rate limiting
|
||||
Fail2ban: Включен
|
||||
Мониторинг: /opt/monitor.sh (каждые 5 мин)
|
||||
|
||||
Проверка безопасности:
|
||||
ufw status
|
||||
systemctl status nginx
|
||||
systemctl status fail2ban
|
||||
certbot certificates
|
||||
|
||||
Логи безопасности:
|
||||
/var/log/nginx/access.log
|
||||
/var/log/nginx/error.log
|
||||
/var/log/fail2ban.log
|
||||
/opt/logs/monitor.log
|
||||
EOF
|
||||
|
||||
# Финальная проверка
|
||||
echo "🔍 Финальная проверка..."
|
||||
sleep 3
|
||||
|
||||
echo "🌐 Проверка nginx..."
|
||||
systemctl status nginx --no-pager -l
|
||||
|
||||
echo "🔥 Статус firewall..."
|
||||
ufw status numbered
|
||||
|
||||
echo "🔐 SSL сертификаты..."
|
||||
certbot certificates
|
||||
|
||||
echo "📡 Проверка доступности..."
|
||||
curl -s -o /dev/null -w "%{http_code}" https://$DOMAIN/api/my/health || echo "Сервис недоступен"
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network защищен!"
|
||||
echo "======================"
|
||||
echo "🌐 Домен: https://$DOMAIN"
|
||||
echo "🔐 SSL: Включен"
|
||||
echo "🔥 Firewall: Включен"
|
||||
echo "🚫 Fail2ban: Включен"
|
||||
echo "📊 Мониторинг: Включен"
|
||||
echo ""
|
||||
echo "🔍 Проверка работы:"
|
||||
echo " curl https://$DOMAIN/api/my/health"
|
||||
echo " systemctl status my-network nginx fail2ban"
|
||||
echo ""
|
||||
echo "📈 Мониторинг:"
|
||||
echo " https://$DOMAIN/api/my/monitor/ (только с localhost)"
|
||||
echo " tail -f /opt/logs/monitor.log"
|
||||
echo ""
|
||||
echo "🛡️ Безопасность:"
|
||||
echo " ufw status"
|
||||
echo " fail2ban-client status"
|
||||
echo " certbot certificates"
|
||||
echo ""
|
||||
echo "📚 Полная документация: /opt/my-network/my-uploader-bot/DOCS_RU.md"
|
||||
@@ -0,0 +1,430 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Minimal Setup - только встроенные модули Python
|
||||
# Без pip, без внешних зависимостей
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network Minimal Setup"
|
||||
echo "==========================="
|
||||
echo "Пользователь: $(whoami)"
|
||||
echo "Директория: $(pwd)"
|
||||
echo ""
|
||||
|
||||
# Переход в my-uploader-bot
|
||||
if [[ -d "my-uploader-bot" ]]; then
|
||||
cd my-uploader-bot
|
||||
echo "✅ Директория my-uploader-bot найдена"
|
||||
else
|
||||
echo "❌ Директория my-uploader-bot не найдена!"
|
||||
echo "Содержимое текущей директории:"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Создание директорий
|
||||
mkdir -p app/api
|
||||
mkdir -p logs
|
||||
|
||||
# Проверка Python
|
||||
echo "🐍 Проверка Python..."
|
||||
python3 --version || { echo "❌ Python3 не найден"; exit 1; }
|
||||
|
||||
# Создание минимального MY Network приложения
|
||||
echo "🏗️ Создание MY Network приложения (только встроенные модули)..."
|
||||
|
||||
cat > app/main.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""MY Network Bootstrap Node - Minimal Version (только встроенные модули)"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import urlparse
|
||||
import threading
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('logs/my-network.log', encoding='utf-8'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger("MY-Network")
|
||||
|
||||
NODE_ID = f"bootstrap-minimal-{int(datetime.now().timestamp())}"
|
||||
DOMAIN = "my-public-node-3.projscale.dev"
|
||||
PORT = 15100
|
||||
|
||||
class MyNetworkHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
try:
|
||||
parsed_path = urlparse(self.path)
|
||||
path = parsed_path.path
|
||||
|
||||
# Маршруты
|
||||
if path == '/':
|
||||
self.send_json_response({"message": "MY Network Bootstrap Node v2.0 - Minimal", "status": "active"})
|
||||
elif path == '/api/my/health':
|
||||
self.send_json_response({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"mode": "minimal",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "monitor"],
|
||||
"version": "2.0.0"
|
||||
})
|
||||
elif path == '/api/my/node/info':
|
||||
self.send_json_response({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "minimal",
|
||||
"mode": "minimal",
|
||||
"services": ["api", "monitor"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
elif path == '/api/my/bootstrap/config':
|
||||
config = {
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": NODE_ID,
|
||||
"host": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "minimal",
|
||||
"mode": "minimal",
|
||||
"services": ["api", "monitor"],
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"max_peers": 5,
|
||||
"sync_interval": 300
|
||||
},
|
||||
"api_endpoints": {
|
||||
"base_url": f"http://{DOMAIN}:{PORT}",
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
self.send_json_response(config)
|
||||
elif path == '/api/my/monitor/' or path == '/api/my/monitor':
|
||||
self.send_html_response(self.get_monitor_html())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
error_response = {"error": "Not Found", "path": path}
|
||||
self.wfile.write(json.dumps(error_response).encode('utf-8'))
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling request: {e}")
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'Internal Server Error')
|
||||
|
||||
def send_json_response(self, data):
|
||||
try:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json; charset=utf-8')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||
self.send_header('X-MY-Network-Version', '2.0')
|
||||
self.send_header('Server', 'MY-Network-Bootstrap/2.0')
|
||||
self.end_headers()
|
||||
json_data = json.dumps(data, indent=2, ensure_ascii=False)
|
||||
self.wfile.write(json_data.encode('utf-8'))
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending JSON response: {e}")
|
||||
|
||||
def send_html_response(self, html):
|
||||
try:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html; charset=utf-8')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('X-MY-Network-Version', '2.0')
|
||||
self.send_header('Server', 'MY-Network-Bootstrap/2.0')
|
||||
self.end_headers()
|
||||
self.wfile.write(html.encode('utf-8'))
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending HTML response: {e}")
|
||||
|
||||
def get_monitor_html(self):
|
||||
uptime = int(time.time() - start_time)
|
||||
uptime_str = f"{uptime // 3600}h {(uptime % 3600) // 60}m {uptime % 60}s"
|
||||
|
||||
return f"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MY Network Bootstrap Monitor - Minimal</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="30">
|
||||
<style>
|
||||
body {{ font-family: 'Courier New', monospace; background: #000; color: #0f0; padding: 20px; margin: 0; }}
|
||||
h1 {{ color: #ff0; text-align: center; text-shadow: 0 0 10px #ff0; }}
|
||||
.box {{ border: 1px solid #0f0; padding: 15px; margin: 10px 0; background: rgba(0,255,0,0.1); }}
|
||||
.status {{ color: #0f0; font-weight: bold; }}
|
||||
.info {{ color: #00f; }}
|
||||
.warning {{ color: #f80; }}
|
||||
.timestamp {{ color: #fff; font-size: 0.8em; }}
|
||||
.ascii-art {{ color: #0f0; font-size: 0.7em; text-align: center; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ascii-art">
|
||||
███╗ ███╗██╗ ██╗ ███╗ ██╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗
|
||||
████╗ ████║╚██╗ ██╔╝ ████╗ ██║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗██║ ██╔╝
|
||||
██╔████╔██║ ╚████╔╝ ██╔██╗ ██║█████╗ ██║ ██║ █╗ ██║██║ ██║██████╔╝█████╔╝
|
||||
██║╚██╔╝██║ ╚██╔╝ ██║╚██╗██║██╔══╝ ██║ ██║███╗██║██║ ██║██╔══██╗██╔═██╗
|
||||
██║ ╚═╝ ██║ ██║ ██║ ╚████║███████╗ ██║ ╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗
|
||||
╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
|
||||
</div>
|
||||
|
||||
<h1>🚀 MY Network Bootstrap Node Monitor</h1>
|
||||
|
||||
<div class="box">
|
||||
<h3 class="status">🟢 Node Status: ACTIVE (Minimal Mode)</h3>
|
||||
<p class="info">🆔 Node ID: {NODE_ID}</p>
|
||||
<p class="info">🌐 Domain: {DOMAIN}</p>
|
||||
<p class="info">🔌 Port: {PORT}</p>
|
||||
<p class="info">📡 Type: Bootstrap Primary</p>
|
||||
<p class="info">🏷️ Version: 2.0.0</p>
|
||||
<p class="info">⚙️ Mode: Minimal HTTP Server</p>
|
||||
<p class="info">🛠️ Services: API, Monitor</p>
|
||||
<p class="info">⏱️ Uptime: {uptime_str}</p>
|
||||
<p class="timestamp">🕐 Last Update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<h3 class="status">🌐 Network Configuration</h3>
|
||||
<p class="info">📋 Protocol Version: 2.0</p>
|
||||
<p class="info">👥 Max Peers: 5</p>
|
||||
<p class="info">🔓 Public Access: Enabled</p>
|
||||
<p class="warning">⚠️ SSL: Disabled (Minimal Mode)</p>
|
||||
<p class="info">🔄 Sync Interval: 300s</p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<h3 class="warning">⚠️ Minimal Mode</h3>
|
||||
<p class="warning">🔧 Сервис запущен в минимальном режиме</p>
|
||||
<p class="warning">📦 Использует только встроенные модули Python</p>
|
||||
<p class="warning">🚫 Без внешних зависимостей</p>
|
||||
<p class="info">🌍 Доступ: http://{DOMAIN}:{PORT}</p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<h3 class="status">🔗 API Endpoints</h3>
|
||||
<p class="info">📊 Health: <a href="/api/my/health" style="color: #0ff;">/api/my/health</a></p>
|
||||
<p class="info">ℹ️ Node Info: <a href="/api/my/node/info" style="color: #0ff;">/api/my/node/info</a></p>
|
||||
<p class="info">🏗️ Bootstrap Config: <a href="/api/my/bootstrap/config" style="color: #0ff;">/api/my/bootstrap/config</a></p>
|
||||
</div>
|
||||
|
||||
<div class="timestamp" style="text-align: center; margin-top: 20px;">
|
||||
⏰ Автообновление каждые 30 секунд
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
def log_message(self, format, *args):
|
||||
client_ip = self.address_string()
|
||||
message = format % args
|
||||
logger.info(f"{client_ip} - {message}")
|
||||
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(200)
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||
self.end_headers()
|
||||
|
||||
# Глобальная переменная для времени запуска
|
||||
start_time = time.time()
|
||||
|
||||
def start_server():
|
||||
global start_time
|
||||
start_time = time.time()
|
||||
|
||||
logger.info("=" * 50)
|
||||
logger.info("🚀 MY Network Bootstrap Node - Minimal Mode")
|
||||
logger.info("=" * 50)
|
||||
logger.info(f"🌐 Domain: {DOMAIN}")
|
||||
logger.info(f"🔌 Port: {PORT}")
|
||||
logger.info(f"🆔 Node ID: {NODE_ID}")
|
||||
logger.info(f"⚙️ Mode: Minimal HTTP Server")
|
||||
logger.info(f"🌍 Access: http://0.0.0.0:{PORT}")
|
||||
logger.info(f"📊 Health: http://0.0.0.0:{PORT}/api/my/health")
|
||||
logger.info(f"🖥️ Monitor: http://0.0.0.0:{PORT}/api/my/monitor/")
|
||||
logger.info("=" * 50)
|
||||
|
||||
try:
|
||||
# Создание HTTP сервера
|
||||
server_address = ("0.0.0.0", PORT)
|
||||
httpd = HTTPServer(server_address, MyNetworkHandler)
|
||||
|
||||
logger.info(f"✅ Server started successfully")
|
||||
logger.info(f"🎯 MY Network Bootstrap Node is ready!")
|
||||
|
||||
# Запуск сервера
|
||||
httpd.serve_forever()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("🛑 Server stopped by user")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Server error: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
start_server()
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Stopping server...")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"❌ Fatal error: {e}")
|
||||
sys.exit(1)
|
||||
EOF
|
||||
|
||||
# Создание скрипта запуска
|
||||
cat > start_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
cd /home/service/my-uploader-bot
|
||||
|
||||
echo "🚀 Запуск MY Network Bootstrap Node (Minimal Mode)"
|
||||
echo "==============================================="
|
||||
echo ""
|
||||
|
||||
# Создание директории для логов
|
||||
mkdir -p logs
|
||||
|
||||
# Запуск приложения
|
||||
echo "▶️ Запуск сервера..."
|
||||
python3 app/main.py
|
||||
EOF
|
||||
|
||||
chmod +x start_my_network.sh
|
||||
|
||||
# Создание скрипта для фонового запуска
|
||||
cat > run_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Проверка, запущен ли MY Network
|
||||
if ! pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
echo "$(date): Запуск MY Network" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
cd /home/service/my-uploader-bot
|
||||
nohup python3 app/main.py >> logs/app.log 2>&1 &
|
||||
PID=$!
|
||||
echo "$(date): MY Network запущен (PID: $PID)" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
echo "MY Network запущен (PID: $PID)"
|
||||
else
|
||||
echo "$(date): MY Network уже запущен" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
echo "MY Network уже запущен"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x run_my_network.sh
|
||||
|
||||
# Создание скрипта остановки
|
||||
cat > stop_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
echo "🛑 Остановка MY Network..."
|
||||
if pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
pkill -f "python3 app/main.py"
|
||||
echo "✅ MY Network остановлен"
|
||||
echo "$(date): MY Network остановлен" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
else
|
||||
echo "⚠️ MY Network не запущен"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x stop_my_network.sh
|
||||
|
||||
# Создание скрипта проверки статуса
|
||||
cat > status_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
echo "📊 Статус MY Network Bootstrap Node"
|
||||
echo "================================="
|
||||
|
||||
if pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
PID=$(pgrep -f "python3 app/main.py")
|
||||
echo "✅ MY Network запущен (PID: $PID)"
|
||||
echo "🌐 Доступ: http://my-public-node-3.projscale.dev:15100"
|
||||
echo "📊 Health: http://my-public-node-3.projscale.dev:15100/api/my/health"
|
||||
echo "🖥️ Monitor: http://my-public-node-3.projscale.dev:15100/api/my/monitor/"
|
||||
|
||||
echo ""
|
||||
echo "📈 Процесс:"
|
||||
ps aux | grep "python3 app/main.py" | grep -v grep
|
||||
|
||||
echo ""
|
||||
echo "🔌 Порт 15100:"
|
||||
netstat -tulpn 2>/dev/null | grep :15100 || echo "netstat недоступен"
|
||||
else
|
||||
echo "❌ MY Network не запущен"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 Последние логи:"
|
||||
if [[ -f logs/my-network.log ]]; then
|
||||
tail -5 logs/my-network.log
|
||||
else
|
||||
echo "Лог файл не найден"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x status_my_network.sh
|
||||
|
||||
# Добавление в crontab
|
||||
echo "📅 Настройка автозапуска..."
|
||||
(crontab -l 2>/dev/null | grep -v "run_my_network.sh"; echo "*/5 * * * * /home/service/my-uploader-bot/run_my_network.sh") | crontab -
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node установлен!"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
echo "🚀 Команды управления:"
|
||||
echo " cd /home/service/my-uploader-bot"
|
||||
echo " ./start_my_network.sh # Запуск в терминале"
|
||||
echo " ./run_my_network.sh # Запуск в фоне"
|
||||
echo " ./stop_my_network.sh # Остановка"
|
||||
echo " ./status_my_network.sh # Проверка статуса"
|
||||
echo ""
|
||||
echo "🌐 Доступ к сервису:"
|
||||
echo " http://my-public-node-3.projscale.dev:15100"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/health"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/monitor/"
|
||||
echo ""
|
||||
echo "📝 Логи:"
|
||||
echo " tail -f logs/my-network.log # Основной лог"
|
||||
echo " tail -f logs/app.log # Лог приложения"
|
||||
echo " tail -f logs/cron.log # Лог cron"
|
||||
echo ""
|
||||
echo "⚡ Автозапуск настроен через cron (каждые 5 минут)"
|
||||
echo ""
|
||||
echo "🎯 MY Network Bootstrap Node готов к работе!"
|
||||
echo ""
|
||||
echo "▶️ Запустить сейчас? Выполните:"
|
||||
echo " ./run_my_network.sh"
|
||||
@@ -0,0 +1,331 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Nginx + SSL Setup
|
||||
# Настройка nginx с SSL сертификатами через certbot
|
||||
|
||||
set -e
|
||||
|
||||
DOMAIN="my-public-node-3.projscale.dev"
|
||||
EMAIL="admin@projscale.dev"
|
||||
SERVICE_PORT="15100"
|
||||
|
||||
echo "🔒 MY Network Nginx + SSL Setup"
|
||||
echo "==============================="
|
||||
echo "Домен: $DOMAIN"
|
||||
echo "Email: $EMAIL"
|
||||
echo "Сервис порт: $SERVICE_PORT"
|
||||
echo ""
|
||||
|
||||
# Проверка root прав
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Запустите от root: sudo bash setup_nginx_ssl.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Обновление системы
|
||||
echo "🔄 Обновление системы..."
|
||||
apt update
|
||||
|
||||
# Установка необходимых пакетов
|
||||
echo "📦 Установка nginx, certbot, ufw..."
|
||||
apt install -y nginx certbot python3-certbot-nginx ufw curl
|
||||
|
||||
# Остановка nginx для получения сертификата
|
||||
echo "🛑 Остановка nginx..."
|
||||
systemctl stop nginx || true
|
||||
|
||||
# Настройка firewall
|
||||
echo "🔥 Настройка firewall..."
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# SSH
|
||||
ufw allow 22/tcp comment 'SSH'
|
||||
|
||||
# HTTP/HTTPS
|
||||
ufw allow 80/tcp comment 'HTTP'
|
||||
ufw allow 443/tcp comment 'HTTPS'
|
||||
|
||||
# Внутренний порт сервиса (только локально)
|
||||
ufw allow from 127.0.0.1 to any port $SERVICE_PORT comment 'MY Network Local'
|
||||
|
||||
ufw --force enable
|
||||
|
||||
# Получение SSL сертификата
|
||||
echo "🔐 Получение SSL сертификата..."
|
||||
certbot certonly \
|
||||
--standalone \
|
||||
--non-interactive \
|
||||
--agree-tos \
|
||||
--email $EMAIL \
|
||||
-d $DOMAIN
|
||||
|
||||
# Проверка что сертификат получен
|
||||
if [[ ! -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ]]; then
|
||||
echo "❌ Не удалось получить SSL сертификат!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ SSL сертификат получен"
|
||||
|
||||
# Создание конфигурации nginx
|
||||
echo "🌐 Настройка nginx..."
|
||||
|
||||
# Удаление дефолтного сайта
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Создание конфигурации MY Network
|
||||
cat > /etc/nginx/sites-available/my-network << EOF
|
||||
# MY Network Bootstrap Node - Nginx Configuration
|
||||
# HTTP -> HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# Certbot renewal
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
# Redirect all HTTP to HTTPS
|
||||
location / {
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
||||
|
||||
# SSL Security
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Security headers
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header X-MY-Network-Version "2.0" always;
|
||||
add_header X-MY-Network-Node "bootstrap" always;
|
||||
|
||||
# Hide nginx version
|
||||
server_tokens off;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
# Rate limiting
|
||||
limit_req_zone \$binary_remote_addr zone=api:10m rate=10r/s;
|
||||
limit_req_zone \$binary_remote_addr zone=monitor:10m rate=1r/s;
|
||||
|
||||
# Main application proxy
|
||||
location / {
|
||||
# Rate limiting
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:$SERVICE_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;
|
||||
proxy_set_header X-Forwarded-Host \$host;
|
||||
proxy_set_header X-Forwarded-Port \$server_port;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 10s;
|
||||
proxy_read_timeout 10s;
|
||||
|
||||
# Buffering
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
# CORS headers
|
||||
add_header Access-Control-Allow-Origin "*" always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
|
||||
|
||||
# Handle preflight requests
|
||||
if (\$request_method = 'OPTIONS') {
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
add_header Access-Control-Max-Age 86400;
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# Monitor endpoint with stricter rate limiting
|
||||
location /api/my/monitor/ {
|
||||
limit_req zone=monitor burst=5 nodelay;
|
||||
|
||||
proxy_pass http://127.0.0.1:$SERVICE_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;
|
||||
|
||||
# Cache static content
|
||||
expires 30s;
|
||||
add_header Cache-Control "public, no-transform";
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /api/my/health {
|
||||
proxy_pass http://127.0.0.1:$SERVICE_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;
|
||||
|
||||
# No caching for health checks
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# Block access to sensitive files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Custom error pages
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
location = /404.html {
|
||||
return 404 '{"error": "Not Found", "message": "MY Network endpoint not found"}';
|
||||
add_header Content-Type application/json;
|
||||
}
|
||||
|
||||
location = /50x.html {
|
||||
return 500 '{"error": "Server Error", "message": "MY Network temporarily unavailable"}';
|
||||
add_header Content-Type application/json;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Активация сайта
|
||||
ln -sf /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
|
||||
|
||||
# Проверка конфигурации nginx
|
||||
echo "🔍 Проверка конфигурации nginx..."
|
||||
nginx -t
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "❌ Ошибка в конфигурации nginx!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Настройка автообновления сертификатов
|
||||
echo "🔄 Настройка автообновления сертификатов..."
|
||||
cat > /etc/cron.d/certbot-renew << EOF
|
||||
# Автообновление SSL сертификатов MY Network
|
||||
0 12 * * * root certbot renew --quiet --post-hook "systemctl reload nginx"
|
||||
EOF
|
||||
|
||||
# Запуск nginx
|
||||
echo "🚀 Запуск nginx..."
|
||||
systemctl enable nginx
|
||||
systemctl start nginx
|
||||
|
||||
# Проверка статуса
|
||||
sleep 2
|
||||
if systemctl is-active --quiet nginx; then
|
||||
echo "✅ Nginx запущен успешно"
|
||||
else
|
||||
echo "❌ Ошибка запуска nginx"
|
||||
systemctl status nginx
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Проверка MY Network сервиса
|
||||
echo "🔍 Проверка MY Network сервиса..."
|
||||
if pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
echo "✅ MY Network сервис работает"
|
||||
else
|
||||
echo "⚠️ MY Network сервис не запущен, запускаем..."
|
||||
cd /home/service/my-uploader-bot
|
||||
sudo -u service ./run_my_network.sh
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Финальная проверка
|
||||
echo "🎯 Финальная проверка..."
|
||||
echo ""
|
||||
|
||||
# Проверка HTTP -> HTTPS redirect
|
||||
echo "📡 Проверка HTTP redirect..."
|
||||
HTTP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://$DOMAIN/ || echo "000")
|
||||
if [[ "$HTTP_RESPONSE" == "301" ]]; then
|
||||
echo "✅ HTTP -> HTTPS redirect работает"
|
||||
else
|
||||
echo "⚠️ HTTP redirect: $HTTP_RESPONSE"
|
||||
fi
|
||||
|
||||
# Проверка HTTPS
|
||||
echo "🔒 Проверка HTTPS..."
|
||||
HTTPS_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://$DOMAIN/api/my/health || echo "000")
|
||||
if [[ "$HTTPS_RESPONSE" == "200" ]]; then
|
||||
echo "✅ HTTPS работает"
|
||||
else
|
||||
echo "❌ HTTPS не работает: $HTTPS_RESPONSE"
|
||||
fi
|
||||
|
||||
# Проверка SSL сертификата
|
||||
echo "🔐 Проверка SSL сертификата..."
|
||||
SSL_INFO=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -dates 2>/dev/null || echo "SSL check failed")
|
||||
if [[ "$SSL_INFO" != "SSL check failed" ]]; then
|
||||
echo "✅ SSL сертификат валиден"
|
||||
echo "$SSL_INFO"
|
||||
else
|
||||
echo "⚠️ Не удалось проверить SSL сертификат"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 MY Network Bootstrap Node с SSL настроен!"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo "🌐 Доступ:"
|
||||
echo " https://$DOMAIN"
|
||||
echo " https://$DOMAIN/api/my/health"
|
||||
echo " https://$DOMAIN/api/my/bootstrap/config"
|
||||
echo " https://$DOMAIN/api/my/monitor/"
|
||||
echo ""
|
||||
echo "🔒 Безопасность:"
|
||||
echo " ✅ SSL/TLS сертификат от Let's Encrypt"
|
||||
echo " ✅ HTTP -> HTTPS redirect"
|
||||
echo " ✅ Security headers"
|
||||
echo " ✅ Rate limiting"
|
||||
echo " ✅ Firewall (UFW)"
|
||||
echo ""
|
||||
echo "🔄 Автообновление:"
|
||||
echo " ✅ SSL сертификаты (cron)"
|
||||
echo " ✅ MY Network сервис (cron)"
|
||||
echo ""
|
||||
echo "📊 Мониторинг:"
|
||||
echo " systemctl status nginx"
|
||||
echo " systemctl status my-network"
|
||||
echo " tail -f /var/log/nginx/access.log"
|
||||
echo " tail -f /var/log/nginx/error.log"
|
||||
echo ""
|
||||
echo "🎯 MY Network Bootstrap Node полностью готов!"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,320 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Direct Setup - для работы с файлами в /home/service
|
||||
# Упрощенная версия без лишних проверок
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network Direct Setup"
|
||||
echo "========================="
|
||||
echo "Работаем с файлами в /home/service"
|
||||
echo "Домен: my-public-node-3.projscale.dev"
|
||||
echo ""
|
||||
|
||||
# Проверка что мы root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "❌ Запустите от root: sudo bash setup_server_direct.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOMAIN="my-public-node-3.projscale.dev"
|
||||
EMAIL="admin@projscale.dev"
|
||||
|
||||
# Переход в директорию проекта
|
||||
cd /home/service
|
||||
|
||||
echo "📁 Содержимое /home/service:"
|
||||
ls -la
|
||||
|
||||
# Обновление системы быстро
|
||||
echo "🔄 Быстрое обновление системы..."
|
||||
apt update
|
||||
|
||||
# Установка только необходимого
|
||||
echo "📦 Установка базовых пакетов..."
|
||||
apt install -y \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
nginx \
|
||||
ufw \
|
||||
curl \
|
||||
wget
|
||||
|
||||
# Переход в my-uploader-bot если есть
|
||||
if [[ -d "my-uploader-bot" ]]; then
|
||||
cd my-uploader-bot
|
||||
echo "✅ Найдена директория my-uploader-bot"
|
||||
else
|
||||
echo "❌ Директория my-uploader-bot не найдена!"
|
||||
echo "Содержимое /home/service:"
|
||||
ls -la /home/service/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Создание Python venv
|
||||
echo "🐍 Создание Python окружения..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Установка базовых пакетов Python
|
||||
echo "📦 Установка Python пакетов..."
|
||||
pip install --upgrade pip
|
||||
pip install fastapi uvicorn sanic requests python-dotenv
|
||||
|
||||
# Создание минимального приложения MY Network
|
||||
echo "🏗️ Создание минимального MY Network приложения..."
|
||||
|
||||
mkdir -p app/api
|
||||
|
||||
# Простое FastAPI приложение
|
||||
cat > app/main.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""MY Network Bootstrap Node - Minimal Version"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse, HTMLResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
|
||||
app = FastAPI(
|
||||
title="MY Network Bootstrap Node",
|
||||
description="MY Network v2.0 Bootstrap Node - Minimal",
|
||||
version="2.0.0"
|
||||
)
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
NODE_ID = f"bootstrap-{int(datetime.now().timestamp())}"
|
||||
DOMAIN = "my-public-node-3.projscale.dev"
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "MY Network Bootstrap Node v2.0", "status": "active"}
|
||||
|
||||
@app.get("/api/my/health")
|
||||
async def health_check():
|
||||
return JSONResponse({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "monitor"],
|
||||
"version": "2.0.0"
|
||||
})
|
||||
|
||||
@app.get("/api/my/node/info")
|
||||
async def node_info():
|
||||
return JSONResponse({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": 443,
|
||||
"ssl": True,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "monitor"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
|
||||
@app.get("/api/my/bootstrap/config")
|
||||
async def bootstrap_config():
|
||||
config = {
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": NODE_ID,
|
||||
"host": DOMAIN,
|
||||
"port": 443,
|
||||
"ssl": True,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "high",
|
||||
"services": ["api", "monitor"],
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"max_peers": 100,
|
||||
"sync_interval": 300
|
||||
},
|
||||
"api_endpoints": {
|
||||
"base_url": f"https://{DOMAIN}",
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
return JSONResponse(config)
|
||||
|
||||
@app.get("/api/my/monitor/")
|
||||
async def monitor_dashboard():
|
||||
html = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MY Network Bootstrap Monitor</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {{ font-family: 'Courier New', monospace; background: #000; color: #0f0; padding: 20px; }}
|
||||
h1 {{ color: #ff0; text-align: center; }}
|
||||
.box {{ border: 1px solid #0f0; padding: 15px; margin: 10px 0; }}
|
||||
.status {{ color: #0f0; }}
|
||||
.info {{ color: #00f; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚀 MY Network Bootstrap Node Monitor</h1>
|
||||
<div class="box">
|
||||
<h3 class="status">Node Status: ACTIVE</h3>
|
||||
<p class="info">Node ID: {NODE_ID}</p>
|
||||
<p class="info">Domain: {DOMAIN}</p>
|
||||
<p class="info">Type: Bootstrap Primary</p>
|
||||
<p class="info">Version: 2.0.0</p>
|
||||
<p class="info">Services: API, Monitor</p>
|
||||
<p class="info">Last Update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="status">Network Configuration</h3>
|
||||
<p class="info">Protocol Version: 2.0</p>
|
||||
<p class="info">Max Peers: 100</p>
|
||||
<p class="info">Public Access: Enabled</p>
|
||||
<p class="info">SSL: Required</p>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => location.reload(), 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"🚀 Starting MY Network Bootstrap Node")
|
||||
print(f"Domain: {DOMAIN}")
|
||||
print(f"Node ID: {NODE_ID}")
|
||||
uvicorn.run(app, host="0.0.0.0", port=15100)
|
||||
EOF
|
||||
|
||||
# Создание systemd сервиса
|
||||
echo "⚙️ Создание systemd сервиса..."
|
||||
cat > /etc/systemd/system/my-network.service << EOF
|
||||
[Unit]
|
||||
Description=MY Network Bootstrap Node
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=service
|
||||
Group=service
|
||||
WorkingDirectory=/home/service/my-uploader-bot
|
||||
Environment=PATH=/home/service/my-uploader-bot/venv/bin
|
||||
ExecStart=/home/service/my-uploader-bot/venv/bin/python app/main.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Права доступа
|
||||
chown -R service:service /home/service/
|
||||
|
||||
# Настройка nginx
|
||||
echo "🌐 Настройка nginx..."
|
||||
cat > /etc/nginx/sites-available/my-network << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name $DOMAIN;
|
||||
|
||||
# Временный самоподписанный сертификат
|
||||
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
|
||||
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-MY-Network-Version "2.0";
|
||||
|
||||
server_tokens off;
|
||||
|
||||
# Main application
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:15100;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# CORS
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Активация сайта
|
||||
ln -sf /etc/nginx/sites-available/my-network /etc/nginx/sites-enabled/
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Проверка nginx
|
||||
nginx -t
|
||||
|
||||
# Простой firewall
|
||||
echo "🔥 Настройка firewall..."
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
|
||||
# SSH
|
||||
ufw allow 22/tcp comment 'SSH'
|
||||
|
||||
# HTTP/HTTPS
|
||||
ufw allow 80/tcp comment 'HTTP'
|
||||
ufw allow 443/tcp comment 'HTTPS'
|
||||
|
||||
ufw --force enable
|
||||
|
||||
# Запуск сервисов
|
||||
echo "🚀 Запуск сервисов..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable my-network
|
||||
systemctl start my-network
|
||||
systemctl enable nginx
|
||||
systemctl start nginx
|
||||
|
||||
# Проверка
|
||||
sleep 5
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node запущен!"
|
||||
echo "=================================="
|
||||
echo "🌐 Домен: https://$DOMAIN"
|
||||
echo "🔌 Локальный порт: 15100"
|
||||
echo ""
|
||||
echo "🔍 Проверка:"
|
||||
echo "systemctl status my-network"
|
||||
echo "curl http://localhost:15100/api/my/health"
|
||||
echo ""
|
||||
echo "🎯 Bootstrap узел готов к работе!"
|
||||
@@ -0,0 +1,318 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network Simple Setup - без venv, прямо с системным Python
|
||||
# Самый простой способ запуска
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network Simple Setup"
|
||||
echo "=========================="
|
||||
echo "Пользователь: $(whoami)"
|
||||
echo "Директория: $(pwd)"
|
||||
echo ""
|
||||
|
||||
# Переход в my-uploader-bot
|
||||
if [[ -d "my-uploader-bot" ]]; then
|
||||
cd my-uploader-bot
|
||||
echo "✅ Директория my-uploader-bot найдена"
|
||||
else
|
||||
echo "❌ Директория my-uploader-bot не найдена!"
|
||||
echo "Содержимое текущей директории:"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Создание директорий
|
||||
mkdir -p app/api
|
||||
mkdir -p logs
|
||||
|
||||
# Проверка Python
|
||||
echo "🐍 Проверка Python..."
|
||||
python3 --version || { echo "❌ Python3 не найден"; exit 1; }
|
||||
|
||||
# Установка пакетов через pip --user
|
||||
echo "📦 Установка Python пакетов (--user)..."
|
||||
python3 -m pip install --user fastapi uvicorn requests python-dotenv
|
||||
|
||||
# Создание упрощенного MY Network приложения
|
||||
echo "🏗️ Создание MY Network приложения..."
|
||||
|
||||
cat > app/main.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""MY Network Bootstrap Node - Simple Version"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any
|
||||
|
||||
# Простой HTTP сервер без FastAPI
|
||||
try:
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
import socketserver
|
||||
import threading
|
||||
USE_SIMPLE_SERVER = True
|
||||
except ImportError:
|
||||
USE_SIMPLE_SERVER = True
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('logs/my-network.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger("MY-Network")
|
||||
|
||||
NODE_ID = f"bootstrap-simple-{int(datetime.now().timestamp())}"
|
||||
DOMAIN = "my-public-node-3.projscale.dev"
|
||||
PORT = 15100
|
||||
|
||||
class MyNetworkHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
parsed_path = urlparse(self.path)
|
||||
path = parsed_path.path
|
||||
|
||||
# Маршруты
|
||||
if path == '/':
|
||||
self.send_json_response({"message": "MY Network Bootstrap Node v2.0 - Simple", "status": "active"})
|
||||
elif path == '/api/my/health':
|
||||
self.send_json_response({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"mode": "simple",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "monitor"],
|
||||
"version": "2.0.0"
|
||||
})
|
||||
elif path == '/api/my/node/info':
|
||||
self.send_json_response({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "low",
|
||||
"mode": "simple",
|
||||
"services": ["api", "monitor"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
elif path == '/api/my/bootstrap/config':
|
||||
config = {
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": NODE_ID,
|
||||
"host": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "low",
|
||||
"mode": "simple",
|
||||
"services": ["api", "monitor"],
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"max_peers": 10,
|
||||
"sync_interval": 300
|
||||
},
|
||||
"api_endpoints": {
|
||||
"base_url": f"http://{DOMAIN}:{PORT}",
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
self.send_json_response(config)
|
||||
elif path == '/api/my/monitor/' or path == '/api/my/monitor':
|
||||
self.send_html_response(self.get_monitor_html())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'Not Found')
|
||||
|
||||
def send_json_response(self, data: Dict[Any, Any]):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||
self.send_header('X-MY-Network-Version', '2.0')
|
||||
self.end_headers()
|
||||
json_data = json.dumps(data, indent=2)
|
||||
self.wfile.write(json_data.encode('utf-8'))
|
||||
|
||||
def send_html_response(self, html: str):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html; charset=utf-8')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('X-MY-Network-Version', '2.0')
|
||||
self.end_headers()
|
||||
self.wfile.write(html.encode('utf-8'))
|
||||
|
||||
def get_monitor_html(self) -> str:
|
||||
return f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MY Network Bootstrap Monitor - Simple</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {{ font-family: 'Courier New', monospace; background: #000; color: #0f0; padding: 20px; }}
|
||||
h1 {{ color: #ff0; text-align: center; }}
|
||||
.box {{ border: 1px solid #0f0; padding: 15px; margin: 10px 0; }}
|
||||
.status {{ color: #0f0; }}
|
||||
.info {{ color: #00f; }}
|
||||
.warning {{ color: #f80; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚀 MY Network Bootstrap Node Monitor</h1>
|
||||
<div class="box">
|
||||
<h3 class="status">Node Status: ACTIVE (Simple Mode)</h3>
|
||||
<p class="info">Node ID: {NODE_ID}</p>
|
||||
<p class="info">Domain: {DOMAIN}</p>
|
||||
<p class="info">Port: {PORT}</p>
|
||||
<p class="info">Type: Bootstrap Primary</p>
|
||||
<p class="info">Version: 2.0.0</p>
|
||||
<p class="info">Mode: Simple HTTP Server</p>
|
||||
<p class="info">Services: API, Monitor</p>
|
||||
<p class="info">Last Update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="status">Network Configuration</h3>
|
||||
<p class="info">Protocol Version: 2.0</p>
|
||||
<p class="info">Max Peers: 10</p>
|
||||
<p class="info">Public Access: Enabled</p>
|
||||
<p class="warning">SSL: Disabled (Simple Mode)</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="warning">⚠️ Simple Mode</h3>
|
||||
<p class="warning">Сервис запущен в упрощенном режиме</p>
|
||||
<p class="warning">Использует встроенный HTTP сервер Python</p>
|
||||
<p class="info">Доступ: http://{DOMAIN}:{PORT}</p>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => location.reload(), 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
def log_message(self, format, *args):
|
||||
logger.info(f"{self.address_string()} - {format % args}")
|
||||
|
||||
def start_server():
|
||||
logger.info(f"🚀 Starting MY Network Bootstrap Node - Simple Mode")
|
||||
logger.info(f"Domain: {DOMAIN}")
|
||||
logger.info(f"Port: {PORT}")
|
||||
logger.info(f"Node ID: {NODE_ID}")
|
||||
logger.info(f"Mode: Simple HTTP Server")
|
||||
logger.info(f"Access: http://{DOMAIN}:{PORT}")
|
||||
|
||||
try:
|
||||
with HTTPServer(("0.0.0.0", PORT), MyNetworkHandler) as httpd:
|
||||
logger.info(f"✅ Server started on http://0.0.0.0:{PORT}")
|
||||
httpd.serve_forever()
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Server error: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_server()
|
||||
EOF
|
||||
|
||||
# Создание скрипта запуска
|
||||
cat > start_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
cd /home/service/my-uploader-bot
|
||||
|
||||
echo "🚀 Запуск MY Network Bootstrap Node (Simple Mode)"
|
||||
echo "==============================================="
|
||||
echo ""
|
||||
|
||||
# Создание директории для логов
|
||||
mkdir -p logs
|
||||
|
||||
# Запуск приложения
|
||||
python3 app/main.py
|
||||
EOF
|
||||
|
||||
chmod +x start_my_network.sh
|
||||
|
||||
# Создание скрипта для фонового запуска
|
||||
cat > run_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Проверка, запущен ли MY Network
|
||||
if ! pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
echo "$(date): Запуск MY Network" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
cd /home/service/my-uploader-bot
|
||||
nohup python3 app/main.py >> logs/app.log 2>&1 &
|
||||
echo "$(date): MY Network запущен (PID: $!)" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
else
|
||||
echo "$(date): MY Network уже запущен" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x run_my_network.sh
|
||||
|
||||
# Создание скрипта остановки
|
||||
cat > stop_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
echo "🛑 Остановка MY Network..."
|
||||
if pgrep -f "python3 app/main.py" > /dev/null; then
|
||||
pkill -f "python3 app/main.py"
|
||||
echo "✅ MY Network остановлен"
|
||||
echo "$(date): MY Network остановлен" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
else
|
||||
echo "⚠️ MY Network не запущен"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x stop_my_network.sh
|
||||
|
||||
# Добавление в crontab
|
||||
echo "📅 Добавление в crontab..."
|
||||
(crontab -l 2>/dev/null | grep -v "run_my_network.sh"; echo "*/5 * * * * /home/service/my-uploader-bot/run_my_network.sh") | crontab -
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node установлен!"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
echo "🚀 Запуск:"
|
||||
echo " cd /home/service/my-uploader-bot"
|
||||
echo " ./start_my_network.sh # В терминале"
|
||||
echo " ./run_my_network.sh # В фоне"
|
||||
echo " ./stop_my_network.sh # Остановка"
|
||||
echo ""
|
||||
echo "🌐 Доступ:"
|
||||
echo " http://my-public-node-3.projscale.dev:15100"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/health"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/monitor/"
|
||||
echo ""
|
||||
echo "📝 Логи:"
|
||||
echo " tail -f logs/my-network.log"
|
||||
echo " tail -f logs/app.log"
|
||||
echo " tail -f logs/cron.log"
|
||||
echo ""
|
||||
echo "⚡ Автозапуск настроен через cron (каждые 5 минут)"
|
||||
echo ""
|
||||
echo "🎯 MY Network Bootstrap Node готов к работе!"
|
||||
@@ -0,0 +1,291 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MY Network User Setup - без sudo, только для пользователя service
|
||||
# Запуск MY Network в пользовательском пространстве
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 MY Network User Setup (без sudo)"
|
||||
echo "==================================="
|
||||
echo "Пользователь: $(whoami)"
|
||||
echo "Директория: $(pwd)"
|
||||
echo ""
|
||||
|
||||
# Переход в my-uploader-bot
|
||||
if [[ -d "my-uploader-bot" ]]; then
|
||||
cd my-uploader-bot
|
||||
echo "✅ Директория my-uploader-bot найдена"
|
||||
else
|
||||
echo "❌ Директория my-uploader-bot не найдена!"
|
||||
echo "Содержимое текущей директории:"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Создание Python venv
|
||||
echo "🐍 Создание Python окружения..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Обновление pip
|
||||
echo "📦 Обновление pip..."
|
||||
pip install --upgrade pip
|
||||
|
||||
# Установка базовых пакетов
|
||||
echo "📦 Установка Python пакетов..."
|
||||
pip install fastapi uvicorn sanic requests python-dotenv
|
||||
|
||||
# Создание директорий
|
||||
mkdir -p app/api
|
||||
mkdir -p logs
|
||||
|
||||
# Создание упрощенного MY Network приложения
|
||||
echo "🏗️ Создание MY Network приложения..."
|
||||
|
||||
cat > app/main.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""MY Network Bootstrap Node - User Space Version"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse, HTMLResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
import logging
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('logs/my-network.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger("MY-Network")
|
||||
|
||||
app = FastAPI(
|
||||
title="MY Network Bootstrap Node",
|
||||
description="MY Network v2.0 Bootstrap Node - User Space",
|
||||
version="2.0.0"
|
||||
)
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
NODE_ID = f"bootstrap-user-{int(datetime.now().timestamp())}"
|
||||
DOMAIN = "my-public-node-3.projscale.dev"
|
||||
PORT = 15100
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
logger.info(f"🚀 MY Network Bootstrap Node запущен")
|
||||
logger.info(f"Node ID: {NODE_ID}")
|
||||
logger.info(f"Domain: {DOMAIN}")
|
||||
logger.info(f"Port: {PORT}")
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "MY Network Bootstrap Node v2.0 - User Space", "status": "active"}
|
||||
|
||||
@app.get("/api/my/health")
|
||||
async def health_check():
|
||||
return JSONResponse({
|
||||
"status": "healthy",
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"mode": "user-space",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"services": ["api", "monitor"],
|
||||
"version": "2.0.0"
|
||||
})
|
||||
|
||||
@app.get("/api/my/node/info")
|
||||
async def node_info():
|
||||
return JSONResponse({
|
||||
"node_id": NODE_ID,
|
||||
"node_type": "bootstrap",
|
||||
"domain": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "medium",
|
||||
"mode": "user-space",
|
||||
"services": ["api", "monitor"],
|
||||
"protocol_version": "2.0",
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
})
|
||||
|
||||
@app.get("/api/my/bootstrap/config")
|
||||
async def bootstrap_config():
|
||||
config = {
|
||||
"version": "2.0",
|
||||
"network_id": "my-network-main",
|
||||
"bootstrap_nodes": [
|
||||
{
|
||||
"id": NODE_ID,
|
||||
"host": DOMAIN,
|
||||
"port": PORT,
|
||||
"ssl": False,
|
||||
"public": True,
|
||||
"region": "eu-central",
|
||||
"capacity": "medium",
|
||||
"mode": "user-space",
|
||||
"services": ["api", "monitor"],
|
||||
"last_seen": datetime.utcnow().isoformat()
|
||||
}
|
||||
],
|
||||
"network_config": {
|
||||
"protocol_version": "2.0",
|
||||
"max_peers": 50,
|
||||
"sync_interval": 300
|
||||
},
|
||||
"api_endpoints": {
|
||||
"base_url": f"http://{DOMAIN}:{PORT}",
|
||||
"health": "/api/my/health",
|
||||
"node_info": "/api/my/node/info",
|
||||
"bootstrap": "/api/my/bootstrap/config"
|
||||
}
|
||||
}
|
||||
return JSONResponse(config)
|
||||
|
||||
@app.get("/api/my/monitor/")
|
||||
async def monitor_dashboard():
|
||||
html = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MY Network Bootstrap Monitor - User Space</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {{ font-family: 'Courier New', monospace; background: #000; color: #0f0; padding: 20px; }}
|
||||
h1 {{ color: #ff0; text-align: center; }}
|
||||
.box {{ border: 1px solid #0f0; padding: 15px; margin: 10px 0; }}
|
||||
.status {{ color: #0f0; }}
|
||||
.info {{ color: #00f; }}
|
||||
.warning {{ color: #f80; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚀 MY Network Bootstrap Node Monitor</h1>
|
||||
<div class="box">
|
||||
<h3 class="status">Node Status: ACTIVE (User Space)</h3>
|
||||
<p class="info">Node ID: {NODE_ID}</p>
|
||||
<p class="info">Domain: {DOMAIN}</p>
|
||||
<p class="info">Port: {PORT}</p>
|
||||
<p class="info">Type: Bootstrap Primary</p>
|
||||
<p class="info">Version: 2.0.0</p>
|
||||
<p class="info">Mode: User Space</p>
|
||||
<p class="info">Services: API, Monitor</p>
|
||||
<p class="info">Last Update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="status">Network Configuration</h3>
|
||||
<p class="info">Protocol Version: 2.0</p>
|
||||
<p class="info">Max Peers: 50</p>
|
||||
<p class="info">Public Access: Enabled</p>
|
||||
<p class="warning">SSL: Disabled (User Space Mode)</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="warning">⚠️ User Space Mode</h3>
|
||||
<p class="warning">Сервис запущен в пользовательском режиме</p>
|
||||
<p class="warning">Для полной функциональности требуются root права</p>
|
||||
<p class="info">Доступ: http://{DOMAIN}:{PORT}</p>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => location.reload(), 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"🚀 Starting MY Network Bootstrap Node - User Space")
|
||||
print(f"Domain: {DOMAIN}")
|
||||
print(f"Port: {PORT}")
|
||||
print(f"Node ID: {NODE_ID}")
|
||||
print(f"Mode: User Space (no root required)")
|
||||
print(f"Access: http://{DOMAIN}:{PORT}")
|
||||
print("")
|
||||
uvicorn.run(app, host="0.0.0.0", port=PORT)
|
||||
EOF
|
||||
|
||||
# Создание скрипта запуска
|
||||
cat > start_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
cd /home/service/my-uploader-bot
|
||||
source venv/bin/activate
|
||||
|
||||
echo "🚀 Запуск MY Network Bootstrap Node (User Space)"
|
||||
echo "==============================================="
|
||||
echo ""
|
||||
|
||||
# Создание директории для логов
|
||||
mkdir -p logs
|
||||
|
||||
# Запуск приложения
|
||||
python app/main.py
|
||||
EOF
|
||||
|
||||
chmod +x start_my_network.sh
|
||||
|
||||
# Создание cron задачи для автозапуска
|
||||
echo "⏰ Создание задачи для автозапуска..."
|
||||
cat > run_my_network.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Проверка, запущен ли MY Network
|
||||
if ! pgrep -f "python app/main.py" > /dev/null; then
|
||||
echo "$(date): Запуск MY Network" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
cd /home/service/my-uploader-bot
|
||||
source venv/bin/activate
|
||||
nohup python app/main.py >> logs/app.log 2>&1 &
|
||||
else
|
||||
echo "$(date): MY Network уже запущен" >> /home/service/my-uploader-bot/logs/cron.log
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x run_my_network.sh
|
||||
|
||||
# Добавление в crontab
|
||||
echo "📅 Добавление в crontab..."
|
||||
(crontab -l 2>/dev/null; echo "*/5 * * * * /home/service/my-uploader-bot/run_my_network.sh") | crontab -
|
||||
|
||||
echo ""
|
||||
echo "✅ MY Network Bootstrap Node установлен!"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
echo "🚀 Запуск вручную:"
|
||||
echo " cd /home/service/my-uploader-bot"
|
||||
echo " ./start_my_network.sh"
|
||||
echo ""
|
||||
echo "🚀 Запуск в фоне:"
|
||||
echo " ./run_my_network.sh"
|
||||
echo ""
|
||||
echo "🌐 Доступ:"
|
||||
echo " http://my-public-node-3.projscale.dev:15100"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/health"
|
||||
echo " http://my-public-node-3.projscale.dev:15100/api/my/monitor/"
|
||||
echo ""
|
||||
echo "📝 Логи:"
|
||||
echo " tail -f logs/my-network.log"
|
||||
echo " tail -f logs/app.log"
|
||||
echo ""
|
||||
echo "⚠️ Примечание: Сервис работает в пользовательском режиме"
|
||||
echo " Для полной функциональности требуются root права"
|
||||
echo ""
|
||||
echo "🎯 MY Network Bootstrap Node готов к работе!"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/expect -f
|
||||
|
||||
set timeout 30
|
||||
set password "DMUEjmnh6mDs/qlzhpjDzQ"
|
||||
|
||||
spawn ssh -o StrictHostKeyChecking=no service@2.58.65.188
|
||||
|
||||
expect {
|
||||
"password:" {
|
||||
send "$password\r"
|
||||
exp_continue
|
||||
}
|
||||
"$ " {
|
||||
interact
|
||||
}
|
||||
eof {
|
||||
exit
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user