#!/bin/bash # ============================================================================= # PaganOS Web v2 – Full Auto-Installer # bash install.sh [--domain paganlinux.eu] [--email admin@paganlinux.eu] # Wysyłasz z pagan-web-v2/, wpisujesz bash install.sh i wszystko samo działa. # ============================================================================= set -euo pipefail # ── Kolory ── G='\033[32m'; C='\033[36m'; R='\033[31m'; Y='\033[33m'; B='\033[1m'; N='\033[0m' banner() { echo -e "${C}═══ $* ${N}"; } ok() { echo -e " ${G}✅${N} $*"; } warn() { echo -e " ${Y}⚠${N} $*"; } err() { echo -e " ${R}❌${N} $*"; } # ── Opcje ── DOMAIN="${DOMAIN:-paganlinux.eu}" EMAIL="${EMAIL:-admin@paganlinux.eu}" SETUP_SSL="${SETUP_SSL:-yes}" SETUP_FIREWALL="${SETUP_FIREWALL:-yes}" GPG_KEY_ID="${GPG_KEY_ID:-}" while [[ $# -gt 0 ]]; do case "$1" in --domain) DOMAIN="$2"; shift 2 ;; --email) EMAIL="$2"; shift 2 ;; --no-ssl) SETUP_SSL="no"; shift ;; --no-fw) SETUP_FIREWALL="no"; shift ;; --gpg-key) GPG_KEY_ID="$2"; shift 2 ;; -h|--help) echo "Użycie: bash install.sh [opcje]" echo " --domain DOMAIN Domena główna (domyślnie: paganlinux.eu)" echo " --email EMAIL Email do Let's Encrypt" echo " --no-ssl Pomiń SSL/Certbot" echo " --no-fw Pomiń firewall" echo " --gpg-key KEYID ID klucza GPG do podpisywania pakietów" exit 0 ;; *) echo "Nieznana opcja: $1"; exit 1 ;; esac done # ── Root check ── if [[ $EUID -ne 0 ]]; then echo -e "${R}❌ Musisz uruchomić jako root: sudo bash install.sh${N}" exit 1 fi echo -e "${C}" echo "╔══════════════════════════════════════════════════╗" echo "║ PaganOS Web v2 – Pełna Instalacja ║" echo "║ Domena: $DOMAIN" echo "║ SSL: $SETUP_SSL | FW: $SETUP_FIREWALL" echo "╚══════════════════════════════════════════════════╝" echo -e "${N}" SRC="$(dirname "$(readlink -f "$0")")" DST="/opt/pagan-web-v2" # ═══════════════════════════════════════════════════════════════ # 1. Pakiety systemowe # ═══════════════════════════════════════════════════════════════ banner "1/7 – Pakiety systemowe" install_pkg() { if command -v apt &>/dev/null; then DEBIAN_FRONTEND=noninteractive apt install -y -qq "$@" 2>/dev/null || apt install -y "$@" elif command -v pacman &>/dev/null; then pacman -Sy --noconfirm "$@" 2>/dev/null || true elif command -v dnf &>/dev/null; then dnf install -y "$@" 2>/dev/null || true else warn "Nieznany menedżer pakietów – zainstaluj ręcznie: $*" fi } install_pkg python3 python3-pip git nginx sqlite3 curl certbot python3-certbot-nginx ufw pip3 install --break-system-packages flask pygments requests pyyaml mistune 2>/dev/null || \ pip3 install flask pygments requests pyyaml mistune 2>/dev/null || true ok "Pakiety gotowe" # ═══════════════════════════════════════════════════════════════ # 2. Kopiowanie aplikacji # ═══════════════════════════════════════════════════════════════ banner "2/7 – Kopiowanie aplikacji do $DST" if [ "$SRC" != "$DST" ]; then rm -rf "$DST" cp -r "$SRC" "$DST" ok "Skopiowano → $DST" else ok "Już w $DST" fi # Nadaj uprawnienia wykonywalne chmod +x "$DST/app.py" "$DST/pagbuild" "$DST/pagsync" "$DST/pag" 2>/dev/null || true # ═══════════════════════════════════════════════════════════════ # 3. Katalogi i symlinki # ═══════════════════════════════════════════════════════════════ banner "3/7 – Katalogi i struktura" mkdir -p /var/lib/pagan-web mkdir -p /var/lib/pagan-sync mkdir -p /var/lib/pagan-build/rootfs mkdir -p /var/cache/pagbuild/{sources,output} mkdir -p /var/git mkdir -p /var/www/repo.paganlinux.eu/stable mkdir -p /var/www/repo.paganlinux.eu/sources mkdir -p /etc/pagan # Symlink recipes if [ ! -d "/var/lib/pagan-sync/recipes/core" ]; then RECIPES_SRC="$(readlink -f "$SRC/../recipes")" if [ -d "$RECIPES_SRC" ]; then ln -sf "$RECIPES_SRC" /var/lib/pagan-sync/recipes ok "recipes → $(basename "$RECIPES_SRC") (symlink)" else mkdir -p /var/lib/pagan-sync/recipes warn "Brak katalogu recipes/ obok pagan-web-v2/ – utworzono pusty" fi else ok "recipes już istnieje" fi # Symlink narzędzi do /usr/bin ln -sf "$DST/pagbuild" /usr/bin/pagbuild 2>/dev/null || true ln -sf "$DST/pagsync" /usr/bin/pagsync 2>/dev/null || true ln -sf "$DST/pag" /usr/bin/pag 2>/dev/null || true ok "Symlinki: pagbuild, pagsync, pag → /usr/bin" # ═══════════════════════════════════════════════════════════════ # 4. GPG – klucz do podpisywania pakietów # ═══════════════════════════════════════════════════════════════ banner "4/7 – GPG (podpisywanie pakietów)" if [[ -n "$GPG_KEY_ID" ]]; then echo "PAGAN_DO_SIGN=1" > /etc/pagan/build.conf echo "PAGAN_GPG_KEY=$GPG_KEY_ID" >> /etc/pagan/build.conf ok "GPG: klucz $GPG_KEY_ID – podpisywanie włączone" elif gpg --list-secret-keys 2>/dev/null | grep -q "^sec"; then KEY_ID=$(gpg --list-secret-keys --with-colons 2>/dev/null | grep "^sec:" | head -1 | cut -d: -f5) echo "PAGAN_DO_SIGN=1" > /etc/pagan/build.conf echo "PAGAN_GPG_KEY=$KEY_ID" >> /etc/pagan/build.conf ok "GPG: wykryto klucz $KEY_ID – podpisywanie włączone" else echo "PAGAN_DO_SIGN=0" > /etc/pagan/build.conf warn "Brak klucza GPG – podpisywanie wyłączone" warn "Wygeneruj: gpg --gen-key | potem: bash install.sh --gpg-key KLUCZ" fi # ═══════════════════════════════════════════════════════════════ # 5. Nginx – konfiguracja tymczasowa (HTTP only, dla certbota) # ═══════════════════════════════════════════════════════════════ banner "5/7 – Nginx (konfiguracja tymczasowa HTTP)" NGINX_CONF="/etc/nginx/sites-available/pagan-web" cat > "$NGINX_CONF" << 'NGINXHTTP' # ═══════════════════════════════════════════════════════════════ # PaganOS Web v2 – Tymczasowy HTTP (do czasu uzyskania certyfikatu) # ═══════════════════════════════════════════════════════════════ upstream pagan_web { server 127.0.0.1:8000 fail_timeout=0; } server { listen 80; server_name _; # Let's Encrypt challenge location /.well-known/acme-challenge/ { root /var/www/certbot; } location /repo/download/ { alias /var/www/repo.paganlinux.eu/; expires 30d; } location /static/ { alias /opt/pagan-web-v2/static/; expires 7d; } location /build/api/stream { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; chunked_transfer_encoding on; } location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } NGINXHTTP ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/pagan-web 2>/dev/null || true rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true nginx -t && systemctl restart nginx 2>/dev/null || nginx -s reload 2>/dev/null || warn "nginx – sprawdź konfigurację" ok "Nginx HTTP – uruchomiony" # ═══════════════════════════════════════════════════════════════ # 6. SSL – Let's Encrypt (Certbot) # ═══════════════════════════════════════════════════════════════ banner "6/7 – SSL / Let's Encrypt" CERT_OK=false if [[ "$SETUP_SSL" == "yes" ]]; then if [[ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]]; then ok "Certyfikat SSL już istnieje dla $DOMAIN" CERT_OK=true else mkdir -p /var/www/certbot sleep 2 echo -e " ${Y}▶${N} Pobieranie certyfikatu SSL..." # Najpierw spróbuj certonly (bez ingerencji w nginx config) certbot certonly --webroot \ --non-interactive --agree-tos \ -m "$EMAIL" \ -w /var/www/certbot \ -d "${DOMAIN}" \ -d "www.${DOMAIN}" \ -d "git.${DOMAIN}" \ -d "repo.${DOMAIN}" \ -d "build.${DOMAIN}" \ -d "docs.${DOMAIN}" \ 2>&1 | sed 's/^/ │ /' || { warn "certonly nie zadziałał, próbuję --nginx..." certbot --nginx \ --non-interactive --agree-tos \ -m "$EMAIL" \ -d "${DOMAIN}" \ -d "www.${DOMAIN}" \ -d "git.${DOMAIN}" \ -d "repo.${DOMAIN}" \ -d "build.${DOMAIN}" \ -d "docs.${DOMAIN}" \ 2>&1 | sed 's/^/ │ /' || true } if [[ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]]; then ok "Certyfikat SSL pobrany!" CERT_OK=true # Cron do auto-odnawiania cat > /etc/cron.daily/pagan-cert-renew << 'CRONEOF' #!/bin/bash certbot renew --quiet --webroot -w /var/www/certbot systemctl reload nginx CRONEOF chmod +x /etc/cron.daily/pagan-cert-renew ok "Auto-renew SSL dodany (cron.daily)" else warn "Certbot nie mógł pobrać certyfikatu." warn "Sprawdź DNS (wszystkie subdomeny muszą wskazywać na ten VPS) i port 80." warn "Potem uruchom: certbot certonly --webroot -w /var/www/certbot -d paganlinux.eu" fi fi else warn "SSL pominięte (--no-ssl)" fi # ═══════════════════════════════════════════════════════════════ # 5b. Nginx – pełna konfiguracja HTTPS (po uzyskaniu certyfikatu) # ═══════════════════════════════════════════════════════════════ banner "5b – Nginx (konfiguracja HTTPS)" if [[ "$CERT_OK" == "true" ]]; then cat > "$NGINX_CONF" << NGINXHTTPS # ═══════════════════════════════════════════════════════════════ # PaganOS Web v2 – Pełna konfiguracja HTTPS # Auto-generated: $(date) # ═══════════════════════════════════════════════════════════════ upstream pagan_web { server 127.0.0.1:8000 fail_timeout=0; } # ── HTTP → HTTPS redirect ── server { listen 80; server_name ${DOMAIN} www.${DOMAIN} git.${DOMAIN} repo.${DOMAIN} build.${DOMAIN} docs.${DOMAIN}; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://\$host\$request_uri; } } # ── HTTPS: Główna + www ── server { listen 443 ssl; http2 on; server_name ${DOMAIN} www.${DOMAIN}; ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; client_max_body_size 500m; location /download/ { alias /var/www/repo.paganlinux.eu/; autoindex on; autoindex_format html; expires 30d; add_header Cache-Control "public, immutable"; } location /sources/ { alias /var/www/repo.paganlinux.eu/sources/; expires 7d; } location /static/ { alias /opt/pagan-web-v2/static/; expires 7d; } location /build/api/stream { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; chunked_transfer_encoding on; } location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; } } # ── HTTPS: Git ── server { listen 443 ssl; http2 on; server_name git.${DOMAIN}; ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; client_max_body_size 500m; location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host git.${DOMAIN}; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; } } # ── HTTPS: Repo ── server { listen 443 ssl; http2 on; server_name repo.${DOMAIN}; ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; location /download/ { alias /var/www/repo.paganlinux.eu/; autoindex on; autoindex_format html; expires 30d; add_header Cache-Control "public, immutable"; } location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host repo.${DOMAIN}; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; } } # ── HTTPS: Build ── server { listen 443 ssl; http2 on; server_name build.${DOMAIN}; ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; location /api/stream { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host build.${DOMAIN}; proxy_set_header X-Forwarded-Proto https; proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; chunked_transfer_encoding on; } location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host build.${DOMAIN}; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; } } # ── HTTPS: Docs ── server { listen 443 ssl; http2 on; server_name docs.${DOMAIN}; ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; location / { proxy_pass http://pagan_web; proxy_http_version 1.1; proxy_set_header Host docs.${DOMAIN}; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Real-IP \$remote_addr; } } NGINXHTTPS nginx -t && systemctl reload nginx 2>/dev/null || nginx -s reload 2>/dev/null || warn "nginx reload – sprawdź" ok "Nginx HTTPS – pełna konfiguracja z subdomenami" else # SSL się nie udał – zostawiamy HTTP warn "SSL niedostępne – zostawiam konfigurację HTTP" warn "Po uzyskaniu certyfikatu uruchom ponownie install.sh" fi # ═══════════════════════════════════════════════════════════════ # 7. Systemd + Firewall + Start # ═══════════════════════════════════════════════════════════════ banner "7/7 – Usługi i finalizacja" # ── Systemd service ── cat > /etc/systemd/system/pagan-web.service << UNITEOF [Unit] Description=PaganOS Web v2 – Unified Platform After=network.target nginx.service Wants=network.target [Service] Type=simple ExecStart=/usr/bin/python3 /opt/pagan-web-v2/app.py WorkingDirectory=/opt/pagan-web-v2 Restart=always RestartSec=5 User=root EnvironmentFile=-/etc/pagan/build.conf Environment=PYTHONUNBUFFERED=1 StandardOutput=journal StandardError=journal # Bezpieczeństwo NoNewPrivileges=false PrivateTmp=false [Install] WantedBy=multi-user.target UNITEOF systemctl daemon-reload systemctl enable pagan-web 2>/dev/null || true ok "systemd: pagan-web.service" # ── Firewall ── if [[ "$SETUP_FIREWALL" == "yes" ]] && command -v ufw &>/dev/null; then ufw --force reset >/dev/null 2>&1 || true ufw default deny incoming >/dev/null 2>&1 || true ufw default allow outgoing >/dev/null 2>&1 || true ufw allow 22/tcp comment "SSH" >/dev/null 2>&1 || true ufw allow 80/tcp comment "HTTP" >/dev/null 2>&1 || true ufw allow 443/tcp comment "HTTPS" >/dev/null 2>&1 || true ufw --force enable >/dev/null 2>&1 || true ok "Firewall (ufw): SSH + HTTP + HTTPS" fi # ── Cron – auto-build co 6h + codzienne czyszczenie cache ── cat > /etc/cron.d/pagan-build << 'CRONBUILD' # PaganOS – auto-build brakujących pakietów co 6 godzin 0 */6 * * * root /opt/pagan-web-v2/pagsync --once --missing >> /var/log/pagan-auto-build.log 2>&1 # Codzienne czyszczenie cache źródeł o 3:00 0 3 * * * root /opt/pagan-web-v2/pagsync --clean-cache >> /var/log/pagan-auto-build.log 2>&1 CRONBUILD chmod 644 /etc/cron.d/pagan-build ok "Cron: auto-build co 6h + clean-cache codziennie" # ── Restart usług ── nginx -t 2>/dev/null && systemctl restart nginx 2>/dev/null || nginx -s reload 2>/dev/null || warn "nginx restart – sprawdź konfigurację" systemctl restart pagan-web 2>/dev/null || warn "pagan-web restart – sprawdź: systemctl status pagan-web" # ═══════════════════════════════════════════════════════════════ # Podsumowanie # ═══════════════════════════════════════════════════════════════ echo "" echo -e "${G}╔══════════════════════════════════════════════════════════╗${N}" echo -e "${G}║ ✅ PaganOS Web v2 – Instalacja zakończona! ║${N}" echo -e "${G}╚══════════════════════════════════════════════════════════╝${N}" echo "" echo -e " ${B}🌐 Strony:${N}" echo -e " Główna: https://${DOMAIN}" echo -e " Git: https://git.${DOMAIN}" echo -e " Repo: https://repo.${DOMAIN}" echo -e " Build: https://build.${DOMAIN}" echo -e " Docs: https://docs.${DOMAIN}" echo "" echo -e " ${B}🔧 Zarządzanie:${N}" echo -e " Status: systemctl status pagan-web" echo -e " Logi: journalctl -u pagan-web -f" echo -e " Restart: systemctl restart pagan-web" echo -e " Nginx: nginx -t && systemctl reload nginx" echo "" echo -e " ${B}📦 Budowanie pakietów:${N}" echo -e " Wszystkie: pagsync --once" echo -e " Force: pagsync --once --force" echo -e " Jeden: pagsync --build nazwa" echo -e " Z GPG: pagsync --once --sign" echo "" echo -e " ${B}🔑 Panel admin:${N}" echo -e " Login: admin / paganadmin2024" echo -e " Build: https://build.${DOMAIN}" echo -e "" if [[ -f "/etc/pagan/build.conf" ]]; then echo -e " ${B}📋 Konfiguracja build:${N} /etc/pagan/build.conf" cat /etc/pagan/build.conf | sed 's/^/ /' fi echo ""