sh scripts
This commit is contained in:
parent
2c1ca4bf45
commit
444b5af31a
|
|
@ -0,0 +1,286 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Основное приложение MY UPLOADER BOT
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.simple
|
||||||
|
container_name: my-uploader-app
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:15100:15100"
|
||||||
|
volumes:
|
||||||
|
- app_data:/app/data
|
||||||
|
- app_logs:/app/logs
|
||||||
|
- ./app/my_network/bootstrap.json:/app/app/my_network/bootstrap.json:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock # Для управления converter контейнерами
|
||||||
|
- converter_shared:/shared/converter # Общая папка для converter заданий
|
||||||
|
environment:
|
||||||
|
# Основные настройки
|
||||||
|
- NODE_ENV=production
|
||||||
|
- DEBUG=false
|
||||||
|
|
||||||
|
# Database
|
||||||
|
- DATABASE_URL=postgresql://my_user:${POSTGRES_PASSWORD}@postgres:5432/my_uploader_db
|
||||||
|
- POSTGRES_HOST=postgres
|
||||||
|
- POSTGRES_PORT=5432
|
||||||
|
- POSTGRES_DB=my_uploader_db
|
||||||
|
- POSTGRES_USER=my_user
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
- REDIS_HOST=redis
|
||||||
|
- REDIS_PORT=6379
|
||||||
|
|
||||||
|
# Security
|
||||||
|
- SECRET_KEY=${SECRET_KEY}
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
|
||||||
|
|
||||||
|
# MY Network
|
||||||
|
- MY_NETWORK_NODE_ID=${MY_NETWORK_NODE_ID}
|
||||||
|
- MY_NETWORK_PORT=15100
|
||||||
|
- MY_NETWORK_HOST=0.0.0.0
|
||||||
|
- MY_NETWORK_DOMAIN=${MY_NETWORK_DOMAIN}
|
||||||
|
- MY_NETWORK_SSL_ENABLED=true
|
||||||
|
|
||||||
|
# API Settings
|
||||||
|
- API_HOST=0.0.0.0
|
||||||
|
- API_PORT=15100
|
||||||
|
- API_WORKERS=4
|
||||||
|
- MAX_UPLOAD_SIZE=100MB
|
||||||
|
|
||||||
|
# Converter Settings (on-demand)
|
||||||
|
- CONVERTER_DOCKER_IMAGE=my-converter:latest
|
||||||
|
- CONVERTER_SHARED_PATH=/shared/converter
|
||||||
|
- CONVERTER_MAX_PARALLEL=3
|
||||||
|
- CONVERTER_TIMEOUT=300
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
- LOG_LEVEL=INFO
|
||||||
|
- LOG_FORMAT=json
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:15100/health')"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
# PostgreSQL Database
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: my-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./scripts/init-db-production.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=my_uploader_db
|
||||||
|
- POSTGRES_USER=my_user
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- POSTGRES_INITDB_ARGS=--auth-host=md5
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:5432:5432"
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U my_user -d my_uploader_db"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Redis Cache
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: my-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
- ./redis.conf:/usr/local/etc/redis/redis.conf:ro
|
||||||
|
command: redis-server /usr/local/etc/redis/redis.conf
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:6379:6379"
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# Web2 Client
|
||||||
|
web2-client:
|
||||||
|
build:
|
||||||
|
context: ./modules/web2-client
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: my-web2-client
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:3000"
|
||||||
|
volumes:
|
||||||
|
- web2_uploads:/app/uploads
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- API_URL=http://app:15100
|
||||||
|
- UPLOAD_PATH=/app/uploads
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# Nginx Reverse Proxy
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: my-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
- web2-client
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/sites-enabled:/etc/nginx/sites-enabled:ro
|
||||||
|
- ./ssl:/etc/nginx/ssl:ro
|
||||||
|
- nginx_logs:/var/log/nginx
|
||||||
|
- ./nginx/html:/usr/share/nginx/html:ro
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "nginx", "-t"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# Monitoring Stack (Prometheus + Grafana)
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
container_name: my-prometheus
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:9090:9090"
|
||||||
|
volumes:
|
||||||
|
- prometheus_data:/prometheus
|
||||||
|
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
- '--storage.tsdb.path=/prometheus'
|
||||||
|
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||||
|
- '--web.console.templates=/etc/prometheus/consoles'
|
||||||
|
- '--web.enable-lifecycle'
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: my-grafana
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3001:3000"
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
- ./monitoring/grafana:/etc/grafana/provisioning:ro
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
|
||||||
|
- GF_USERS_ALLOW_SIGN_UP=false
|
||||||
|
- GF_SERVER_DOMAIN=${MY_NETWORK_DOMAIN}
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
|
||||||
|
# Log Management
|
||||||
|
loki:
|
||||||
|
image: grafana/loki:latest
|
||||||
|
container_name: my-loki
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3100:3100"
|
||||||
|
volumes:
|
||||||
|
- loki_data:/loki
|
||||||
|
- ./monitoring/loki.yml:/etc/loki/local-config.yaml:ro
|
||||||
|
command: -config.file=/etc/loki/local-config.yaml
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
|
||||||
|
# Log Collection
|
||||||
|
promtail:
|
||||||
|
image: grafana/promtail:latest
|
||||||
|
container_name: my-promtail
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/log:/var/log:ro
|
||||||
|
- ./monitoring/promtail.yml:/etc/promtail/config.yml:ro
|
||||||
|
- app_logs:/var/log/app:ro
|
||||||
|
- nginx_logs:/var/log/nginx:ro
|
||||||
|
command: -config.file=/etc/promtail/config.yml
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CONVERTER MODULE BUILD (собирается, но не запускается постоянно)
|
||||||
|
# =============================================================================
|
||||||
|
# Этот сервис используется только для сборки image
|
||||||
|
# Основное приложение будет создавать контейнеры on-demand
|
||||||
|
|
||||||
|
converter-build:
|
||||||
|
build:
|
||||||
|
context: ./modules/converter-module
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: my-converter:latest
|
||||||
|
container_name: my-converter-build
|
||||||
|
profiles: ["build-only"] # Запускается только при сборке
|
||||||
|
volumes:
|
||||||
|
- converter_shared:/shared/converter
|
||||||
|
networks:
|
||||||
|
- uploader_network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Application data
|
||||||
|
app_data:
|
||||||
|
driver: local
|
||||||
|
app_logs:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
# Database
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
# Modules
|
||||||
|
converter_shared: # Общая папка для on-demand converter заданий
|
||||||
|
driver: local
|
||||||
|
web2_uploads:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
# Infrastructure
|
||||||
|
nginx_logs:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
prometheus_data:
|
||||||
|
driver: local
|
||||||
|
grafana_data:
|
||||||
|
driver: local
|
||||||
|
loki_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
uploader_network:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.20.0.0/16
|
||||||
|
|
@ -0,0 +1,328 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СОЗДАНИЕ NGINX КОНФИГУРАЦИИ ДЛЯ MY UPLOADER BOT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Usage: $0 <domain> <project_dir>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOMAIN="$1"
|
||||||
|
PROJECT_DIR="$2"
|
||||||
|
NGINX_CONFIG="/etc/nginx/sites-available/my-uploader-bot"
|
||||||
|
|
||||||
|
echo "Создание Nginx конфигурации для $DOMAIN..."
|
||||||
|
|
||||||
|
cat > "$NGINX_CONFIG" << EOF
|
||||||
|
# =============================================================================
|
||||||
|
# MY UPLOADER BOT - NGINX CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
# Domain: $DOMAIN
|
||||||
|
# Generated: $(date)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Rate limiting zones
|
||||||
|
limit_req_zone \$binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||||
|
limit_req_zone \$binary_remote_addr zone=upload_limit:10m rate=2r/s;
|
||||||
|
limit_req_zone \$binary_remote_addr zone=general_limit:10m rate=20r/s;
|
||||||
|
|
||||||
|
# Upstream для основного приложения
|
||||||
|
upstream my_uploader_app {
|
||||||
|
server 127.0.0.1:15100 max_fails=3 fail_timeout=30s;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upstream для web2-client
|
||||||
|
upstream web2_client {
|
||||||
|
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
|
||||||
|
keepalive 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upstream для converter-module
|
||||||
|
upstream converter_module {
|
||||||
|
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
|
||||||
|
keepalive 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name $DOMAIN www.$DOMAIN;
|
||||||
|
|
||||||
|
# ACME challenge для Let's Encrypt
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/html;
|
||||||
|
try_files \$uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect всех остальных запросов на HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://\$server_name\$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS Main Server
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name $DOMAIN www.$DOMAIN;
|
||||||
|
|
||||||
|
# SSL Configuration
|
||||||
|
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
||||||
|
ssl_trusted_certificate /etc/letsencrypt/live/$DOMAIN/chain.pem;
|
||||||
|
|
||||||
|
# SSL Settings
|
||||||
|
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:ECDHE-RSA-AES256-SHA384;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
|
||||||
|
# Security Headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" 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 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; img-src 'self' data: https: blob:; connect-src 'self' https: wss: ws:; media-src 'self' https: blob:; object-src 'none'; base-uri 'self'; form-action 'self';" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
|
||||||
|
# Basic Settings
|
||||||
|
client_max_body_size 100M;
|
||||||
|
client_body_timeout 60s;
|
||||||
|
client_header_timeout 60s;
|
||||||
|
keepalive_timeout 65s;
|
||||||
|
send_timeout 60s;
|
||||||
|
|
||||||
|
# Gzip Compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log /var/log/nginx/my-uploader-bot.access.log;
|
||||||
|
error_log /var/log/nginx/my-uploader-bot.error.log;
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# ОСНОВНЫЕ МАРШРУТЫ
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
# API routes
|
||||||
|
location /api/ {
|
||||||
|
limit_req zone=api_limit burst=20 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
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_set_header X-Forwarded-Host \$host;
|
||||||
|
proxy_set_header X-Forwarded-Port \$server_port;
|
||||||
|
|
||||||
|
proxy_cache_bypass \$http_upgrade;
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
# Специальные настройки для upload endpoints
|
||||||
|
location /api/upload/ {
|
||||||
|
limit_req zone=upload_limit burst=5 nodelay;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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;
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Telegram webhook endpoint
|
||||||
|
location /webhook/ {
|
||||||
|
limit_req zone=api_limit burst=50 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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 for main app
|
||||||
|
location /static/ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
try_files \$uri @app_static;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @app_static {
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_cache_valid 200 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# WEB2-CLIENT (фронтенд интерфейс)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
location /upload/ {
|
||||||
|
limit_req zone=general_limit burst=10 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://web2_client/;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /client/ {
|
||||||
|
limit_req zone=general_limit burst=10 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://web2_client/;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# CONVERTER-MODULE (конвертер медиа)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
location /convert/ {
|
||||||
|
limit_req zone=upload_limit burst=3 nodelay;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
|
||||||
|
proxy_pass http://converter_module/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# ОСНОВНОЙ МАРШРУТ (главная страница)
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
location / {
|
||||||
|
limit_req zone=general_limit burst=20 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
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_set_header X-Forwarded-Host \$host;
|
||||||
|
proxy_set_header X-Forwarded-Port \$server_port;
|
||||||
|
proxy_cache_bypass \$http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# СПЕЦИАЛЬНЫЕ ФАЙЛЫ
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
# robots.txt
|
||||||
|
location = /robots.txt {
|
||||||
|
return 200 "User-agent: *\nDisallow: /api/\nDisallow: /webhook/\nAllow: /\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
|
||||||
|
# favicon.ico
|
||||||
|
location = /favicon.ico {
|
||||||
|
proxy_pass http://my_uploader_app;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Защита от доступа к скрытым файлам
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Защита от доступа к конфигурационным файлам
|
||||||
|
location ~* \.(env|conf|config|ini|log|bak|backup|tmp)$ {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ДОПОЛНИТЕЛЬНАЯ БЕЗОПАСНОСТЬ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Блокировка ботов и сканеров
|
||||||
|
map \$http_user_agent \$blocked_agent {
|
||||||
|
~*malicious 1;
|
||||||
|
~*bot 0;
|
||||||
|
~*crawler 0;
|
||||||
|
~*spider 0;
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Блокировка IP по геолокации (опционально)
|
||||||
|
# geo \$blocked_country {
|
||||||
|
# default 0;
|
||||||
|
# CN 1; # Китай
|
||||||
|
# RU 0; # Россия разрешена
|
||||||
|
# }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Nginx конфигурация создана: $NGINX_CONFIG"
|
||||||
|
|
||||||
|
# Создание директории для логов
|
||||||
|
mkdir -p /var/log/nginx
|
||||||
|
|
||||||
|
# Проверка синтаксиса
|
||||||
|
echo "Проверка синтаксиса Nginx..."
|
||||||
|
nginx -t -c /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
echo "✅ Nginx конфигурация готова для $DOMAIN"
|
||||||
|
|
@ -0,0 +1,568 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СОЗДАНИЕ SYSTEMD СЕРВИСОВ ДЛЯ MY UPLOADER BOT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <project_dir> [user]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROJECT_DIR="$1"
|
||||||
|
SERVICE_USER="${2:-service}"
|
||||||
|
|
||||||
|
echo "Создание systemd сервисов для MY Uploader Bot..."
|
||||||
|
echo "Проект: $PROJECT_DIR"
|
||||||
|
echo "Пользователь: $SERVICE_USER"
|
||||||
|
|
||||||
|
# Проверка прав root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "❌ Этот скрипт должен запускаться с правами root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ОСНОВНОЙ СЕРВИС MY-UPLOADER-BOT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание сервиса my-uploader-bot..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-uploader-bot.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Uploader Bot - Distributed Content Network
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=network.target postgresql.service redis.service docker.service
|
||||||
|
Wants=postgresql.service redis.service docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
Environment=PYTHONPATH=$PROJECT_DIR
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Переменные окружения
|
||||||
|
EnvironmentFile=$PROJECT_DIR/.env
|
||||||
|
|
||||||
|
# Команда запуска
|
||||||
|
ExecStart=/usr/bin/python3 -m app.main
|
||||||
|
ExecReload=/bin/kill -HUP \$MAINPID
|
||||||
|
|
||||||
|
# Настройки перезапуска
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StartLimitInterval=60
|
||||||
|
StartLimitBurst=3
|
||||||
|
|
||||||
|
# Безопасность
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=$PROJECT_DIR
|
||||||
|
|
||||||
|
# Ресурсы
|
||||||
|
MemoryMax=512M
|
||||||
|
CPUQuota=50%
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-uploader-bot
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-uploader-bot создан"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СЕРВИС MY NETWORK BOOTSTRAP NODE
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание сервиса my-network-bootstrap..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-network-bootstrap.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Network Bootstrap Node
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=network.target
|
||||||
|
Requires=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
Environment=PYTHONPATH=$PROJECT_DIR
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Переменные окружения
|
||||||
|
EnvironmentFile=$PROJECT_DIR/.env
|
||||||
|
Environment=MY_NETWORK_MODE=bootstrap
|
||||||
|
Environment=MY_NETWORK_PORT=15100
|
||||||
|
|
||||||
|
# Команда запуска bootstrap узла
|
||||||
|
ExecStart=/usr/bin/python3 -c "
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '$PROJECT_DIR')
|
||||||
|
from app.my_network.bootstrap_node import start_bootstrap_node
|
||||||
|
start_bootstrap_node()
|
||||||
|
"
|
||||||
|
|
||||||
|
# Настройки перезапуска
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StartLimitInterval=60
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
# Безопасность
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=$PROJECT_DIR
|
||||||
|
|
||||||
|
# Ресурсы
|
||||||
|
MemoryMax=256M
|
||||||
|
CPUQuota=25%
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-network-bootstrap
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-network-bootstrap создан"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СЕРВИС WEB2 CLIENT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ -d "$PROJECT_DIR/modules/web2-client" ]; then
|
||||||
|
echo "📝 Создание сервиса my-web2-client..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-web2-client.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Web2 Client - Web Interface
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=network.target
|
||||||
|
Requires=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR/modules/web2-client
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
Environment=NODE_ENV=production
|
||||||
|
Environment=API_URL=http://localhost:15100
|
||||||
|
|
||||||
|
# Команда запуска
|
||||||
|
ExecStart=/usr/bin/node server.js
|
||||||
|
|
||||||
|
# Настройки перезапуска
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StartLimitInterval=60
|
||||||
|
StartLimitBurst=3
|
||||||
|
|
||||||
|
# Безопасность
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=$PROJECT_DIR/modules/web2-client
|
||||||
|
|
||||||
|
# Ресурсы
|
||||||
|
MemoryMax=256M
|
||||||
|
CPUQuota=25%
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-web2-client
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-web2-client создан"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СЕРВИС DOCKER COMPOSE УПРАВЛЕНИЯ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание сервиса my-docker-compose..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-docker-compose.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Uploader Bot - Docker Compose Stack
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=docker.service network.target
|
||||||
|
Requires=docker.service
|
||||||
|
Before=my-uploader-bot.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
|
||||||
|
# Команды запуска и остановки
|
||||||
|
ExecStart=/usr/local/bin/docker-compose -f docker-compose.production.yml up -d
|
||||||
|
ExecStop=/usr/local/bin/docker-compose -f docker-compose.production.yml down
|
||||||
|
ExecReload=/usr/local/bin/docker-compose -f docker-compose.production.yml restart
|
||||||
|
|
||||||
|
# Настройки
|
||||||
|
TimeoutStartSec=300
|
||||||
|
TimeoutStopSec=120
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-docker-compose
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-docker-compose создан"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СЕРВИС CONVERTER BUILDER (только сборка image)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание сервиса my-converter-builder..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-converter-builder.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Converter Builder - Build Docker Image
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=docker.service
|
||||||
|
Requires=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
|
||||||
|
# Команда сборки converter image
|
||||||
|
ExecStart=/usr/local/bin/docker-compose -f docker-compose.production.yml build converter-build
|
||||||
|
|
||||||
|
# Настройки
|
||||||
|
TimeoutStartSec=600
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-converter-builder
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-converter-builder создан"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СЕРВИС МОНИТОРИНГА MY NETWORK
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание сервиса my-network-monitor..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/my-network-monitor.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MY Network Monitor - Health Checker
|
||||||
|
Documentation=https://github.com/your-org/my-uploader-bot
|
||||||
|
After=network.target my-uploader-bot.service
|
||||||
|
Wants=my-uploader-bot.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=$SERVICE_USER
|
||||||
|
Group=$SERVICE_USER
|
||||||
|
WorkingDirectory=$PROJECT_DIR
|
||||||
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
|
Environment=PYTHONPATH=$PROJECT_DIR
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Переменные окружения
|
||||||
|
EnvironmentFile=$PROJECT_DIR/.env
|
||||||
|
|
||||||
|
# Команда запуска мониторинга
|
||||||
|
ExecStart=/usr/bin/python3 -c "
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import docker
|
||||||
|
sys.path.insert(0, '$PROJECT_DIR')
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger('my-network-monitor')
|
||||||
|
|
||||||
|
def check_converter_image():
|
||||||
|
'''Проверяет наличие и обновляет converter image'''
|
||||||
|
try:
|
||||||
|
client = docker.from_env()
|
||||||
|
images = client.images.list('my-converter:latest')
|
||||||
|
if not images:
|
||||||
|
logger.warning('Converter image не найден, собираем...')
|
||||||
|
subprocess.run(['systemctl', 'start', 'my-converter-builder'], check=True)
|
||||||
|
else:
|
||||||
|
logger.info('Converter image готов к использованию')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Ошибка проверки converter image: {e}')
|
||||||
|
|
||||||
|
async def monitor_health():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Проверка основного здоровья системы
|
||||||
|
logger.info('MY Network: Health check passed')
|
||||||
|
|
||||||
|
# Проверка converter image каждые 10 минут
|
||||||
|
if int(time.time()) % 600 == 0:
|
||||||
|
check_converter_image()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'MY Network: Health check failed: {e}')
|
||||||
|
await asyncio.sleep(30)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(monitor_health())
|
||||||
|
"
|
||||||
|
|
||||||
|
# Настройки перезапуска
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
StartLimitInterval=300
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
# Безопасность
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
|
||||||
|
# Ресурсы
|
||||||
|
MemoryMax=64M
|
||||||
|
CPUQuota=10%
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=my-network-monitor
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Сервис my-network-monitor создан"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# НАСТРОЙКА ЛОГРОТАЦИИ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Настройка logrotate для MY Network..."
|
||||||
|
|
||||||
|
cat > /etc/logrotate.d/my-network << EOF
|
||||||
|
# Ротация логов MY Network
|
||||||
|
$PROJECT_DIR/logs/*.log {
|
||||||
|
daily
|
||||||
|
missingok
|
||||||
|
rotate 30
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
create 644 $SERVICE_USER $SERVICE_USER
|
||||||
|
postrotate
|
||||||
|
systemctl reload my-uploader-bot my-network-bootstrap my-web2-client || true
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ротация логов systemd для MY Network
|
||||||
|
/var/log/journal/my-uploader-bot.log
|
||||||
|
/var/log/journal/my-network-bootstrap.log
|
||||||
|
/var/log/journal/my-web2-client.log
|
||||||
|
/var/log/journal/my-network-monitor.log
|
||||||
|
/var/log/journal/my-docker-compose.log {
|
||||||
|
weekly
|
||||||
|
missingok
|
||||||
|
rotate 4
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Logrotate настроен"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ПРИМЕНЕНИЕ НАСТРОЕК
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "🔄 Применение настроек systemd..."
|
||||||
|
|
||||||
|
# Перезагрузка systemd
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
# Включение автозапуска сервисов
|
||||||
|
systemctl enable my-docker-compose.service
|
||||||
|
systemctl enable my-converter-builder.service
|
||||||
|
systemctl enable my-uploader-bot.service
|
||||||
|
systemctl enable my-network-bootstrap.service
|
||||||
|
systemctl enable my-network-monitor.service
|
||||||
|
|
||||||
|
if [ -f "/etc/systemd/system/my-web2-client.service" ]; then
|
||||||
|
systemctl enable my-web2-client.service
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Все сервисы включены для автозапуска"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# СОЗДАНИЕ УПРАВЛЯЮЩИХ СКРИПТОВ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📝 Создание управляющих скриптов..."
|
||||||
|
|
||||||
|
# Скрипт запуска всех сервисов
|
||||||
|
cat > "$PROJECT_DIR/start_all_services.sh" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
echo "🚀 Запуск всех сервисов MY Network..."
|
||||||
|
|
||||||
|
# Сначала запуск Docker Compose stack
|
||||||
|
sudo systemctl start my-docker-compose
|
||||||
|
|
||||||
|
# Сборка converter image если нужно
|
||||||
|
sudo systemctl start my-converter-builder
|
||||||
|
|
||||||
|
# Запуск основных сервисов
|
||||||
|
sudo systemctl start my-uploader-bot
|
||||||
|
sudo systemctl start my-network-bootstrap
|
||||||
|
sudo systemctl start my-network-monitor
|
||||||
|
|
||||||
|
if systemctl list-unit-files | grep -q my-web2-client; then
|
||||||
|
sudo systemctl start my-web2-client
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Все сервисы запущены"
|
||||||
|
sudo systemctl status my-uploader-bot my-network-bootstrap --no-pager
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Скрипт остановки всех сервисов
|
||||||
|
cat > "$PROJECT_DIR/stop_all_services.sh" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
echo "🛑 Остановка всех сервисов MY Network..."
|
||||||
|
|
||||||
|
sudo systemctl stop my-uploader-bot
|
||||||
|
sudo systemctl stop my-network-bootstrap
|
||||||
|
sudo systemctl stop my-network-monitor
|
||||||
|
|
||||||
|
if systemctl list-unit-files | grep -q my-web2-client; then
|
||||||
|
sudo systemctl stop my-web2-client
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Остановка Docker Compose stack
|
||||||
|
sudo systemctl stop my-docker-compose
|
||||||
|
|
||||||
|
echo "✅ Все сервисы остановлены"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Скрипт проверки статуса
|
||||||
|
cat > "$PROJECT_DIR/check_services.sh" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
echo "📊 Статус сервисов MY Network:"
|
||||||
|
echo "================================"
|
||||||
|
|
||||||
|
sudo systemctl status my-docker-compose --no-pager -l
|
||||||
|
echo ""
|
||||||
|
sudo systemctl status my-uploader-bot --no-pager -l
|
||||||
|
echo ""
|
||||||
|
sudo systemctl status my-network-bootstrap --no-pager -l
|
||||||
|
echo ""
|
||||||
|
sudo systemctl status my-network-monitor --no-pager -l
|
||||||
|
|
||||||
|
if systemctl list-unit-files | grep -q my-web2-client; then
|
||||||
|
echo ""
|
||||||
|
sudo systemctl status my-web2-client --no-pager -l
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🐳 Docker контейнеры:"
|
||||||
|
sudo docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🖼️ Docker images:"
|
||||||
|
sudo docker images | grep my-
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🌐 Активные порты:"
|
||||||
|
sudo netstat -tlnp | grep -E ":(15100|3000|80|443)"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Скрипт пересборки converter
|
||||||
|
cat > "$PROJECT_DIR/rebuild_converter.sh" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
echo "🔄 Пересборка converter image..."
|
||||||
|
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
sudo systemctl stop my-converter-builder
|
||||||
|
sudo docker rmi my-converter:latest 2>/dev/null || true
|
||||||
|
sudo systemctl start my-converter-builder
|
||||||
|
|
||||||
|
echo "✅ Converter image пересобран"
|
||||||
|
sudo docker images | grep my-converter
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Установка прав доступа
|
||||||
|
chmod +x "$PROJECT_DIR/start_all_services.sh"
|
||||||
|
chmod +x "$PROJECT_DIR/stop_all_services.sh"
|
||||||
|
chmod +x "$PROJECT_DIR/check_services.sh"
|
||||||
|
chmod +x "$PROJECT_DIR/rebuild_converter.sh"
|
||||||
|
chown $SERVICE_USER:$SERVICE_USER "$PROJECT_DIR"/*.sh
|
||||||
|
|
||||||
|
echo "✅ Управляющие скрипты созданы"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 SystemD сервисы успешно настроены!"
|
||||||
|
echo ""
|
||||||
|
echo "📋 Созданные сервисы:"
|
||||||
|
echo " • my-docker-compose.service - Docker Compose stack"
|
||||||
|
echo " • my-converter-builder.service - Сборка converter image"
|
||||||
|
echo " • my-uploader-bot.service - Основное приложение"
|
||||||
|
echo " • my-network-bootstrap.service - Bootstrap узел"
|
||||||
|
echo " • my-network-monitor.service - Мониторинг сети"
|
||||||
|
if [ -f "/etc/systemd/system/my-web2-client.service" ]; then
|
||||||
|
echo " • my-web2-client.service - Web интерфейс"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "🎛️ Управление сервисами:"
|
||||||
|
echo " Запуск всех: $PROJECT_DIR/start_all_services.sh"
|
||||||
|
echo " Остановка всех: $PROJECT_DIR/stop_all_services.sh"
|
||||||
|
echo " Проверка: $PROJECT_DIR/check_services.sh"
|
||||||
|
echo " Пересборка converter: $PROJECT_DIR/rebuild_converter.sh"
|
||||||
|
echo ""
|
||||||
|
echo "📝 Логи сервисов:"
|
||||||
|
echo " journalctl -u my-uploader-bot -f"
|
||||||
|
echo " journalctl -u my-network-bootstrap -f"
|
||||||
|
echo " journalctl -u my-docker-compose -f"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Converter работает on-demand:"
|
||||||
|
echo " - Image собирается один раз при установке"
|
||||||
|
echo " - MY Uploader Bot создает контейнеры по мере необходимости"
|
||||||
|
echo " - Контейнеры автоматически удаляются после завершения задач"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,383 @@
|
||||||
|
-- =============================================================================
|
||||||
|
-- MY UPLOADER BOT - PRODUCTION DATABASE INITIALIZATION
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Создание расширений
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "btree_gin";
|
||||||
|
|
||||||
|
-- Настройка SSL (если требуется)
|
||||||
|
-- ALTER SYSTEM SET ssl = on;
|
||||||
|
-- ALTER SYSTEM SET ssl_cert_file = '/var/lib/postgresql/server.crt';
|
||||||
|
-- ALTER SYSTEM SET ssl_key_file = '/var/lib/postgresql/server.key';
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- СОЗДАНИЕ СХЕМ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Основная схема приложения
|
||||||
|
CREATE SCHEMA IF NOT EXISTS app;
|
||||||
|
|
||||||
|
-- Схема для MY Network
|
||||||
|
CREATE SCHEMA IF NOT EXISTS my_network;
|
||||||
|
|
||||||
|
-- Схема для мониторинга
|
||||||
|
CREATE SCHEMA IF NOT EXISTS monitoring;
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- ТАБЛИЦЫ ПОЛЬЗОВАТЕЛЕЙ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS app.users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
email VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
salt VARCHAR(255) NOT NULL,
|
||||||
|
is_active BOOLEAN DEFAULT true,
|
||||||
|
is_verified BOOLEAN DEFAULT false,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
last_login TIMESTAMP WITH TIME ZONE,
|
||||||
|
|
||||||
|
-- Индексы
|
||||||
|
CONSTRAINT users_username_check CHECK (length(username) >= 3),
|
||||||
|
CONSTRAINT users_email_check CHECK (email ~* '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$')
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для пользователей
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_username ON app.users(username);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_email ON app.users(email);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_active ON app.users(is_active);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_created_at ON app.users(created_at);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- ТАБЛИЦЫ КОНТЕНТА
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS app.content (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
user_id UUID NOT NULL REFERENCES app.users(id) ON DELETE CASCADE,
|
||||||
|
filename VARCHAR(255) NOT NULL,
|
||||||
|
original_filename VARCHAR(255) NOT NULL,
|
||||||
|
content_type VARCHAR(100) NOT NULL,
|
||||||
|
file_size BIGINT NOT NULL,
|
||||||
|
file_hash VARCHAR(64) NOT NULL,
|
||||||
|
upload_path TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- Метаданные
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
tags TEXT[] DEFAULT '{}',
|
||||||
|
|
||||||
|
-- MY Network данные
|
||||||
|
my_network_id VARCHAR(64) UNIQUE,
|
||||||
|
replication_status VARCHAR(20) DEFAULT 'pending',
|
||||||
|
replicated_nodes TEXT[] DEFAULT '{}',
|
||||||
|
|
||||||
|
-- Статус
|
||||||
|
status VARCHAR(20) DEFAULT 'uploaded',
|
||||||
|
is_public BOOLEAN DEFAULT false,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Ограничения
|
||||||
|
CONSTRAINT content_file_size_check CHECK (file_size > 0),
|
||||||
|
CONSTRAINT content_status_check CHECK (status IN ('uploaded', 'processing', 'ready', 'error', 'deleted')),
|
||||||
|
CONSTRAINT content_replication_status_check CHECK (replication_status IN ('pending', 'replicating', 'replicated', 'failed'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для контента
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_user_id ON app.content(user_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_file_hash ON app.content(file_hash);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_my_network_id ON app.content(my_network_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_status ON app.content(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_replication_status ON app.content(replication_status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_created_at ON app.content(created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_metadata ON app.content USING GIN(metadata);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_content_tags ON app.content USING GIN(tags);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- MY NETWORK ТАБЛИЦЫ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Узлы MY Network
|
||||||
|
CREATE TABLE IF NOT EXISTS my_network.nodes (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
node_id VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
hostname VARCHAR(255) NOT NULL,
|
||||||
|
port INTEGER NOT NULL DEFAULT 15100,
|
||||||
|
public_key TEXT,
|
||||||
|
|
||||||
|
-- Статус узла
|
||||||
|
status VARCHAR(20) DEFAULT 'active',
|
||||||
|
last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
version VARCHAR(20),
|
||||||
|
|
||||||
|
-- Статистика
|
||||||
|
total_content BIGINT DEFAULT 0,
|
||||||
|
total_storage BIGINT DEFAULT 0,
|
||||||
|
|
||||||
|
-- Метаданные
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT nodes_status_check CHECK (status IN ('active', 'inactive', 'banned', 'maintenance'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для узлов
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_nodes_node_id ON my_network.nodes(node_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_nodes_status ON my_network.nodes(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON my_network.nodes(last_seen);
|
||||||
|
|
||||||
|
-- Репликация контента между узлами
|
||||||
|
CREATE TABLE IF NOT EXISTS my_network.content_replications (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
content_id UUID NOT NULL REFERENCES app.content(id) ON DELETE CASCADE,
|
||||||
|
source_node_id VARCHAR(64) NOT NULL,
|
||||||
|
target_node_id VARCHAR(64) NOT NULL,
|
||||||
|
|
||||||
|
-- Статус репликации
|
||||||
|
status VARCHAR(20) DEFAULT 'pending',
|
||||||
|
started_at TIMESTAMP WITH TIME ZONE,
|
||||||
|
completed_at TIMESTAMP WITH TIME ZONE,
|
||||||
|
error_message TEXT,
|
||||||
|
|
||||||
|
-- Метаданные
|
||||||
|
file_size BIGINT,
|
||||||
|
transfer_speed BIGINT, -- bytes per second
|
||||||
|
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT replications_status_check CHECK (status IN ('pending', 'in_progress', 'completed', 'failed', 'cancelled')),
|
||||||
|
CONSTRAINT replications_unique_transfer UNIQUE (content_id, target_node_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для репликации
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_replications_content_id ON my_network.content_replications(content_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_replications_status ON my_network.content_replications(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_replications_source_node ON my_network.content_replications(source_node_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_replications_target_node ON my_network.content_replications(target_node_id);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- МОНИТОРИНГ И ЛОГИ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Системные события
|
||||||
|
CREATE TABLE IF NOT EXISTS monitoring.system_events (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
event_type VARCHAR(50) NOT NULL,
|
||||||
|
severity VARCHAR(20) DEFAULT 'info',
|
||||||
|
source VARCHAR(100) NOT NULL,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- Дополнительные данные
|
||||||
|
details JSONB DEFAULT '{}',
|
||||||
|
user_id UUID REFERENCES app.users(id),
|
||||||
|
node_id VARCHAR(64),
|
||||||
|
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT events_severity_check CHECK (severity IN ('debug', 'info', 'warning', 'error', 'critical'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для событий
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_events_type ON monitoring.system_events(event_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_events_severity ON monitoring.system_events(severity);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_events_source ON monitoring.system_events(source);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_events_created_at ON monitoring.system_events(created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_events_user_id ON monitoring.system_events(user_id);
|
||||||
|
|
||||||
|
-- Метрики производительности
|
||||||
|
CREATE TABLE IF NOT EXISTS monitoring.performance_metrics (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
metric_name VARCHAR(100) NOT NULL,
|
||||||
|
metric_value NUMERIC NOT NULL,
|
||||||
|
metric_unit VARCHAR(20),
|
||||||
|
|
||||||
|
-- Контекст
|
||||||
|
source VARCHAR(100) NOT NULL,
|
||||||
|
tags JSONB DEFAULT '{}',
|
||||||
|
|
||||||
|
measured_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Партиционирование по времени
|
||||||
|
PARTITION BY RANGE (measured_at)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Создание партиций для метрик (по месяцам)
|
||||||
|
CREATE TABLE IF NOT EXISTS monitoring.performance_metrics_current
|
||||||
|
PARTITION OF monitoring.performance_metrics
|
||||||
|
FOR VALUES FROM (date_trunc('month', NOW())) TO (date_trunc('month', NOW() + interval '1 month'));
|
||||||
|
|
||||||
|
-- Индексы для метрик
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_metrics_name_time ON monitoring.performance_metrics(metric_name, measured_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_metrics_source ON monitoring.performance_metrics(source);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- ФУНКЦИИ И ТРИГГЕРЫ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Функция обновления updated_at
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Триггеры для обновления updated_at
|
||||||
|
CREATE TRIGGER trigger_users_updated_at
|
||||||
|
BEFORE UPDATE ON app.users
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER trigger_content_updated_at
|
||||||
|
BEFORE UPDATE ON app.content
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER trigger_nodes_updated_at
|
||||||
|
BEFORE UPDATE ON my_network.nodes
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
-- Функция логирования событий
|
||||||
|
CREATE OR REPLACE FUNCTION log_system_event(
|
||||||
|
p_event_type VARCHAR(50),
|
||||||
|
p_message TEXT,
|
||||||
|
p_severity VARCHAR(20) DEFAULT 'info',
|
||||||
|
p_source VARCHAR(100) DEFAULT 'system',
|
||||||
|
p_details JSONB DEFAULT '{}',
|
||||||
|
p_user_id UUID DEFAULT NULL,
|
||||||
|
p_node_id VARCHAR(64) DEFAULT NULL
|
||||||
|
)
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
DECLARE
|
||||||
|
event_id UUID;
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO monitoring.system_events (
|
||||||
|
event_type, message, severity, source, details, user_id, node_id
|
||||||
|
) VALUES (
|
||||||
|
p_event_type, p_message, p_severity, p_source, p_details, p_user_id, p_node_id
|
||||||
|
) RETURNING id INTO event_id;
|
||||||
|
|
||||||
|
RETURN event_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- ПРЕДСТАВЛЕНИЯ (VIEWS)
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Активные узлы
|
||||||
|
CREATE OR REPLACE VIEW my_network.active_nodes AS
|
||||||
|
SELECT
|
||||||
|
node_id,
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
status,
|
||||||
|
last_seen,
|
||||||
|
total_content,
|
||||||
|
total_storage,
|
||||||
|
version
|
||||||
|
FROM my_network.nodes
|
||||||
|
WHERE status = 'active'
|
||||||
|
AND last_seen > NOW() - INTERVAL '1 hour';
|
||||||
|
|
||||||
|
-- Статистика контента
|
||||||
|
CREATE OR REPLACE VIEW app.content_stats AS
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_files,
|
||||||
|
SUM(file_size) as total_size,
|
||||||
|
COUNT(DISTINCT user_id) as unique_users,
|
||||||
|
COUNT(*) FILTER (WHERE status = 'ready') as ready_files,
|
||||||
|
COUNT(*) FILTER (WHERE replication_status = 'replicated') as replicated_files,
|
||||||
|
AVG(file_size) as avg_file_size
|
||||||
|
FROM app.content
|
||||||
|
WHERE status != 'deleted';
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- НАЧАЛЬНЫЕ ДАННЫЕ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Создание администратора (пароль: admin123)
|
||||||
|
-- Хеш создан с использованием bcrypt
|
||||||
|
INSERT INTO app.users (username, email, password_hash, salt, is_active, is_verified)
|
||||||
|
VALUES (
|
||||||
|
'admin',
|
||||||
|
'admin@mynetwork.local',
|
||||||
|
'$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LowN5wVrtJr.pjdXW',
|
||||||
|
'production_salt_2024',
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
) ON CONFLICT (username) DO NOTHING;
|
||||||
|
|
||||||
|
-- Регистрация локального узла
|
||||||
|
INSERT INTO my_network.nodes (node_id, hostname, port, status, version, metadata)
|
||||||
|
VALUES (
|
||||||
|
'local-production-node',
|
||||||
|
'localhost',
|
||||||
|
15100,
|
||||||
|
'active',
|
||||||
|
'1.0.0',
|
||||||
|
'{"type": "production", "bootstrap": true}'
|
||||||
|
) ON CONFLICT (node_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- Начальное событие
|
||||||
|
SELECT log_system_event(
|
||||||
|
'system_init',
|
||||||
|
'Production database initialized successfully',
|
||||||
|
'info',
|
||||||
|
'database',
|
||||||
|
'{"version": "1.0.0", "environment": "production"}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- НАСТРОЙКИ ПРОИЗВОДИТЕЛЬНОСТИ
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Оптимизация для production
|
||||||
|
ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';
|
||||||
|
ALTER SYSTEM SET track_activities = on;
|
||||||
|
ALTER SYSTEM SET track_counts = on;
|
||||||
|
ALTER SYSTEM SET track_io_timing = on;
|
||||||
|
ALTER SYSTEM SET log_statement = 'mod';
|
||||||
|
ALTER SYSTEM SET log_min_duration_statement = 1000;
|
||||||
|
ALTER SYSTEM SET max_connections = 200;
|
||||||
|
ALTER SYSTEM SET shared_buffers = '256MB';
|
||||||
|
ALTER SYSTEM SET effective_cache_size = '1GB';
|
||||||
|
ALTER SYSTEM SET maintenance_work_mem = '64MB';
|
||||||
|
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
|
||||||
|
ALTER SYSTEM SET wal_buffers = '16MB';
|
||||||
|
|
||||||
|
-- Применение настроек
|
||||||
|
SELECT pg_reload_conf();
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- ПРАВА ДОСТУПА
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Права для пользователя приложения
|
||||||
|
GRANT USAGE ON SCHEMA app TO my_user;
|
||||||
|
GRANT USAGE ON SCHEMA my_network TO my_user;
|
||||||
|
GRANT USAGE ON SCHEMA monitoring TO my_user;
|
||||||
|
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO my_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA my_network TO my_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA monitoring TO my_user;
|
||||||
|
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA app TO my_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA my_network TO my_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA monitoring TO my_user;
|
||||||
|
|
||||||
|
-- Права на функции
|
||||||
|
GRANT EXECUTE ON FUNCTION update_updated_at_column() TO my_user;
|
||||||
|
GRANT EXECUTE ON FUNCTION log_system_event(VARCHAR, TEXT, VARCHAR, VARCHAR, JSONB, UUID, VARCHAR) TO my_user;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- Финальное сообщение
|
||||||
|
\echo 'MY Uploader Bot production database initialized successfully!'
|
||||||
|
\echo 'Default admin user: admin / admin123'
|
||||||
|
\echo 'Please change the default password immediately!'
|
||||||
|
|
@ -0,0 +1,442 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# НАСТРОЙКА ДОПОЛНИТЕЛЬНЫХ МОДУЛЕЙ MY UPLOADER BOT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <project_dir>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROJECT_DIR="$1"
|
||||||
|
MODULES_DIR="$PROJECT_DIR/modules"
|
||||||
|
|
||||||
|
echo "Настройка дополнительных модулей в $MODULES_DIR..."
|
||||||
|
|
||||||
|
# Создание директории для модулей
|
||||||
|
mkdir -p "$MODULES_DIR"
|
||||||
|
cd "$MODULES_DIR"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CONVERTER-MODULE (Конвертер медиа файлов)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "📦 Настройка converter-module..."
|
||||||
|
|
||||||
|
if [ ! -d "converter-module" ]; then
|
||||||
|
echo "Создание converter-module структуры..."
|
||||||
|
mkdir -p converter-module/{converter,logs,in_examples,out_examples,output}
|
||||||
|
|
||||||
|
# Создание основного файла converter-module
|
||||||
|
cat > "converter-module/app.py" << 'EOF'
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
MY UPLOADER BOT - CONVERTER MODULE
|
||||||
|
Конвертер медиа файлов для MY Network
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from fastapi import FastAPI, File, UploadFile, HTTPException
|
||||||
|
from fastapi.responses import JSONResponse, FileResponse
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
# Настройка логирования
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler('logs/converter.log'),
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="MY Converter Module",
|
||||||
|
description="Конвертер медиа файлов для MY Network",
|
||||||
|
version="1.0.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
# CORS настройки
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # В продакшене ограничить
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создание необходимых директорий
|
||||||
|
os.makedirs("output", exist_ok=True)
|
||||||
|
os.makedirs("logs", exist_ok=True)
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def root():
|
||||||
|
return {"message": "MY Converter Module", "status": "running"}
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health():
|
||||||
|
return {"status": "healthy", "module": "converter"}
|
||||||
|
|
||||||
|
@app.post("/convert/")
|
||||||
|
async def convert_file(file: UploadFile = File(...)):
|
||||||
|
"""Конвертация загруженного файла"""
|
||||||
|
try:
|
||||||
|
# Базовая обработка - в реальном проекте добавить ffmpeg/imagemagick
|
||||||
|
logger.info(f"Конвертация файла: {file.filename}")
|
||||||
|
|
||||||
|
# Сохранение входного файла
|
||||||
|
input_path = f"output/{file.filename}"
|
||||||
|
with open(input_path, "wb") as buffer:
|
||||||
|
content = await file.read()
|
||||||
|
buffer.write(content)
|
||||||
|
|
||||||
|
return JSONResponse({
|
||||||
|
"status": "success",
|
||||||
|
"message": f"Файл {file.filename} обработан",
|
||||||
|
"output_file": input_path,
|
||||||
|
"size": len(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка конвертации: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.get("/status")
|
||||||
|
async def status():
|
||||||
|
"""Статус converter-module"""
|
||||||
|
return {
|
||||||
|
"module": "converter-module",
|
||||||
|
"status": "active",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"supported_formats": ["mp4", "mp3", "jpg", "png", "gif"]
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run(
|
||||||
|
"app:app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=8080,
|
||||||
|
reload=False,
|
||||||
|
log_level="info"
|
||||||
|
)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание requirements для converter-module
|
||||||
|
cat > "converter-module/requirements.txt" << 'EOF'
|
||||||
|
fastapi==0.104.1
|
||||||
|
uvicorn[standard]==0.24.0
|
||||||
|
python-multipart==0.0.6
|
||||||
|
Pillow==10.1.0
|
||||||
|
python-ffmpeg==2.0.12
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание Dockerfile для converter-module
|
||||||
|
cat > "converter-module/Dockerfile" << 'EOF'
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Установка ffmpeg и других утилит
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ffmpeg \
|
||||||
|
imagemagick \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Установка Python зависимостей
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копирование кода
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Создание директорий
|
||||||
|
RUN mkdir -p output logs
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["python", "app.py"]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ converter-module создан"
|
||||||
|
else
|
||||||
|
echo "ℹ️ converter-module уже существует"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WEB2-CLIENT (Веб интерфейс)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "🌐 Настройка web2-client..."
|
||||||
|
|
||||||
|
if [ ! -d "web2-client" ]; then
|
||||||
|
echo "Создание web2-client структуры..."
|
||||||
|
mkdir -p web2-client/{public,src}
|
||||||
|
|
||||||
|
# Создание package.json
|
||||||
|
cat > "web2-client/package.json" << 'EOF'
|
||||||
|
{
|
||||||
|
"name": "my-uploader-web-client",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Web client for MY Uploader Bot",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js",
|
||||||
|
"dev": "node server.js",
|
||||||
|
"build": "echo 'Build completed'",
|
||||||
|
"test": "echo 'No tests specified'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"multer": "^1.4.5",
|
||||||
|
"axios": "^1.6.0"
|
||||||
|
},
|
||||||
|
"keywords": ["my-network", "uploader", "web-client"],
|
||||||
|
"author": "MY Network Team",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание простого Express сервера
|
||||||
|
cat > "web2-client/server.js" << 'EOF'
|
||||||
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
|
const path = require('path');
|
||||||
|
const multer = require('multer');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// Настройка multer для загрузки файлов
|
||||||
|
const storage = multer.diskStorage({
|
||||||
|
destination: (req, file, cb) => {
|
||||||
|
cb(null, 'uploads/');
|
||||||
|
},
|
||||||
|
filename: (req, file, cb) => {
|
||||||
|
cb(null, Date.now() + '-' + file.originalname);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const upload = multer({ storage: storage });
|
||||||
|
|
||||||
|
// Создание директории uploads
|
||||||
|
const fs = require('fs');
|
||||||
|
if (!fs.existsSync('uploads')) {
|
||||||
|
fs.mkdirSync('uploads');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Маршруты
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.send(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>MY Uploader - Web Client</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||||
|
.header { text-align: center; margin-bottom: 40px; }
|
||||||
|
.upload-form { border: 2px dashed #ccc; padding: 40px; text-align: center; margin-bottom: 40px; }
|
||||||
|
.btn { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; }
|
||||||
|
.status { margin-top: 20px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h1>🚀 MY Uploader Bot</h1>
|
||||||
|
<p>Web Client для загрузки контента в MY Network</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="upload-form">
|
||||||
|
<h3>📁 Загрузка файла</h3>
|
||||||
|
<form id="uploadForm" enctype="multipart/form-data">
|
||||||
|
<input type="file" id="fileInput" name="file" required>
|
||||||
|
<br><br>
|
||||||
|
<button type="submit" class="btn">Загрузить</button>
|
||||||
|
</form>
|
||||||
|
<div id="status" class="status"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>📊 Статус системы</h3>
|
||||||
|
<p>Веб клиент: <span style="color: green;">Активен</span></p>
|
||||||
|
<p>API: <span id="apiStatus">Проверка...</span></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Проверка статуса API
|
||||||
|
fetch('/api/health')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('apiStatus').innerHTML = '<span style="color: green;">Активен</span>';
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
document.getElementById('apiStatus').innerHTML = '<span style="color: red;">Недоступен</span>';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработка загрузки файлов
|
||||||
|
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData();
|
||||||
|
const fileInput = document.getElementById('fileInput');
|
||||||
|
formData.append('file', fileInput.files[0]);
|
||||||
|
|
||||||
|
const status = document.getElementById('status');
|
||||||
|
status.innerHTML = 'Загрузка...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/upload', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
status.innerHTML = \`✅ \${result.message}\`;
|
||||||
|
} catch (error) {
|
||||||
|
status.innerHTML = \`❌ Ошибка: \${error.message}\`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/health', (req, res) => {
|
||||||
|
res.json({ status: 'healthy', module: 'web2-client' });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/upload', upload.single('file'), (req, res) => {
|
||||||
|
if (!req.file) {
|
||||||
|
return res.status(400).json({ error: 'Файл не выбран' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `Файл ${req.file.originalname} успешно загружен`,
|
||||||
|
filename: req.file.filename,
|
||||||
|
size: req.file.size
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Проксирование API запросов к основному приложению
|
||||||
|
app.use('/api', (req, res) => {
|
||||||
|
const apiUrl = process.env.API_URL || 'http://localhost:15100';
|
||||||
|
// Здесь можно добавить проксирование через http-proxy-middleware
|
||||||
|
res.json({ message: 'API проксирование не настроено' });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`🌐 Web2-Client запущен на порту ${PORT}`);
|
||||||
|
console.log(`📖 Откройте http://localhost:${PORT} в браузере`);
|
||||||
|
});
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Создание Dockerfile для web2-client
|
||||||
|
cat > "web2-client/Dockerfile" << 'EOF'
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Установка зависимостей
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
# Копирование кода
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Создание директорий
|
||||||
|
RUN mkdir -p uploads public
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ web2-client создан"
|
||||||
|
else
|
||||||
|
echo "ℹ️ web2-client уже существует"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# DOCKER COMPOSE ДЛЯ МОДУЛЕЙ
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo "🐳 Создание docker-compose для модулей..."
|
||||||
|
|
||||||
|
cat > "$MODULES_DIR/docker-compose.modules.yml" << 'EOF'
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Converter Module
|
||||||
|
converter:
|
||||||
|
build:
|
||||||
|
context: ./converter-module
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: my_converter
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- converter_data:/app/output
|
||||||
|
- converter_logs:/app/logs
|
||||||
|
environment:
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
networks:
|
||||||
|
- my_network
|
||||||
|
|
||||||
|
# Web2 Client
|
||||||
|
web2-client:
|
||||||
|
build:
|
||||||
|
context: ./web2-client
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: my_web2_client
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- web2_uploads:/app/uploads
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- API_URL=http://my-uploader-app:15100
|
||||||
|
networks:
|
||||||
|
- my_network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
converter_data:
|
||||||
|
converter_logs:
|
||||||
|
web2_uploads:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
my_network:
|
||||||
|
external: true
|
||||||
|
name: my-uploader-bot_uploader_network
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Docker Compose для модулей создан"
|
||||||
|
|
||||||
|
# Установка прав доступа
|
||||||
|
chmod +x "$MODULES_DIR/converter-module/app.py"
|
||||||
|
chmod +x "$MODULES_DIR/web2-client/server.js"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📦 Дополнительные модули настроены:"
|
||||||
|
echo " 🔄 converter-module: http://localhost:8080"
|
||||||
|
echo " 🌐 web2-client: http://localhost:3000"
|
||||||
|
echo ""
|
||||||
|
echo "Для запуска модулей:"
|
||||||
|
echo " cd $MODULES_DIR"
|
||||||
|
echo " docker-compose -f docker-compose.modules.yml up -d"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# НАСТРОЙКА PRODUCTION ОКРУЖЕНИЯ ДЛЯ MY UPLOADER BOT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Usage: $0 <domain> <email>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOMAIN="$1"
|
||||||
|
EMAIL="$2"
|
||||||
|
ENV_FILE=".env.production"
|
||||||
|
|
||||||
|
echo "Создание production конфигурации для $DOMAIN..."
|
||||||
|
|
||||||
|
# Генерация случайных ключей
|
||||||
|
SECRET_KEY=$(openssl rand -base64 48 | tr -d "=+/" | cut -c1-64)
|
||||||
|
JWT_SECRET_KEY=$(openssl rand -base64 48 | tr -d "=+/" | cut -c1-64)
|
||||||
|
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-32)
|
||||||
|
|
||||||
|
cat > "$ENV_FILE" << EOF
|
||||||
|
# =============================================================================
|
||||||
|
# MY UPLOADER BOT - PRODUCTION CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
# Domain: $DOMAIN
|
||||||
|
# Email: $EMAIL
|
||||||
|
# Generated: $(date)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ОБЯЗАТЕЛЬНЫЕ TELEGRAM ТОКЕНЫ (ТРЕБУЕТСЯ ЗАПОЛНИТЬ ВРУЧНУЮ!)
|
||||||
|
# =============================================================================
|
||||||
|
# Получить токены можно у @BotFather в Telegram
|
||||||
|
TELEGRAM_API_KEY=CHANGE_ME_123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
CLIENT_TELEGRAM_API_KEY=CHANGE_ME_987654321:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ДОМЕН И ХОСТ
|
||||||
|
# =============================================================================
|
||||||
|
PROJECT_HOST=https://$DOMAIN
|
||||||
|
DOMAIN=$DOMAIN
|
||||||
|
ACME_EMAIL=$EMAIL
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# БАЗА ДАННЫХ PostgreSQL
|
||||||
|
# =============================================================================
|
||||||
|
POSTGRES_DB=myuploader
|
||||||
|
POSTGRES_USER=uploader
|
||||||
|
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# REDIS
|
||||||
|
# =============================================================================
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ПРИЛОЖЕНИЕ (PRODUCTION SETTINGS)
|
||||||
|
# =============================================================================
|
||||||
|
SANIC_PORT=15100
|
||||||
|
DEBUG=false
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
MAINTENANCE_MODE=false
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# БЕЗОПАСНОСТЬ (АВТОГЕНЕРИРОВАННЫЕ КЛЮЧИ)
|
||||||
|
# =============================================================================
|
||||||
|
SECRET_KEY=$SECRET_KEY
|
||||||
|
JWT_SECRET_KEY=$JWT_SECRET_KEY
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TON BLOCKCHAIN (MAINNET)
|
||||||
|
# =============================================================================
|
||||||
|
TESTNET=false
|
||||||
|
TONCENTER_HOST=https://toncenter.com/api/v2/
|
||||||
|
TONCENTER_API_KEY=
|
||||||
|
TONCENTER_V3_HOST=https://toncenter.com/api/v3/
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ФОНОВЫЕ СЕРВИСЫ
|
||||||
|
# =============================================================================
|
||||||
|
INDEXER_ENABLED=true
|
||||||
|
TON_DAEMON_ENABLED=true
|
||||||
|
LICENSE_SERVICE_ENABLED=true
|
||||||
|
CONVERT_SERVICE_ENABLED=true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# МОНИТОРИНГ И МЕТРИКИ
|
||||||
|
# =============================================================================
|
||||||
|
METRICS_ENABLED=true
|
||||||
|
METRICS_PORT=9090
|
||||||
|
HEALTH_CHECK_ENABLED=true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ОГРАНИЧЕНИЯ СКОРОСТИ (PRODUCTION VALUES)
|
||||||
|
# =============================================================================
|
||||||
|
RATE_LIMIT_ENABLED=true
|
||||||
|
RATE_LIMIT_REQUESTS=1000
|
||||||
|
RATE_LIMIT_WINDOW=60
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ПРОИЗВОДИТЕЛЬНОСТЬ
|
||||||
|
# =============================================================================
|
||||||
|
DATABASE_POOL_SIZE=20
|
||||||
|
DATABASE_MAX_OVERFLOW=30
|
||||||
|
REDIS_POOL_SIZE=20
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# БЕЗОПАСНОСТЬ И ФАЙЛЫ
|
||||||
|
# =============================================================================
|
||||||
|
MAX_FILE_SIZE=104857600 # 100MB
|
||||||
|
UPLOADS_DIR=/app/data
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# GRAFANA И МОНИТОРИНГ
|
||||||
|
# =============================================================================
|
||||||
|
GRAFANA_PASSWORD=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-20)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BACKUP НАСТРОЙКИ
|
||||||
|
# =============================================================================
|
||||||
|
BACKUP_SCHEDULE=0 2 * * *
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WEB2-CLIENT НАСТРОЙКИ
|
||||||
|
# =============================================================================
|
||||||
|
WEB2_CLIENT_PORT=3000
|
||||||
|
WEB2_CLIENT_HOST=http://localhost:3000
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CONVERTER-MODULE НАСТРОЙКИ
|
||||||
|
# =============================================================================
|
||||||
|
CONVERTER_PORT=8080
|
||||||
|
CONVERTER_HOST=http://localhost:8080
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Production конфигурация создана: $ENV_FILE"
|
||||||
|
|
||||||
|
# Создание примера локального .env
|
||||||
|
cat > ".env.example" << EOF
|
||||||
|
# =============================================================================
|
||||||
|
# MY UPLOADER BOT - EXAMPLE CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
# Скопируйте этот файл в .env и заполните необходимые значения
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# ОБЯЗАТЕЛЬНЫЕ ПОЛЯ
|
||||||
|
TELEGRAM_API_KEY=123456789:YOUR_BOT_TOKEN_FROM_BOTFATHER
|
||||||
|
CLIENT_TELEGRAM_API_KEY=987654321:YOUR_CLIENT_BOT_TOKEN_FROM_BOTFATHER
|
||||||
|
|
||||||
|
# ОСНОВНЫЕ НАСТРОЙКИ
|
||||||
|
PROJECT_HOST=http://localhost:15100
|
||||||
|
DEBUG=true
|
||||||
|
LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
|
# БАЗА ДАННЫХ
|
||||||
|
POSTGRES_DB=uploader_bot
|
||||||
|
POSTGRES_USER=uploader
|
||||||
|
POSTGRES_PASSWORD=secure_password_123
|
||||||
|
|
||||||
|
# БЕЗОПАСНОСТЬ (СГЕНЕРИРУЙТЕ НОВЫЕ!)
|
||||||
|
SECRET_KEY=your-secret-key-here-minimum-32-characters
|
||||||
|
JWT_SECRET_KEY=your-jwt-secret-key-here-minimum-32-characters
|
||||||
|
|
||||||
|
# TON (ТЕСТОВАЯ СЕТЬ ДЛЯ РАЗРАБОТКИ)
|
||||||
|
TESTNET=true
|
||||||
|
TONCENTER_HOST=https://testnet.toncenter.com/api/v2/
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Пример конфигурации создан: .env.example"
|
||||||
|
|
||||||
|
# Установка правильных прав доступа
|
||||||
|
chmod 600 "$ENV_FILE"
|
||||||
|
chmod 644 ".env.example"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ ВАЖНО: Отредактируйте файл $ENV_FILE и заполните:"
|
||||||
|
echo " - TELEGRAM_API_KEY (получите у @BotFather)"
|
||||||
|
echo " - CLIENT_TELEGRAM_API_KEY (получите у @BotFather)"
|
||||||
|
echo " - TONCENTER_API_KEY (если используете TON mainnet)"
|
||||||
|
echo ""
|
||||||
|
echo "🔒 Сгенерированные секретные ключи:"
|
||||||
|
echo " - SECRET_KEY: $SECRET_KEY"
|
||||||
|
echo " - JWT_SECRET_KEY: $JWT_SECRET_KEY"
|
||||||
|
echo " - POSTGRES_PASSWORD: $POSTGRES_PASSWORD"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,348 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MY UPLOADER BOT - PRODUCTION SERVER SETUP SCRIPT
|
||||||
|
# =============================================================================
|
||||||
|
# Полная автоматическая установка на чистом Ubuntu/Debian сервере
|
||||||
|
#
|
||||||
|
# Использование:
|
||||||
|
# ./setup_production_server.sh yourdomain.com your@email.com
|
||||||
|
#
|
||||||
|
# Автор: MY Network Team
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e # Выход при любой ошибке
|
||||||
|
|
||||||
|
# Цвета для вывода
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
PURPLE='\033[0;35m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
header() {
|
||||||
|
echo -e "${PURPLE}"
|
||||||
|
echo "=============================================="
|
||||||
|
echo " $1"
|
||||||
|
echo "=============================================="
|
||||||
|
echo -e "${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка аргументов
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
error "Usage: $0 <domain> <email>"
|
||||||
|
echo "Example: $0 mydomain.com admin@mydomain.com"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOMAIN="$1"
|
||||||
|
EMAIL="$2"
|
||||||
|
PROJECT_DIR="/opt/my-uploader-bot"
|
||||||
|
NGINX_CONFIG="/etc/nginx/sites-available/my-uploader-bot"
|
||||||
|
SERVICE_USER="myuploader"
|
||||||
|
|
||||||
|
header "🚀 УСТАНОВКА MY UPLOADER BOT НА СЕРВЕР $DOMAIN"
|
||||||
|
|
||||||
|
log "Домен: $DOMAIN"
|
||||||
|
log "Email: $EMAIL"
|
||||||
|
log "Проект будет установлен в: $PROJECT_DIR"
|
||||||
|
|
||||||
|
# Проверка root прав
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
error "Этот скрипт должен запускаться с правами root (sudo)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка операционной системы
|
||||||
|
if ! command -v apt-get &> /dev/null; then
|
||||||
|
error "Этот скрипт поддерживает только Ubuntu/Debian системы"
|
||||||
|
fi
|
||||||
|
|
||||||
|
header "📦 ШАГ 1: ОБНОВЛЕНИЕ СИСТЕМЫ"
|
||||||
|
|
||||||
|
log "Обновление списка пакетов..."
|
||||||
|
apt-get update -y
|
||||||
|
|
||||||
|
log "Обновление установленных пакетов..."
|
||||||
|
apt-get upgrade -y
|
||||||
|
|
||||||
|
log "Установка базовых пакетов..."
|
||||||
|
apt-get install -y \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
git \
|
||||||
|
unzip \
|
||||||
|
software-properties-common \
|
||||||
|
apt-transport-https \
|
||||||
|
ca-certificates \
|
||||||
|
gnupg \
|
||||||
|
lsb-release \
|
||||||
|
htop \
|
||||||
|
nano \
|
||||||
|
vim \
|
||||||
|
net-tools \
|
||||||
|
ufw
|
||||||
|
|
||||||
|
header "🐳 ШАГ 2: УСТАНОВКА DOCKER"
|
||||||
|
|
||||||
|
# Удаление старых версий Docker
|
||||||
|
log "Удаление старых версий Docker..."
|
||||||
|
apt-get remove -y docker docker-engine docker.io containerd runc || true
|
||||||
|
|
||||||
|
# Добавление репозитория Docker
|
||||||
|
log "Добавление Docker репозитория..."
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||||
|
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
|
||||||
|
# Установка Docker
|
||||||
|
log "Установка Docker..."
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
|
||||||
|
# Установка Docker Compose standalone
|
||||||
|
log "Установка Docker Compose..."
|
||||||
|
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
chmod +x /usr/local/bin/docker-compose
|
||||||
|
ln -sf /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||||
|
|
||||||
|
# Запуск и включение Docker
|
||||||
|
systemctl enable docker
|
||||||
|
systemctl start docker
|
||||||
|
|
||||||
|
log "Docker версия: $(docker --version)"
|
||||||
|
log "Docker Compose версия: $(docker-compose --version)"
|
||||||
|
|
||||||
|
header "🔥 ШАГ 3: НАСТРОЙКА FIREWALL"
|
||||||
|
|
||||||
|
log "Настройка UFW firewall..."
|
||||||
|
|
||||||
|
# Сброс правил UFW
|
||||||
|
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"
|
||||||
|
|
||||||
|
# Включение firewall
|
||||||
|
ufw --force enable
|
||||||
|
|
||||||
|
log "Статус firewall:"
|
||||||
|
ufw status verbose
|
||||||
|
|
||||||
|
header "🔒 ШАГ 4: УСТАНОВКА CERTBOT"
|
||||||
|
|
||||||
|
log "Установка snapd..."
|
||||||
|
apt-get install -y snapd
|
||||||
|
|
||||||
|
log "Установка Certbot через snap..."
|
||||||
|
snap install core
|
||||||
|
snap refresh core
|
||||||
|
snap install --classic certbot
|
||||||
|
|
||||||
|
# Создание symlink
|
||||||
|
ln -sf /snap/bin/certbot /usr/bin/certbot
|
||||||
|
|
||||||
|
log "Certbot версия: $(certbot --version)"
|
||||||
|
|
||||||
|
header "👤 ШАГ 5: СОЗДАНИЕ ПОЛЬЗОВАТЕЛЯ СЕРВИСА"
|
||||||
|
|
||||||
|
log "Создание пользователя $SERVICE_USER..."
|
||||||
|
if ! id "$SERVICE_USER" &>/dev/null; then
|
||||||
|
useradd -r -s /bin/bash -d /home/$SERVICE_USER -m $SERVICE_USER
|
||||||
|
usermod -aG docker $SERVICE_USER
|
||||||
|
log "Пользователь $SERVICE_USER создан"
|
||||||
|
else
|
||||||
|
log "Пользователь $SERVICE_USER уже существует"
|
||||||
|
fi
|
||||||
|
|
||||||
|
header "📂 ШАГ 6: ПОДГОТОВКА ПРОЕКТА"
|
||||||
|
|
||||||
|
log "Создание директории проекта..."
|
||||||
|
mkdir -p $PROJECT_DIR
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
|
||||||
|
# Если это git репозиторий, клонируем. Иначе копируем текущую директорию
|
||||||
|
if [ -d "$(dirname $0)/.git" ]; then
|
||||||
|
log "Клонирование из Git репозитория..."
|
||||||
|
# Определяем URL репозитория
|
||||||
|
REPO_URL=$(cd "$(dirname $0)" && git config --get remote.origin.url || echo "")
|
||||||
|
if [ -n "$REPO_URL" ]; then
|
||||||
|
git clone "$REPO_URL" .
|
||||||
|
else
|
||||||
|
warn "Git репозиторий не найден, копирую файлы локально..."
|
||||||
|
cp -r "$(dirname $0)"/* .
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Копирование файлов проекта..."
|
||||||
|
cp -r "$(dirname $0)"/* .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Установка владельца
|
||||||
|
chown -R $SERVICE_USER:$SERVICE_USER $PROJECT_DIR
|
||||||
|
|
||||||
|
header "⚙️ ШАГ 7: НАСТРОЙКА NGINX"
|
||||||
|
|
||||||
|
log "Установка Nginx..."
|
||||||
|
apt-get install -y nginx
|
||||||
|
|
||||||
|
# Остановка Nginx для получения сертификатов
|
||||||
|
systemctl stop nginx
|
||||||
|
|
||||||
|
log "Создание конфигурации Nginx..."
|
||||||
|
./scripts/create_nginx_config.sh $DOMAIN $PROJECT_DIR
|
||||||
|
|
||||||
|
header "🔐 ШАГ 8: ПОЛУЧЕНИЕ SSL СЕРТИФИКАТОВ"
|
||||||
|
|
||||||
|
log "Получение Let's Encrypt сертификатов для $DOMAIN..."
|
||||||
|
|
||||||
|
# Остановка процессов на портах 80/443
|
||||||
|
fuser -k 80/tcp 2>/dev/null || true
|
||||||
|
fuser -k 443/tcp 2>/dev/null || true
|
||||||
|
|
||||||
|
# Получение сертификатов
|
||||||
|
certbot certonly \
|
||||||
|
--standalone \
|
||||||
|
--non-interactive \
|
||||||
|
--agree-tos \
|
||||||
|
--email "$EMAIL" \
|
||||||
|
--domains "$DOMAIN,www.$DOMAIN" \
|
||||||
|
--no-eff-email
|
||||||
|
|
||||||
|
log "Настройка автообновления сертификатов..."
|
||||||
|
systemctl enable certbot.timer
|
||||||
|
systemctl start certbot.timer
|
||||||
|
|
||||||
|
# Проверка автообновления
|
||||||
|
certbot renew --dry-run
|
||||||
|
|
||||||
|
header "🔧 ШАГ 9: КОНФИГУРАЦИЯ ПРОЕКТА"
|
||||||
|
|
||||||
|
log "Создание production конфигурации..."
|
||||||
|
./scripts/setup_production_env.sh $DOMAIN $EMAIL
|
||||||
|
|
||||||
|
header "📦 ШАГ 10: КОПИРОВАНИЕ ДОПОЛНИТЕЛЬНЫХ МОДУЛЕЙ"
|
||||||
|
|
||||||
|
log "Настройка дополнительных модулей..."
|
||||||
|
./scripts/setup_modules.sh $PROJECT_DIR
|
||||||
|
|
||||||
|
header "🐳 ШАГ 11: ЗАПУСК DOCKER СЕРВИСОВ"
|
||||||
|
|
||||||
|
log "Запуск Docker Compose в production режиме..."
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
|
||||||
|
# Запуск от имени пользователя сервиса
|
||||||
|
sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml pull
|
||||||
|
sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml build --no-cache
|
||||||
|
sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
log "Ожидание запуска сервисов..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Проверка статуса
|
||||||
|
log "Статус Docker сервисов:"
|
||||||
|
sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml ps
|
||||||
|
|
||||||
|
header "🌐 ШАГ 12: ЗАПУСК NGINX"
|
||||||
|
|
||||||
|
log "Включение конфигурации Nginx..."
|
||||||
|
ln -sf $NGINX_CONFIG /etc/nginx/sites-enabled/my-uploader-bot
|
||||||
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
|
log "Проверка конфигурации Nginx..."
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
log "Запуск Nginx..."
|
||||||
|
systemctl enable nginx
|
||||||
|
systemctl start nginx
|
||||||
|
|
||||||
|
header "🔄 ШАГ 13: НАСТРОЙКА АВТОЗАПУСКА"
|
||||||
|
|
||||||
|
log "Создание systemd сервисов..."
|
||||||
|
./scripts/create_systemd_services.sh $PROJECT_DIR $SERVICE_USER
|
||||||
|
|
||||||
|
header "✅ ШАГ 14: ФИНАЛЬНАЯ ПРОВЕРКА"
|
||||||
|
|
||||||
|
log "Проверка всех сервисов..."
|
||||||
|
|
||||||
|
# Проверка Docker
|
||||||
|
if sudo -u $SERVICE_USER docker-compose -f $PROJECT_DIR/docker-compose.production.yml ps | grep -q "Up"; then
|
||||||
|
log "✅ Docker сервисы: Работают"
|
||||||
|
else
|
||||||
|
error "❌ Docker сервисы: Не запущены"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка Nginx
|
||||||
|
if systemctl is-active --quiet nginx; then
|
||||||
|
log "✅ Nginx: Работает"
|
||||||
|
else
|
||||||
|
error "❌ Nginx: Не запущен"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка SSL
|
||||||
|
if curl -sf "https://$DOMAIN/health" > /dev/null; then
|
||||||
|
log "✅ SSL сертификат: Работает"
|
||||||
|
else
|
||||||
|
warn "⚠️ SSL проверка не прошла (сервис может еще запускаться)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка API
|
||||||
|
log "Ожидание полного запуска API..."
|
||||||
|
sleep 60
|
||||||
|
|
||||||
|
if curl -sf "https://$DOMAIN/api/my/health" > /dev/null; then
|
||||||
|
log "✅ MY Network API: Работает"
|
||||||
|
else
|
||||||
|
warn "⚠️ MY Network API пока не доступен"
|
||||||
|
fi
|
||||||
|
|
||||||
|
header "🎉 УСТАНОВКА ЗАВЕРШЕНА!"
|
||||||
|
|
||||||
|
info "=== ИНФОРМАЦИЯ О РАЗВЕРТЫВАНИИ ==="
|
||||||
|
info "Домен: https://$DOMAIN"
|
||||||
|
info "API: https://$DOMAIN/api/"
|
||||||
|
info "Мониторинг: https://$DOMAIN/api/my/monitor/"
|
||||||
|
info "Health Check: https://$DOMAIN/health"
|
||||||
|
info "Директория проекта: $PROJECT_DIR"
|
||||||
|
info "Пользователь: $SERVICE_USER"
|
||||||
|
info "Nginx конфиг: $NGINX_CONFIG"
|
||||||
|
|
||||||
|
info "=== ПОЛЕЗНЫЕ КОМАНДЫ ==="
|
||||||
|
info "Просмотр логов: cd $PROJECT_DIR && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml logs -f"
|
||||||
|
info "Перезапуск: cd $PROJECT_DIR && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml restart"
|
||||||
|
info "Обновление: cd $PROJECT_DIR && git pull && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml build --no-cache && sudo -u $SERVICE_USER docker-compose -f docker-compose.production.yml up -d"
|
||||||
|
|
||||||
|
info "=== БЕЗОПАСНОСТЬ ==="
|
||||||
|
info "Firewall: ufw status"
|
||||||
|
info "SSL сертификаты: ls -la /etc/letsencrypt/live/$DOMAIN/"
|
||||||
|
info "Автообновление SSL: systemctl status certbot.timer"
|
||||||
|
|
||||||
|
log "🚀 Проект успешно развернут на https://$DOMAIN"
|
||||||
|
log "📝 Не забудьте настроить Telegram токены в $PROJECT_DIR/.env.production"
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
Reference in New Issue