#!/bin/bash set -e GREEN='\033[0;32m' CYAN='\033[0;36m' YELLOW='\033[1;33m' BLUE='\033[1;34m' NC='\033[0m' stage() { echo -e "\n${BLUE}========== $1 ==========${NC}"; } progressbar() { local msg=$1; for i in {1..14}; do echo -ne "${CYAN}$msg$(printf '%*s' $((i%4)) | tr ' ' '.')\r${NC}"; sleep 0.12; done; echo ""; } rocket_anim() { for i in $(seq 1 6); do echo -ne "šŸš€"; sleep 0.1; done; echo ""; } validate_domain() { [[ "$1" =~ ^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$ ]] && [[ ! "$1" =~ / ]] && [[ ! "$1" =~ ^(http|https) ]] && [[ ! "$1" =~ ^www\. ]] && [[ ! "$1" =~ \.$ ]] && [[ ! "$1" =~ ^\. ]] && [[ ! "$1" =~ [^a-zA-Z0-9.-] ]]; } validate_login() { [[ "$1" =~ ^[a-zA-Z0-9-]{3,}$ ]]; } validate_password() { [[ ${#1} -ge 8 ]]; } generate_password() { tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 10; } generate_login() { tr -dc 'A-Za-z' < /dev/urandom | head -c 6; } echo -e "${GREEN}\n✨ Marzban 0.8.4 + Nginx One-Click Installer (with Reality config, Udemy Edition, by MaxiCoder) ✨${NC}\n" if [[ $EUID -ne 0 ]]; then echo -e "${YELLOW}[!] Please run as root.${NC}" exit 1 fi while true; do read -rp "🌐 Enter your domain (example.com or my.site.com): " DOMAIN if validate_domain "$DOMAIN"; then break else echo -e "${YELLOW}āŒ Invalid domain format! Only domain, no http(s), www, slashes, spaces.${NC}" fi done while true; do read -rp "šŸ‘¤ Set admin username (3+ latin letters, numbers, or dash, leave empty to autogenerate): " ADMIN_USER if [[ -z "$ADMIN_USER" ]]; then ADMIN_USER=$(generate_login) echo -e "${CYAN}šŸ†• Generated username: $ADMIN_USER${NC}" break fi if validate_login "$ADMIN_USER"; then break else echo -e "${YELLOW}āŒ Invalid login! Only latin letters, numbers, dash, min 3 chars.${NC}" fi done while true; do read -srp "šŸ”‘ Enter admin password (min 8 chars, leave empty to autogenerate): " ADMIN_PASS1; echo if [[ -z "$ADMIN_PASS1" ]]; then ADMIN_PASS=$(generate_password) echo -e "\n${CYAN}šŸ”’ Generated password: $ADMIN_PASS${NC}" break fi read -srp "šŸ”‘ Repeat admin password: " ADMIN_PASS2; echo if [[ "$ADMIN_PASS1" != "$ADMIN_PASS2" ]]; then echo -e "${YELLOW}āŒ Passwords do not match! Try again.${NC}" continue fi if ! validate_password "$ADMIN_PASS1"; then echo -e "${YELLOW}āŒ Password must be at least 8 characters.${NC}" continue fi ADMIN_PASS="$ADMIN_PASS1" break done stage "Updating packages and installing dependencies" progressbar "Updating system" DEBIAN_FRONTEND=noninteractive apt-get update -y -qq stage "Updating packages and installing dependencies" progressbar "Updating system" DEBIAN_FRONTEND=noninteractive apt-get update -y -qq # --- Add Docker repository if not exists and install Docker Compose plugin --- if ! apt-cache policy docker-compose-plugin 2>/dev/null | grep -q download.docker.com; then echo -e "${YELLOW}Adding official Docker repository for latest docker-compose-plugin...${NC}" apt-get install -y ca-certificates curl gnupg lsb-release install -m 0755 -d /etc/apt/keyrings if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg fi echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update fi progressbar "Installing core packages" DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates curl gnupg lsb-release git nginx docker.io docker-compose-plugin jq progressbar "Installing core packages" DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates curl gnupg lsb-release git nginx docker.io docker-compose-plugin jq stage "Enabling services" systemctl enable --now docker systemctl enable --now nginx if ss -tuln | grep -q ":80 "; then if ! ss -tulnp | grep -q ':80.*nginx'; then echo -e "${YELLOW}āŒ Port 80 is occupied by another service. Stop it first, or reset your VPS to factory settings in provider dashboard!${NC}" exit 1 fi fi stage "Cloning Marzban" progressbar "Cloning from GitHub" rm -rf /opt/marzban git clone --branch v0.8.4 --depth 1 https://github.com/Gozargah/Marzban.git /opt/marzban > /dev/null cd /opt/marzban stage "Configuring Docker Compose" progressbar "Generating docker-compose.yml" cat > docker-compose.yml < /dev/null 2>&1 || true docker compose up -d > /dev/null sleep 8 if ! docker compose ps | grep -q Up; then echo -e "${YELLOW}āŒ Marzban container did not start! Check logs with: docker compose logs${NC}\nOr contact MaxiCoder for support." exit 1 fi echo -e "${CYAN}Creating admin user automatically...${NC}" echo -e "y\n\n\n" | docker compose exec -T marzban marzban-cli admin create --username "$ADMIN_USER" --password "$ADMIN_PASS" || true stage "Generating Reality XRay config (inside container)" progressbar "Generating keys and config" PRIVATE_KEY=$(docker compose exec marzban xray x25519 | grep 'Private' | awk '{print $3}') PUBLIC_KEY=$(docker compose exec marzban xray x25519 | grep 'Public' | awk '{print $3}') SHORT_ID=$(openssl rand -hex 8) cat > /var/lib/marzban/xray_config.json < /etc/nginx/sites-available/marzban < "$INFO_FILE" <