#!/usr/bin/env bash set -euo pipefail ####################################### # STATE TRACKING ####################################### STATE_FILE="/var/log/pterodactyl_node_state" mkdir -p $(dirname "$STATE_FILE") touch "$STATE_FILE" mark_done() { echo "$1" >> "$STATE_FILE" } is_done() { grep -qx "$1" "$STATE_FILE" || false } save_var() { echo "$1=$2" >> "$STATE_FILE" } load_var() { grep "^$1=" "$STATE_FILE" | cut -d= -f2 } ####################################### # CONFIG ####################################### PANEL_URL="https://panel.amslabs.net" PTERO_API_KEY="ptla_1qpEnNZxXFAPAvjJrtRdlX5oRaDWYBImKuizXMzYne1" PTERO_LOCATION_ID=1 CLOUDFLARE_API_TOKEN="tIrFGHOC40BuhlAsRKJYWLm-k0SH9fsnGFoKEqI4" CLOUDFLARE_ZONE_ID="902bc1958422630ff8ce974e84af3a05" DOMAIN="amslabs.net" PUBLIC_IP="121.99.242.84" DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/1460095439930916915/PJiafXkssc4NyLXdCAGGdvGxwpo93z4Gso_LBSGKKX_E9k0CDm8Uq2dbTtYXNJzXcJHN" HOSTS_TEMPLATE="/etc/cloud/templates/hosts.debian.tmpl" HOSTS_FILE="/etc/hosts" WINGS_BIN="/usr/local/bin/wings" WINGS_DIR="/etc/pterodactyl" ####################################### # INPUTS ####################################### if ! is_done "input_hostname"; then read -rp "Enter host name (e.g. node03): " HOSTNAME save_var "HOSTNAME" "$HOSTNAME" else HOSTNAME=$(load_var HOSTNAME) fi FQDN="${HOSTNAME}.${DOMAIN}" if ! is_done "daemon_ports"; then read -rp "Enter Wings daemon listen port (e.g. 8082): " DAEMON_LISTEN read -rp "Enter Wings daemon SFTP port (e.g. 2024): " DAEMON_SFTP save_var "DAEMON_LISTEN" "$DAEMON_LISTEN" save_var "DAEMON_SFTP" "$DAEMON_SFTP" else DAEMON_LISTEN=$(load_var DAEMON_LISTEN) DAEMON_SFTP=$(load_var DAEMON_SFTP) fi if ! is_done "resources"; then echo "Select memory option:" echo "1) 32768 MB" echo "2) 65536 MB" echo "3) 92160 MB" read -rp "Choice [1-3]: " MEM_CHOICE case "$MEM_CHOICE" in 1) MEMORY=32768 ;; 2) MEMORY=65536 ;; 3) MEMORY=92160 ;; *) MEMORY=65536 ;; esac echo "Select disk option:" echo "1) 32768 MB" echo "2) 65536 MB" echo "3) 92160 MB" read -rp "Choice [1-3]: " DISK_CHOICE case "$DISK_CHOICE" in 1) DISK=32768 ;; 2) DISK=65536 ;; 3) DISK=92160 ;; *) DISK=65536 ;; esac save_var "MEMORY" "$MEMORY" save_var "DISK" "$DISK" else MEMORY=$(load_var MEMORY) DISK=$(load_var DISK) fi echo "🚀 Bootstrapping ${FQDN}" ####################################### # HOSTS TEMPLATE ####################################### if ! is_done "hosts_template"; then sudo tee "${HOSTS_TEMPLATE}" >/dev/null </dev/null else curl -s -X POST \ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ -H "Content-Type: application/json" \ --data "$DNS_PAYLOAD" \ "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records" >/dev/null fi mark_done "cloudflare_dns" fi ####################################### # PACKAGES ####################################### if ! is_done "packages"; then sudo apt update sudo apt install -y curl jq docker.io certbot python3-certbot-dns-cloudflare sudo systemctl enable docker --now mark_done "packages" fi ####################################### # CREATE NODE ####################################### if ! is_done "node_created"; then echo "📦 Creating node in panel" NODE_PAYLOAD=$(jq -n \ --arg name "$HOSTNAME" \ --arg fqdn "$FQDN" \ --argjson location "$PTERO_LOCATION_ID" \ --argjson memory "$MEMORY" \ --argjson disk "$DISK" \ --argjson daemon_listen "$DAEMON_LISTEN" \ --argjson daemon_sftp "$DAEMON_SFTP" \ '{ name: $fqdn, location_id: $location, fqdn: $fqdn, scheme: "https", memory: $memory, memory_overallocate: 0, disk: $disk, disk_overallocate: 0, daemon_listen: $daemon_listen, daemon_sftp: $daemon_sftp, daemon_base: "/var/lib/pterodactyl/volumes", upload_size: 100 }') NODE_RESPONSE=$(curl -s -X POST \ "${PANEL_URL}/api/application/nodes" \ -H "Authorization: Bearer ${PTERO_API_KEY}" \ -H "Accept: Application/vnd.pterodactyl.v1+json" \ -H "Content-Type: application/json" \ --data "$NODE_PAYLOAD") echo "📄 Node creation response:" echo "$NODE_RESPONSE" | jq NODE_ID=$(echo "$NODE_RESPONSE" | jq -r '.attributes.id // empty') if [[ -z "$NODE_ID" ]]; then echo "❌ Node creation failed, check API key, permissions, and values." exit 1 fi save_var "NODE_ID" "$NODE_ID" mark_done "node_created" fi ####################################### # INSTALL WINGS ####################################### if ! is_done "wings_installed"; then sudo mkdir -p "${WINGS_DIR}" curl -Lo "${WINGS_BIN}" https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_amd64 sudo chmod +x "${WINGS_BIN}" sudo tee /etc/systemd/system/wings.service >/dev/null </dev/null sudo chmod 600 "${WINGS_DIR}/config.yml" mark_done "node_deployed" echo "✅ Fetched config.yml successfully" break else echo "❌ Failed to fetch config, attempt $i/3" sleep 5 fi done if [[ ! -f "${WINGS_DIR}/config.yml" ]]; then echo "❌ Could not fetch config.yml from Panel" echo "$CONFIG_RESPONSE" | jq exit 1 fi fi ####################################### # CERTBOT ####################################### if ! is_done "certbot_done"; then sudo mkdir -p /root/.secrets/certbot sudo tee /root/.secrets/certbot/cloudflare.ini >/dev/null </dev/null <