#!/bin/bash set -e # ============================================================ # D3V-NPMWG Installer for Ubuntu/Debian # Nginx Proxy Manager + WireGuard VPN # https://github.com/xtcnet/D3V-NPMWG # ============================================================ INSTALL_DIR="/opt/d3v-npmwg" COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml" CONTAINER_NAME="d3v-npmwg" IMAGE_NAME="ghcr.io/xtcnet/d3v-npmwg:latest" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # ----------------------------------------------------------- # Helpers # ----------------------------------------------------------- log_step() { echo -e "${CYAN}[*]${NC} $1"; } log_ok() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } log_err() { echo -e "${RED}[✗]${NC} $1"; } separator() { echo -e "${GREEN}=================================================================${NC}"; } require_root() { if [ "$(id -u)" -ne 0 ]; then log_err "This script must be run as root. Use: sudo $0" exit 1 fi } get_compose_cmd() { if docker compose version > /dev/null 2>&1; then echo "docker compose" else log_err "Docker Compose plugin not found. Please install it first." exit 1 fi } detect_public_ip() { local ip="" ip=$(curl -s -m 5 https://ifconfig.me 2>/dev/null) \ || ip=$(curl -s -m 5 https://icanhazip.com 2>/dev/null) \ || ip=$(curl -s -m 5 https://api.ipify.org 2>/dev/null) \ || ip="" echo "$ip" } # ----------------------------------------------------------- # 1. Install system dependencies # ----------------------------------------------------------- install_deps() { separator echo -e "${BOLD} Installing System Dependencies${NC}" separator # --- curl --- log_step "Checking curl..." if command -v curl > /dev/null 2>&1; then log_ok "curl is already installed." else log_step "Installing curl..." apt-get update -qq apt-get install -y curl log_ok "curl installed." fi # --- Docker --- log_step "Checking Docker..." if command -v docker > /dev/null 2>&1; then log_ok "Docker is already installed ($(docker --version))." else log_step "Installing Docker... (this may take 1-2 minutes)" curl -fsSL https://get.docker.com | sh systemctl enable --now docker log_ok "Docker installed." fi # --- Wait for Docker daemon --- log_step "Waiting for Docker daemon to be ready..." local retries=0 while ! docker info > /dev/null 2>&1; do retries=$((retries + 1)) if [ "$retries" -ge 30 ]; then log_err "Docker daemon did not start after 30 seconds." log_err "Try: systemctl restart docker" exit 1 fi sleep 1 done log_ok "Docker daemon is running." # --- Remove old standalone docker-compose (Python) if present --- if command -v docker-compose > /dev/null 2>&1; then local dc_path dc_path=$(command -v docker-compose) # Check if it's the old Python version if docker-compose version 2>&1 | grep -qi "docker-compose version 1"; then log_warn "Detected legacy docker-compose (Python). Removing it..." apt-get purge -y docker-compose 2>/dev/null || rm -f "$dc_path" 2>/dev/null || true log_ok "Legacy docker-compose removed." fi fi # --- Docker Compose plugin --- log_step "Checking Docker Compose plugin..." if docker compose version > /dev/null 2>&1; then log_ok "Docker Compose plugin is ready ($(docker compose version))." else log_step "Installing Docker Compose plugin..." apt-get update -qq apt-get install -y docker-compose-plugin log_ok "Docker Compose plugin installed." fi echo "" log_ok "All system dependencies are ready." } # ----------------------------------------------------------- # 2. Install D3V-NPMWG # ----------------------------------------------------------- do_install() { require_root if [ -d "$INSTALL_DIR" ]; then log_warn "D3V-NPMWG is already installed at ${INSTALL_DIR}." log_warn "Use the Update option to pull the latest image, or Uninstall first." return fi separator echo -e "${BOLD} D3V-NPMWG Installation${NC}" separator echo "" # --- Dependencies --- install_deps echo "" # --- Detect IP --- log_step "Detecting server public IP..." local detected_ip detected_ip=$(detect_public_ip) local wg_host="" if [ -n "$detected_ip" ]; then log_ok "Detected IP: ${BOLD}${detected_ip}${NC}" read -rp "$(echo -e "${CYAN}[?]${NC} WG_HOST [${detected_ip}]: ")" wg_host wg_host="${wg_host:-$detected_ip}" else log_warn "Could not auto-detect public IP." read -rp "$(echo -e "${CYAN}[?]${NC} Enter server public IP or domain: ")" wg_host fi if [ -z "$wg_host" ]; then log_err "WG_HOST cannot be empty. Aborting." return fi # --- Create directory --- log_step "Creating ${INSTALL_DIR}..." mkdir -p "$INSTALL_DIR" log_ok "Directory created." # --- Write docker-compose.yml --- log_step "Generating docker-compose.yml..." cat > "$COMPOSE_FILE" </dev/null || true cd / log_step "Removing ${INSTALL_DIR}..." rm -rf "$INSTALL_DIR" log_ok "D3V-NPMWG uninstalled." } # ----------------------------------------------------------- # 4. Purge — uninstall app + system deps (Docker) # ----------------------------------------------------------- do_purge() { require_root do_uninstall echo "" log_warn "Do you also want to remove Docker and Docker Compose from this system?" read -rp "$(echo -e "${RED}Remove Docker? (y/N): ${NC}")" confirm if [[ ! "$confirm" =~ ^[yY]$ ]]; then echo "Skipped Docker removal." return fi log_step "Removing Docker packages..." apt-get purge -y docker-ce docker-ce-cli containerd.io \ docker-compose-plugin docker-buildx-plugin 2>/dev/null || true apt-get autoremove -y --purge 2>/dev/null || true log_ok "Docker and related packages removed." } # ----------------------------------------------------------- # 5. Reset admin password # ----------------------------------------------------------- do_reset_password() { require_root if ! docker ps --format '{{.Names}}' | grep -q "$CONTAINER_NAME"; then log_err "Container ${CONTAINER_NAME} is not running. Start it first." return fi read -rp "$(echo -e "${CYAN}[?]${NC} New admin email [admin@example.com]: ")" new_email new_email="${new_email:-admin@example.com}" read -rsp "$(echo -e "${CYAN}[?]${NC} New password [changeme]: ")" new_pass echo "" new_pass="${new_pass:-changeme}" log_step "Resetting admin credentials inside container..." docker exec "$CONTAINER_NAME" node -e " import('bcrypt').then(async bcrypt => { const hash = await bcrypt.hash('${new_pass}', 13); const knex = (await import('/app/db.js')).default; await knex('auth').where('user_id', 1).update({ secret: hash }); await knex('user').where('id', 1).update({ email: '${new_email}', is_deleted: 0, is_disabled: 0 }); console.log('OK'); process.exit(0); }).catch(e => { console.error(e); process.exit(1); }); " if [ $? -eq 0 ]; then log_ok "Credentials updated." echo -e " Email : ${BOLD}${new_email}${NC}" echo -e " Password : ${BOLD}${new_pass}${NC}" log_warn "Please change your password after logging in." else log_err "Failed to reset password. Check container logs." fi } # ----------------------------------------------------------- # 6. Update D3V-NPMWG # ----------------------------------------------------------- do_update() { require_root if [ ! -d "$INSTALL_DIR" ]; then log_err "D3V-NPMWG is not installed. Install it first." return fi log_step "Pulling latest image..." local dc dc=$(get_compose_cmd) cd "$INSTALL_DIR" $dc pull log_ok "Image pulled." log_step "Recreating containers..." $dc up -d log_ok "D3V-NPMWG updated." log_step "Cleaning old images..." docker image prune -f > /dev/null 2>&1 log_ok "Done." } # ----------------------------------------------------------- # Interactive menu # ----------------------------------------------------------- show_menu() { while true; do echo "" separator echo -e "${BOLD} D3V-NPMWG Installation Manager${NC}" separator echo " 1) Install D3V-NPMWG" echo " 2) Uninstall D3V-NPMWG" echo " 3) Uninstall D3V-NPMWG + Docker (Purge)" echo " 4) Reset Admin Password" echo " 5) Update D3V-NPMWG" echo " 6) Exit" separator read -rp " Select [1-6]: " choice echo "" case "$choice" in 1) do_install ;; 2) do_uninstall ;; 3) do_purge ;; 4) do_reset_password ;; 5) do_update ;; 6) echo "Bye!"; exit 0 ;; *) log_err "Invalid option." ;; esac done } show_help() { echo "Usage: $0 [command]" echo "" echo "Commands:" echo " install Install D3V-NPMWG and dependencies" echo " uninstall Remove D3V-NPMWG (keeps Docker)" echo " purge Remove D3V-NPMWG AND Docker" echo " reset Reset web admin password" echo " update Pull latest image and restart" echo " help Show this help" echo "" echo "Run without arguments to open the interactive menu." } # ----------------------------------------------------------- # Entry point # ----------------------------------------------------------- if [ "$#" -eq 0 ]; then show_menu else case "$1" in install) do_install ;; uninstall) do_uninstall ;; purge) do_purge ;; reset) do_reset_password ;; update) do_update ;; help|-h|--help) show_help ;; *) log_err "Unknown command: $1" show_help exit 1 ;; esac fi