From b0ef3197281377b5a29059f2230160e76a8274b2 Mon Sep 17 00:00:00 2001 From: bocil kematian Date: Mon, 22 Jun 2026 23:56:10 +0700 Subject: [PATCH] chore: migrate application to gunicorn server and add deployment configuration files --- ecosystem.config.js | 20 +++++++++--- nginx.conf | 73 ++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + setup.sh | 78 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 nginx.conf create mode 100644 setup.sh diff --git a/ecosystem.config.js b/ecosystem.config.js index 1b84bd8..98bd384 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -2,18 +2,28 @@ module.exports = { apps: [ { name: 'vsphere-backup-manager', - script: 'gui_app.py', - // If you are using a virtual environment (recommended), set the interpreter to: - // interpreter: './venv/bin/python3', - interpreter: 'python3', + // Gunicorn as the production WSGI server + // If using a virtualenv, replace 'gunicorn' with './venv/bin/gunicorn' + script: 'gunicorn', + args: [ + '--workers', '4', + '--bind', '127.0.0.1:5000', + '--timeout', '300', // long timeout for backup operations + '--keep-alive', '5', + '--log-level', 'info', + '--access-logfile', './logs/gunicorn_access.log', + '--error-logfile', './logs/gunicorn_error.log', + 'gui_app:app' + ].join(' '), + interpreter: 'none', // gunicorn is its own executable cwd: './', instances: 1, autorestart: true, watch: false, max_memory_restart: '500M', env: { - PORT: '5000', SECRET_KEY: 'vsphere-backup-production-key-change-this' + // PORT is no longer used; gunicorn binds via --bind above }, error_file: './logs/pm2_err.log', out_file: './logs/pm2_out.log', diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9634100 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,73 @@ +# ─── vSphere Backup Manager — Nginx reverse proxy ──────────────────────────── +# +# Place this file at: /etc/nginx/sites-available/vsphere-backup-manager +# Then enable it with: sudo ln -s /etc/nginx/sites-available/vsphere-backup-manager \ +# /etc/nginx/sites-enabled/ +# +# Assumes: +# - SSL cert + key are at /etc/ssl/vsphere-backup/ +# - Gunicorn is running on 127.0.0.1:5000 (via PM2) +# ───────────────────────────────────────────────────────────────────────────── + +# Redirect plain HTTP → HTTPS +server { + listen 80; + server_name _; # catches any hostname / IP + + return 301 https://$host$request_uri; +} + +# Main HTTPS block +server { + listen 443 ssl; + server_name _; + + # ── SSL certificate (self-signed, generated by setup.sh) ────────────────── + ssl_certificate /etc/ssl/vsphere-backup/cert.pem; + ssl_certificate_key /etc/ssl/vsphere-backup/key.pem; + + # ── TLS hardening ───────────────────────────────────────────────────────── + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # ── Security headers ────────────────────────────────────────────────────── + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Referrer-Policy strict-origin-when-cross-origin; + # Force HTTPS for 1 year — remove this line if you ever need to switch back to HTTP + add_header Strict-Transport-Security "max-age=31536000" always; + + # ── Proxy to Gunicorn ───────────────────────────────────────────────────── + location / { + proxy_pass http://127.0.0.1:5000; + 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; + + # Long timeouts — backup jobs can run for hours + proxy_read_timeout 3600; + proxy_connect_timeout 60; + proxy_send_timeout 3600; + + # Allow large log downloads + client_max_body_size 64M; + } + + # ── Static files served directly by Nginx (faster than Python) ─────────── + location /static/ { + alias /home/rizqiv2/vSphere-Backup-Manager/static/; + expires 7d; + add_header Cache-Control "public"; + } + + # ── Logging ─────────────────────────────────────────────────────────────── + access_log /var/log/nginx/vsphere-backup-access.log; + error_log /var/log/nginx/vsphere-backup-error.log; +} diff --git a/requirements.txt b/requirements.txt index 19c0126..95701e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ paramiko zstandard APScheduler>=3.10 Flask>=2.3 +gunicorn>=21.0 diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..afcbe48 --- /dev/null +++ b/setup.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# ============================================================================= +# setup.sh — One-time setup for vSphere Backup Manager (Nginx + Gunicorn + HTTPS) +# Run as a user with sudo privileges. +# Usage: bash setup.sh +# ============================================================================= +set -e + +APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SSL_DIR="/etc/ssl/vsphere-backup" +NGINX_CONF="/etc/nginx/sites-available/vsphere-backup-manager" +NGINX_LINK="/etc/nginx/sites-enabled/vsphere-backup-manager" + +echo "" +echo "==========================================" +echo " vSphere Backup Manager — Setup Script" +echo "==========================================" +echo "" + +# ── 1. Install system packages ──────────────────────────────────────────────── +echo "[1/6] Installing Nginx..." +sudo apt-get update -qq +sudo apt-get install -y nginx + +# ── 2. Install Python packages ──────────────────────────────────────────────── +echo "[2/6] Installing Python dependencies (including gunicorn)..." +# If you use a virtualenv, activate it first: +# source ./venv/bin/activate +pip install -r "$APP_DIR/requirements.txt" + +# ── 3. Generate self-signed SSL certificate (10-year validity) ──────────────── +echo "[3/6] Generating self-signed TLS certificate (10 years)..." +sudo mkdir -p "$SSL_DIR" +sudo openssl req -x509 -nodes -newkey rsa:4096 \ + -days 3650 \ + -keyout "$SSL_DIR/key.pem" \ + -out "$SSL_DIR/cert.pem" \ + -subj "/C=XX/ST=Local/L=Local/O=vSphereBackup/CN=vsphere-backup-manager" \ + -addext "subjectAltName=IP:127.0.0.1,IP:$(hostname -I | awk '{print $1}')" +sudo chmod 600 "$SSL_DIR/key.pem" +sudo chmod 644 "$SSL_DIR/cert.pem" +echo " Certificate saved to: $SSL_DIR" + +# ── 4. Install Nginx config ─────────────────────────────────────────────────── +echo "[4/6] Installing Nginx config..." +sudo cp "$APP_DIR/nginx.conf" "$NGINX_CONF" +# Remove default site if it exists +sudo rm -f /etc/nginx/sites-enabled/default +# Create symlink to enable our site +sudo ln -sf "$NGINX_CONF" "$NGINX_LINK" +sudo nginx -t # validate config before applying +sudo systemctl enable nginx +sudo systemctl restart nginx +echo " Nginx configured and running." + +# ── 5. Create logs directory ────────────────────────────────────────────────── +echo "[5/6] Creating logs directory..." +mkdir -p "$APP_DIR/logs" + +# ── 6. (Re)start with PM2 ──────────────────────────────────────────────────── +echo "[6/6] Starting app with PM2..." +cd "$APP_DIR" +# Stop old instance if running (ignore error if not) +pm2 delete vsphere-backup-manager 2>/dev/null || true +pm2 start ecosystem.config.js +pm2 save + +echo "" +echo "==========================================" +echo " Setup complete!" +echo "" +echo " Access the app at:" +echo " https://$(hostname -I | awk '{print $1}')" +echo "" +echo " First visit: browser will warn about self-signed cert." +echo " Click: Advanced → Proceed (one-time per browser)" +echo "==========================================" +echo ""