Files
worst-scan-web/install.sh
T
Renato 225bb491a6 feat: direct VPS installer — Node.js 22 + Caddy + systemd, no Docker
Replaces the Docker-based install.sh with a direct-install approach:
- Docker cleanup: removes container, stops daemon, uninstalls packages
- Node.js 22 via NodeSource apt/rpm/pacman repos
- Caddy via Cloudsmith apt/COPR/pacman repos
- Clone, npm ci, npm run build, standalone output
- systemd unit (worst-scan-web.service) — user-level service, HOSTNAME=127.0.0.1
- Caddy reverse proxy on :80 → 127.0.0.1:3000
- deploy/ reference files for manual editing

~150-160 MB RAM total (vs ~300-400 MB with Docker) on 1GB VPS
2026-07-28 00:24:16 +02:00

257 lines
9.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ──────────────────────────────────────────────
# worst-scan-web — Direct installer (no Docker)
# Usage: bash <(curl -fsSL https://gitea.cbcren.online/renato97/worst-scan-web/raw/branch/main/install.sh)
# ──────────────────────────────────────────────
REPO_URL="https://gitea.cbcren.online/renato97/worst-scan-web.git"
INSTALL_DIR="${HOME}/worst-scan-web"
BRANCH="main"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
log() { printf "${GREEN}${NC} %s\n" "$1"; }
warn() { printf "${YELLOW}${NC} %s\n" "$1"; }
err() { printf "${RED}${NC} %s\n" "$1"; }
info() { printf "${CYAN}${NC} %s\n" "$1"; }
# ── OS detection ────────────────────────────────
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release && echo "$ID"
elif command -v lsb_release &>/dev/null; then
lsb_release -si | tr '[:upper:]' '[:lower:]'
else echo "unknown"; fi
}
OS=$(detect_os)
bail() { err "Unsupported OS: $OS. Install manually: $1"; exit 1; }
# ── Sudo check ─────────────────────────────────
sudo -v || (err "sudo required" && exit 1)
# ── git + curl ─────────────────────────────────
for cmd in git curl; do
if ! command -v "$cmd" &>/dev/null; then
info "Installing $cmd..."
case "$OS" in
ubuntu|debian) sudo apt update -qq && sudo apt install -y -qq "$cmd" ;;
arch|endeavouros) sudo pacman -S --noconfirm "$cmd" ;;
fedora|centos|rhel|*) sudo dnf install -y "$cmd" ;;
esac
log "$cmd installed"
fi
done
# ── Docker cleanup ──────────────────────────────
if command -v docker &>/dev/null; then
info "Cleaning up Docker..."
if [ -f "$INSTALL_DIR/docker-compose.yml" ]; then
(cd "$INSTALL_DIR" && docker compose down) 2>/dev/null || true
fi
sudo docker stop worst-scan-web 2>/dev/null || true
sudo docker rm worst-scan-web 2>/dev/null || true
sudo systemctl stop docker docker.socket 2>/dev/null || true
sudo systemctl disable docker docker.socket 2>/dev/null || true
sudo apt remove -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras 2>/dev/null || true
sudo rm -f /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.asc
sudo apt update -qq 2>/dev/null || true
log "Docker removed"
fi
# ── Node.js 22 ──────────────────────────────────
if ! command -v node &>/dev/null || [ "$(node -v | cut -d. -f1 | tr -d v)" -lt 22 ]; then
info "Installing Node.js 22..."
case "$OS" in
ubuntu|debian)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash
sudo apt install -y -qq nodejs
;;
arch|endeavouros)
sudo pacman -S --noconfirm nodejs npm
;;
fedora)
curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash
sudo dnf install -y nodejs
;;
*) bail "https://nodejs.org/en/download/" ;;
esac
log "Node.js $(node -v) installed"
else
log "Node.js $(node -v) already installed"
fi
# ── Caddy ────────────────────────────────────────
if ! command -v caddy &>/dev/null; then
info "Installing Caddy..."
case "$OS" in
ubuntu|debian)
sudo apt install -y -qq debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' | sudo bash
sudo apt install -y -qq caddy
;;
arch|endeavouros)
sudo pacman -S --noconfirm caddy
;;
fedora)
sudo dnf install -y 'dnf-command(copr)'
sudo dnf copr enable -y @caddy/caddy
sudo dnf install -y caddy
;;
*) bail "https://caddyserver.com/docs/install" ;;
esac
log "Caddy $(caddy version | cut -d' ' -f1) installed"
else
log "Caddy already installed"
fi
# ── Clone / Pull ─────────────────────────────────
if [ -d "$INSTALL_DIR" ]; then
info "Updating repository..."
cd "$INSTALL_DIR"
git fetch origin "$BRANCH"
git reset --hard "origin/$BRANCH"
log "Updated to latest commit"
else
info "Cloning repository..."
git clone --depth=1 -b "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
cd "$INSTALL_DIR"
log "Repository cloned"
fi
INSTALL_DIR="$(cd "$INSTALL_DIR" && pwd)"
# ── .env ─────────────────────────────────────────
if [ -f "$INSTALL_DIR/.env" ]; then
warn ".env already exists — not overwriting"
else
cp "$INSTALL_DIR/.env.example" "$INSTALL_DIR/.env"
log "Created .env from .env.example"
fi
# ── Data directory ────────────────────────────────
mkdir -p "$INSTALL_DIR/data"
log "Data directory ready"
# ── Build ────────────────────────────────────────
info "Installing npm dependencies..."
cd "$INSTALL_DIR"
npm ci
log "Dependencies installed"
info "Building Next.js app (this may take a few minutes)..."
npm run build
log "Build complete"
STANDALONE_DIR="$INSTALL_DIR/.next/standalone"
# Ensure static files are copied
if [ ! -d "$STANDALONE_DIR/.next/static" ]; then
mkdir -p "$STANDALONE_DIR/.next"
cp -r "$INSTALL_DIR/.next/static" "$STANDALONE_DIR/.next/static"
fi
cp -r "$INSTALL_DIR/public" "$STANDALONE_DIR/public" 2>/dev/null || true
# ── systemd service ──────────────────────────────
SERVICE_NAME="worst-scan-web"
APP_USER="$USER"
APP_HOME="$HOME"
sudo tee "/etc/systemd/system/$SERVICE_NAME.service" > /dev/null <<SYSTEMD
[Unit]
Description=worst-scan-web
After=network.target
[Service]
Type=exec
User=$APP_USER
WorkingDirectory=$STANDALONE_DIR
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=HOSTNAME=127.0.0.1
Environment=DB_PATH=$INSTALL_DIR/data/worst-scan.db
Environment=COVERS_DIR=$INSTALL_DIR/data/covers
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
SYSTEMD
sudo systemctl daemon-reload
sudo systemctl enable "$SERVICE_NAME"
sudo systemctl restart "$SERVICE_NAME"
log "systemd service $SERVICE_NAME started"
# ── Caddy reverse proxy ──────────────────────────
CADDYFILE="/etc/caddy/Caddyfile"
CADDY_SITE_MARK="# worst-scan-web (auto-installed)"
if grep -q "$CADDY_SITE_MARK" "$CADDYFILE" 2>/dev/null; then
warn "Caddy config already contains worst-scan-web block — not modifying"
else
sudo mkdir -p "$(dirname "$CADDYFILE")"
if [ -s "$CADDYFILE" ] && [ "$(wc -l < "$CADDYFILE")" -gt 2 ]; then
EXISTING_IMPORT=$(grep -c 'import' "$CADDYFILE" 2>/dev/null || true)
if [ "$EXISTING_IMPORT" -eq 0 ]; then
sudo tee -a "$CADDYFILE" > /dev/null <<CADDY
$CADDY_SITE_MARK
:80 {
reverse_proxy 127.0.0.1:3000
}
CADDY
fi
else
sudo tee "$CADDYFILE" > /dev/null <<CADDY
$CADDY_SITE_MARK
:80 {
reverse_proxy 127.0.0.1:3000
}
CADDY
fi
log "Caddy config written"
fi
sudo systemctl enable caddy 2>/dev/null || true
sudo systemctl restart caddy
log "Caddy configured"
# ── Wait for health ──────────────────────────────
info "Waiting for web service..."
for i in $(seq 1 15); do
if curl -fsS http://127.0.0.1:3000/api/health >/dev/null 2>&1; then
log "Web service is healthy"; break
fi
if [ "$i" -eq 15 ]; then warn "Timed out — check: journalctl -u $SERVICE_NAME -f"; fi
sleep 2
done
# ── IP ──────────────────────────────────────────
IP=$(curl -fsSL http://checkip.amazonaws.com 2>/dev/null || curl -fsSL https://api.ipify.org 2>/dev/null || echo "localhost")
# ── Summary ──────────────────────────────────────
BOX_W=58
hr() { printf "║ %-*s ║\n" "$((BOX_W-6))" "$1"; }
printf "\n"
printf "╔══════════════════════════════════════════════════════════════╗\n"
printf "║ ${GREEN}worst-scan-web installed!${NC} ║\n"
printf "╠══════════════════════════════════════════════════════════════╣\n"
hr "Web: http://$IP"
hr "Setup: http://$IP/setup"
hr "Config: $INSTALL_DIR/.env"
hr "Repo: $INSTALL_DIR"
hr ""
hr "Manage: sudo systemctl restart $SERVICE_NAME"
hr "Logs: journalctl -u $SERVICE_NAME -f"
hr "Update: cd $INSTALL_DIR && git pull && npm run build"
hr " && sudo systemctl restart $SERVICE_NAME"
hr ""
hr "Caddy: sudo systemctl restart caddy"
hr "Cfg: $CADDYFILE"
printf "╚══════════════════════════════════════════════════════════════╝\n"
printf "\n"