#!/bin/bash ################################################################################ # Multi-Hosting Provider Management Script # Supports: Hetzner, Vultr, DigitalOcean ################################################################################ export TERM=tmux-256color RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' API_KEYS_DIR="$HOME/.apikeys" LOG_FILE="$HOME/.hosting_manager.log" check_dependencies() { local missing_tools=() for tool in curl jq bc; do if ! command -v "$tool" &> /dev/null; then missing_tools+=("$tool") fi done if [[ ${#missing_tools[@]} -gt 0 ]]; then echo -e "${RED}Error: Missing required tools: ${missing_tools[*]}${NC}" echo -e "${YELLOW}Install them with: sudo apt-get install ${missing_tools[*]}${NC}" exit 1 fi } log_action() { local level=$1 local message=$2 echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >> "$LOG_FILE" } clear_screen() { clear } print_header() { echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║${NC} Multi-Hosting Provider Management Tool ${CYAN}║${NC}" echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}" } pause_menu() { echo -e "\n${YELLOW}Press Enter to continue...${NC}" read -r } # ============================================================================ # VULTR FUNCTIONS # ============================================================================ vultr_get_api_key() { if [[ ! -f "$API_KEYS_DIR/vultr.txt" ]]; then echo -e "${RED}Error: Vultr API key not found${NC}" return 1 fi cat "$API_KEYS_DIR/vultr.txt" | tr -d '\n' | tr -d ' ' } vultr_list_instances() { local api_key=$(vultr_get_api_key) [[ -z "$api_key" ]] && echo '{"instances":[]}' && return 1 local response=$(curl -s -w "\n%{http_code}" -X GET "https://api.vultr.com/v2/instances" \ -H "Authorization: Bearer $api_key" -H "Content-Type: application/json") local http_code=$(echo "$response" | tail -n1) local body=$(echo "$response" | sed '$d') if [[ "$http_code" != "200" ]]; then echo -e "${RED}Vultr API Error (HTTP $http_code)${NC}" >&2 log_action "ERROR" "Vultr API returned HTTP $http_code" echo '{"instances":[]}' return 1 fi echo "$body" } vultr_display_instances() { clear_screen print_header echo -e "\n${BLUE}Vultr Instances:${NC}\n" local instances=$(vultr_list_instances) if ! echo "$instances" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve instances${NC}" log_action "ERROR" "Invalid Vultr API response" pause_menu return fi local instance_count=$(echo "$instances" | jq '.instances | length' 2>/dev/null || echo 0) [[ $instance_count -eq 0 ]] && echo -e "${YELLOW}No instances found${NC}" && pause_menu && return echo "$instances" | jq -r '.instances[] | "\(if .label != null then .label else "N/A" end)\t|\t\(.id)\t|\t\(.region)\t|\t\(.status)"' | \ column -t -s $'\t' -N "NAME,INSTANCE_ID,REGION,STATUS" log_action "INFO" "Displayed Vultr instances ($instance_count found)" pause_menu } vultr_instance_bandwidth() { clear_screen print_header echo -e "\n${BLUE}Vultr Bandwidth Usage (Today):${NC}\n" local api_key=$(vultr_get_api_key) [[ -z "$api_key" ]] && echo -e "${RED}API key not found${NC}" && pause_menu && return 1 local instances=$(vultr_list_instances) if ! echo "$instances" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve instances${NC}" pause_menu return fi local instance_count=$(echo "$instances" | jq '.instances | length' 2>/dev/null || echo 0) [[ $instance_count -eq 0 ]] && echo -e "${YELLOW}No instances found${NC}" && pause_menu && return local today=$(date '+%Y-%m-%d') echo "$instances" | jq -r '.instances[] | .id' | while read -r instance_id; do local label=$(echo "$instances" | jq -r ".instances[] | select(.id == \"$instance_id\") | .label") local bandwidth=$(curl -s -X GET "https://api.vultr.com/v2/instances/$instance_id/bandwidth" \ -H "Authorization: Bearer $api_key" -H "Content-Type: application/json") local incoming=$(echo "$bandwidth" | jq -r ".bandwidth.\"$today\"[0]" 2>/dev/null || echo "0") local outgoing=$(echo "$bandwidth" | jq -r ".bandwidth.\"$today\"[1]" 2>/dev/null || echo "0") local incoming_mb=$(echo "scale=2; $incoming / 1024 / 1024" | bc -l 2>/dev/null || echo "0") local outgoing_mb=$(echo "scale=2; $outgoing / 1024 / 1024" | bc -l 2>/dev/null || echo "0") echo -e "${CYAN}$label:${NC}" echo -e " Incoming: ${GREEN}${incoming_mb} MB${NC}" echo -e " Outgoing: ${GREEN}${outgoing_mb} MB${NC}\n" done log_action "INFO" "Viewed Vultr bandwidth usage" pause_menu } # ============================================================================ # DIGITALOCEAN FUNCTIONS # ============================================================================ digitalocean_get_api_key() { if [[ ! -f "$API_KEYS_DIR/digitalocean.txt" ]]; then echo -e "${RED}Error: DigitalOcean API key not found${NC}" return 1 fi cat "$API_KEYS_DIR/digitalocean.txt" | tr -d '\n' | tr -d ' ' } digitalocean_list_droplets() { local api_key=$(digitalocean_get_api_key) [[ -z "$api_key" ]] && echo '{"droplets":[]}' && return 1 local response=$(curl -s -w "\n%{http_code}" -X GET "https://api.digitalocean.com/v2/droplets" \ -H "Authorization: Bearer $api_key" -H "Content-Type: application/json") local http_code=$(echo "$response" | tail -n1) local body=$(echo "$response" | sed '$d') if [[ "$http_code" != "200" ]]; then echo -e "${RED}DigitalOcean API Error (HTTP $http_code)${NC}" >&2 log_action "ERROR" "DigitalOcean API returned HTTP $http_code" echo '{"droplets":[]}' return 1 fi echo "$body" } digitalocean_display_droplets() { clear_screen print_header echo -e "\n${BLUE}DigitalOcean Droplets:${NC}\n" local droplets=$(digitalocean_list_droplets) if ! echo "$droplets" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve droplets${NC}" log_action "ERROR" "Invalid DigitalOcean API response" pause_menu return fi local droplet_count=$(echo "$droplets" | jq '.droplets | length' 2>/dev/null || echo 0) [[ $droplet_count -eq 0 ]] && echo -e "${YELLOW}No droplets found${NC}" && pause_menu && return echo "$droplets" | jq -r '.droplets[] | "\(.name)\t|\t\(.id)\t|\t\(.region.slug)\t|\t\(.status)"' | \ column -t -s $'\t' -N "NAME,DROPLET_ID,REGION,STATUS" log_action "INFO" "Displayed DigitalOcean droplets ($droplet_count found)" pause_menu } digitalocean_display_droplet_info() { clear_screen print_header echo -e "\n${BLUE}DigitalOcean Droplet Detailed Info:${NC}\n" local droplets=$(digitalocean_list_droplets) if ! echo "$droplets" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve droplets${NC}" pause_menu return fi local droplet_count=$(echo "$droplets" | jq '.droplets | length' 2>/dev/null || echo 0) [[ $droplet_count -eq 0 ]] && echo -e "${YELLOW}No droplets found${NC}" && pause_menu && return echo "$droplets" | jq -r '.droplets[] | "\(.name)\t|\t\(.memory)MB\t|\t\(.vcpus) vCPU\t|\t\(.disk)GB\t|\t\(.region.name)\t|\t\(.networks.v4[] | select(.type == \"public\") | .ip_address // \"N/A\")"' | \ column -t -s $'\t' -N "NAME,MEMORY,VCPUS,DISK,REGION,PUBLIC_IP" log_action "INFO" "Viewed DigitalOcean droplet details" pause_menu } # ============================================================================ # HETZNER FUNCTIONS # ============================================================================ hetzner_find_api_keys() { local keys=() if [[ ! -d "$API_KEYS_DIR" ]]; then return 1 fi while IFS= read -r -d '' file; do keys+=("$file") done < <(find "$API_KEYS_DIR" -maxdepth 1 -name "hetzner*.txt" -type f -print0 2>/dev/null) if [[ ${#keys[@]} -eq 0 ]]; then return 1 fi IFS=$'\n' sorted_keys=($(sort <<<"${keys[*]}")) unset IFS printf '%s\n' "${sorted_keys[@]}" } hetzner_select_project() { local api_key="" local selected_file="" local keys_array=() while IFS= read -r key; do keys_array+=("$key") done < <(hetzner_find_api_keys) if [[ ${#keys_array[@]} -eq 0 ]]; then echo -e "${RED}Error: No Hetzner API keys found in $API_KEYS_DIR${NC}" log_action "ERROR" "No Hetzner API keys found" return 1 fi if [[ ${#keys_array[@]} -eq 1 ]]; then selected_file="${keys_array[0]}" else clear_screen print_header echo -e "\n${BLUE}Select Hetzner Project:${NC}\n" for i in "${!keys_array[@]}"; do local filename=$(basename "${keys_array[$i]}") local project_name="${filename#hetzner-}" project_name="${project_name%.txt}" echo -e "$((i+1)). ${YELLOW}$project_name${NC}" done echo "" read -p "Choose a project (1-${#keys_array[@]}): " choice if ! [[ "$choice" =~ ^[0-9]+$ ]] || ((choice < 1 || choice > ${#keys_array[@]})); then echo -e "${RED}Invalid selection${NC}" log_action "ERROR" "Invalid Hetzner project selection" return 1 fi selected_file="${keys_array[$((choice-1))]}" fi api_key=$(cat "$selected_file" | tr -d '\n' | tr -d ' ') echo "$api_key" } hetzner_list_servers() { local api_key="$1" [[ -z "$api_key" ]] && return 1 local response response=$(curl -s -w "\n%{http_code}" \ -H "Authorization: Bearer $api_key" \ -H "Content-Type: application/json" \ https://api.hetzner.cloud/v1/servers) local http_code http_code=$(echo "$response" | tail -n1) local body body=$(echo "$response" | sed '$d') if [[ "$http_code" != "200" ]]; then echo '{"servers":[]}' log_action "ERROR" "Hetzner API HTTP $http_code" return 1 fi echo "$body" } hetzner_display_servers() { clear_screen print_header echo -e "\n${BLUE}Hetzner Cloud Servers:${NC}\n" local api_key=$(hetzner_select_project) [[ -z "$api_key" ]] && pause_menu && return local servers=$(hetzner_list_servers "$api_key") if ! echo "$servers" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve servers${NC}" log_action "ERROR" "Invalid Hetzner API response" pause_menu return fi local server_count=$(echo "$servers" | jq '.servers | length' 2>/dev/null || echo 0) [[ $server_count -eq 0 ]] && echo -e "${YELLOW}No servers found${NC}" && pause_menu && return echo "$servers" | jq -r '.servers[] | "\(.name)\t|\t\(.id)\t|\t\(.datacenter.name)\t|\t\(.status)"' | \ column -t -s $'\t' -N "NAME,SERVER_ID,DATACENTER,STATUS" log_action "INFO" "Displayed Hetzner servers ($server_count found)" pause_menu } hetzner_display_server_info() { clear_screen print_header echo -e "\n${BLUE}Hetzner Cloud Server Detailed Info:${NC}\n" local api_key=$(hetzner_select_project) [[ -z "$api_key" ]] && pause_menu && return local servers=$(hetzner_list_servers "$api_key") if ! echo "$servers" | jq empty 2>/dev/null; then echo -e "${RED}Failed to retrieve servers${NC}" pause_menu return fi local server_count=$(echo "$servers" | jq '.servers | length' 2>/dev/null || echo 0) [[ $server_count -eq 0 ]] && echo -e "${YELLOW}No servers found${NC}" && pause_menu && return echo "$servers" | jq -r '.servers[] | "\(.name)\t|\t\(.server_type.name)\t|\t\(.server_type.cores) cores\t|\t\(.server_type.memory)GB\t|\t\(.server_type.disk)GB\t|\t\(.datacenter.description)\t|\t\(.public_net.ipv4.ip // \"N/A\")"' | \ column -t -s $'\t' -N "NAME,SERVER_TYPE,CORES,MEMORY,DISK,DATACENTER,PUBLIC_IP" log_action "INFO" "Viewed Hetzner server details" pause_menu } # ============================================================================ # API KEY MANAGEMENT # ============================================================================ setup_api_keys() { clear_screen print_header echo -e "\n${BLUE}API Key Setup:${NC}\n" if [[ ! -d "$API_KEYS_DIR" ]]; then mkdir -p "$API_KEYS_DIR" chmod 700 "$API_KEYS_DIR" echo -e "${GREEN}Created $API_KEYS_DIR${NC}" fi echo -e "${YELLOW}1.${NC} Setup Vultr API Key" echo -e "${YELLOW}2.${NC} Setup DigitalOcean API Key" echo -e "${YELLOW}3.${NC} Setup Hetzner API Key (Single Project)" echo -e "${YELLOW}4.${NC} Setup Hetzner API Key (Multiple Projects)" echo -e "${YELLOW}5.${NC} Back to Main Menu" echo "" read -p "Choose an option: " choice case $choice in 1) read -sp "Enter Vultr API Key: " vultr_key echo "" echo "$vultr_key" > "$API_KEYS_DIR/vultr.txt" chmod 600 "$API_KEYS_DIR/vultr.txt" echo -e "${GREEN}Vultr API key saved${NC}" log_action "INFO" "Vultr API key configured" ;; 2) read -sp "Enter DigitalOcean API Key: " do_key echo "" echo "$do_key" > "$API_KEYS_DIR/digitalocean.txt" chmod 600 "$API_KEYS_DIR/digitalocean.txt" echo -e "${GREEN}DigitalOcean API key saved${NC}" log_action "INFO" "DigitalOcean API key configured" ;; 3) read -sp "Enter Hetzner API Key: " hz_key echo "" echo "$hz_key" > "$API_KEYS_DIR/hetzner.txt" chmod 600 "$API_KEYS_DIR/hetzner.txt" echo -e "${GREEN}Hetzner API key saved${NC}" log_action "INFO" "Hetzner API key configured" ;; 4) read -p "Enter project name (e.g., production, staging): " project_name read -sp "Enter Hetzner API Key for $project_name: " hz_key echo "" echo "$hz_key" > "$API_KEYS_DIR/hetzner-${project_name}.txt" chmod 600 "$API_KEYS_DIR/hetzner-${project_name}.txt" echo -e "${GREEN}Hetzner API key for '$project_name' saved${NC}" log_action "INFO" "Hetzner API key for project '$project_name' configured" ;; 5) return ;; *) echo -e "${RED}Invalid option${NC}" ;; esac pause_menu setup_api_keys } # ============================================================================ # LOGGING # ============================================================================ view_logs() { clear_screen print_header echo -e "\n${BLUE}Recent Activity Log (Last 50 entries):${NC}\n" if [[ ! -f "$LOG_FILE" ]]; then echo -e "${YELLOW}No log file found${NC}" pause_menu return fi tail -n 50 "$LOG_FILE" | while IFS= read -r line; do if [[ "$line" == *"ERROR"* ]]; then echo -e "${RED}$line${NC}" elif [[ "$line" == *"WARNING"* ]]; then echo -e "${YELLOW}$line${NC}" else echo -e "${GREEN}$line${NC}" fi done pause_menu } # ============================================================================ # MAIN MENU # ============================================================================ show_main_menu() { clear_screen print_header echo -e "\n${BLUE}Main Menu:${NC}\n" echo -e "${YELLOW}1.${NC} List Vultr Instances" echo -e "${YELLOW}2.${NC} Vultr Instance Bandwidth Usage (Today)" echo -e "${YELLOW}3.${NC} List DigitalOcean Droplets" echo -e "${YELLOW}4.${NC} DigitalOcean Droplet Detailed Info" echo -e "${YELLOW}5.${NC} List Hetzner Servers" echo -e "${YELLOW}6.${NC} Hetzner Server Detailed Info" echo -e "${YELLOW}7.${NC} Setup/Update API Keys" echo -e "${YELLOW}8.${NC} View Activity Logs" echo -e "${YELLOW}9.${NC} Exit" echo "" read -p "Choose an option (1-9): " choice case $choice in 1) vultr_display_instances ;; 2) vultr_instance_bandwidth ;; 3) digitalocean_display_droplets ;; 4) digitalocean_display_droplet_info ;; 5) hetzner_display_servers ;; 6) hetzner_display_server_info ;; 7) setup_api_keys ;; 8) view_logs ;; 9) clear_screen echo -e "${CYAN}Goodbye!${NC}" log_action "INFO" "Script terminated" exit 0 ;; *) echo -e "${RED}Invalid option${NC}" pause_menu ;; esac show_main_menu } # ============================================================================ # MAIN EXECUTION # ============================================================================ check_dependencies log_action "INFO" "Script started" show_main_menu