Sanaei 15 órája
szülő
commit
69ccdba734
3 módosított fájl, 1136 hozzáadás és 43 törlés
  1. 441 18
      install.sh
  2. 459 8
      update.sh
  3. 236 17
      x-ui.sh

+ 441 - 18
install.sh

@@ -39,32 +39,46 @@ arch() {
 
 echo "Arch: $(arch)"
 
+# Simple helpers
+is_ipv4() {
+    [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
+}
+is_ipv6() {
+    [[ "$1" =~ : ]] && return 0 || return 1
+}
+is_ip() {
+    is_ipv4 "$1" || is_ipv6 "$1"
+}
+is_domain() {
+    [[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+[A-Za-z]{2,}$ ]] && return 0 || return 1
+}
+
 install_base() {
     case "${release}" in
         ubuntu | debian | armbian)
-            apt-get update && apt-get install -y -q wget curl tar tzdata
+            apt-get update && apt-get install -y -q wget curl tar tzdata openssl socat
         ;;
         fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
-            dnf -y update && dnf install -y -q wget curl tar tzdata
+            dnf -y update && dnf install -y -q wget curl tar tzdata openssl socat
         ;;
         centos)
             if [[ "${VERSION_ID}" =~ ^7 ]]; then
-                yum -y update && yum install -y wget curl tar tzdata
+                yum -y update && yum install -y wget curl tar tzdata openssl socat
             else
-                dnf -y update && dnf install -y -q wget curl tar tzdata
+                dnf -y update && dnf install -y -q wget curl tar tzdata openssl socat
             fi
         ;;
         arch | manjaro | parch)
-            pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata
+            pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata openssl socat
         ;;
         opensuse-tumbleweed | opensuse-leap)
-            zypper refresh && zypper -q install -y wget curl tar timezone
+            zypper refresh && zypper -q install -y wget curl tar timezone openssl socat
         ;;
         alpine)
-            apk update && apk add wget curl tar tzdata
+            apk update && apk add wget curl tar tzdata openssl socat
         ;;
         *)
-            apt-get update && apt-get install -y -q wget curl tar tzdata
+            apt-get update && apt-get install -y -q wget curl tar tzdata openssl socat
         ;;
     esac
 }
@@ -75,10 +89,373 @@ gen_random_string() {
     echo "$random_string"
 }
 
+install_acme() {
+    echo -e "${green}Installing acme.sh for SSL certificate management...${plain}"
+    cd ~ || return 1
+    curl -s https://get.acme.sh | sh >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Failed to install acme.sh${plain}"
+        return 1
+    else
+        echo -e "${green}acme.sh installed successfully${plain}"
+    fi
+    return 0
+}
+
+setup_ssl_certificate() {
+    local domain="$1"
+    local server_ip="$2"
+    local existing_port="$3"
+    local existing_webBasePath="$4"
+    
+    echo -e "${green}Setting up SSL certificate...${plain}"
+    
+    # Check if acme.sh is installed
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        install_acme
+        if [ $? -ne 0 ]; then
+            echo -e "${yellow}Failed to install acme.sh, skipping SSL setup${plain}"
+            return 1
+        fi
+    fi
+    
+    # Create certificate directory
+    local certPath="/root/cert/${domain}"
+    mkdir -p "$certPath"
+    
+    # Issue certificate
+    echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
+    echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
+    
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
+    ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
+    
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
+        echo -e "${yellow}Please ensure port 80 is open and try again later with: x-ui${plain}"
+        rm -rf ~/.acme.sh/${domain} 2>/dev/null
+        rm -rf "$certPath" 2>/dev/null
+        return 1
+    fi
+    
+    # Install certificate
+    ~/.acme.sh/acme.sh --installcert -d ${domain} \
+        --key-file /root/cert/${domain}/privkey.pem \
+        --fullchain-file /root/cert/${domain}/fullchain.pem \
+        --reloadcmd "systemctl restart x-ui" >/dev/null 2>&1
+    
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Failed to install certificate${plain}"
+        return 1
+    fi
+    
+    # Enable auto-renew
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+    chmod 755 $certPath/* 2>/dev/null
+    
+    # Set certificate for panel
+    local webCertFile="/root/cert/${domain}/fullchain.pem"
+    local webKeyFile="/root/cert/${domain}/privkey.pem"
+    
+    if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+        /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" >/dev/null 2>&1
+        echo -e "${green}SSL certificate installed and configured successfully!${plain}"
+        return 0
+    else
+        echo -e "${yellow}Certificate files not found${plain}"
+        return 1
+    fi
+}
+
+# Fallback: generate a self-signed certificate (not publicly trusted)
+setup_self_signed_certificate() {
+    local name="$1"   # domain or IP to place in SAN
+    local certDir="/root/cert/selfsigned"
+
+    echo -e "${yellow}Generating a self-signed certificate (not publicly trusted)...${plain}"
+
+    mkdir -p "$certDir"
+
+    local sanExt=""
+    if is_ip "$name"; then
+        sanExt="IP:${name}"
+    else
+        sanExt="DNS:${name}"
+    fi
+
+    # Use -addext if supported; fallback to config file if needed
+    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+        -keyout "${certDir}/privkey.pem" \
+        -out "${certDir}/fullchain.pem" \
+        -subj "/CN=${name}" \
+        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
+
+    if [[ $? -ne 0 ]]; then
+        # Fallback via temporary config file (for older OpenSSL versions)
+        local tmpCfg="${certDir}/openssl.cnf"
+        cat > "$tmpCfg" <<EOF
+[req]
+distinguished_name=req_distinguished_name
+req_extensions=v3_req
+[req_distinguished_name]
+[v3_req]
+subjectAltName=${sanExt}
+EOF
+        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+            -keyout "${certDir}/privkey.pem" \
+            -out "${certDir}/fullchain.pem" \
+            -subj "/CN=${name}" \
+            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
+        rm -f "$tmpCfg"
+    fi
+
+    if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
+        echo -e "${red}Failed to generate self-signed certificate${plain}"
+        return 1
+    fi
+
+    chmod 755 ${certDir}/* 2>/dev/null
+    /usr/local/x-ui/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
+    echo -e "${yellow}Self-signed certificate configured. Browsers will show a warning.${plain}"
+    return 0
+}
+
+# Comprehensive manual SSL certificate issuance via acme.sh
+ssl_cert_issue() {
+    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
+    local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
+    
+    # check for acme.sh first
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        echo "acme.sh could not be found. Installing now..."
+        cd ~ || return 1
+        curl -s https://get.acme.sh | sh
+        if [ $? -ne 0 ]; then
+            echo -e "${red}Failed to install acme.sh${plain}"
+            return 1
+        else
+            echo -e "${green}acme.sh installed successfully${plain}"
+        fi
+    fi
+
+    # get the domain here, and we need to verify it
+    local domain=""
+    while true; do
+        read -rp "Please enter your domain name: " domain
+        domain="${domain// /}"  # Trim whitespace
+        
+        if [[ -z "$domain" ]]; then
+            echo -e "${red}Domain name cannot be empty. Please try again.${plain}"
+            continue
+        fi
+        
+        if ! is_domain "$domain"; then
+            echo -e "${red}Invalid domain format: ${domain}. Please enter a valid domain name.${plain}"
+            continue
+        fi
+        
+        break
+    done
+    echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
+
+    # check if there already exists a certificate
+    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
+    if [ "${currentCert}" == "${domain}" ]; then
+        local certInfo=$(~/.acme.sh/acme.sh --list)
+        echo -e "${red}System already has certificates for this domain. Cannot issue again.${plain}"
+        echo -e "${yellow}Current certificate details:${plain}"
+        echo "$certInfo"
+        return 1
+    else
+        echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
+    fi
+
+    # create a directory for the certificate
+    certPath="/root/cert/${domain}"
+    if [ ! -d "$certPath" ]; then
+        mkdir -p "$certPath"
+    else
+        rm -rf "$certPath"
+        mkdir -p "$certPath"
+    fi
+
+    # get the port number for the standalone server
+    local WebPort=80
+    read -rp "Please choose which port to use (default is 80): " WebPort
+    if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
+        echo -e "${yellow}Your input ${WebPort} is invalid, will use default port 80.${plain}"
+        WebPort=80
+    fi
+    echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}"
+
+    # Stop panel temporarily
+    echo -e "${yellow}Stopping panel temporarily...${plain}"
+    systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
+
+    # issue the certificate
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
+    ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Issuing certificate failed, please check logs.${plain}"
+        rm -rf ~/.acme.sh/${domain}
+        systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+        return 1
+    else
+        echo -e "${green}Issuing certificate succeeded, installing certificates...${plain}"
+    fi
+
+    # Setup reload command
+    reloadCmd="systemctl restart x-ui || rc-service x-ui restart"
+    echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}"
+    echo -e "${green}This command will run on every certificate issue and renew.${plain}"
+    read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
+    if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
+        echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; systemctl restart x-ui"
+        echo -e "${green}\t2.${plain} Input your own command"
+        echo -e "${green}\t0.${plain} Keep default reloadcmd"
+        read -rp "Choose an option: " choice
+        case "$choice" in
+        1)
+            echo -e "${green}Reloadcmd is: systemctl reload nginx ; systemctl restart x-ui${plain}"
+            reloadCmd="systemctl reload nginx ; systemctl restart x-ui"
+            ;;
+        2)
+            echo -e "${yellow}It's recommended to put x-ui restart at the end${plain}"
+            read -rp "Please enter your custom reloadcmd: " reloadCmd
+            echo -e "${green}Reloadcmd is: ${reloadCmd}${plain}"
+            ;;
+        *)
+            echo -e "${green}Keeping default reloadcmd${plain}"
+            ;;
+        esac
+    fi
+
+    # install the certificate
+    ~/.acme.sh/acme.sh --installcert -d ${domain} \
+        --key-file /root/cert/${domain}/privkey.pem \
+        --fullchain-file /root/cert/${domain}/fullchain.pem --reloadcmd "${reloadCmd}"
+
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Installing certificate failed, exiting.${plain}"
+        rm -rf ~/.acme.sh/${domain}
+        systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+        return 1
+    else
+        echo -e "${green}Installing certificate succeeded, enabling auto renew...${plain}"
+    fi
+
+    # enable auto-renew
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
+        ls -lah /root/cert/${domain}/
+        chmod 755 $certPath/*
+    else
+        echo -e "${green}Auto renew succeeded, certificate details:${plain}"
+        ls -lah /root/cert/${domain}/
+        chmod 755 $certPath/*
+    fi
+
+    # Restart panel
+    systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+
+    # Prompt user to set panel paths after successful certificate installation
+    read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel
+    if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
+        local webCertFile="/root/cert/${domain}/fullchain.pem"
+        local webKeyFile="/root/cert/${domain}/privkey.pem"
+
+        if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+            /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
+            echo -e "${green}Certificate paths set for the panel${plain}"
+            echo -e "${green}Certificate File: $webCertFile${plain}"
+            echo -e "${green}Private Key File: $webKeyFile${plain}"
+            echo ""
+            echo -e "${green}Access URL: https://${domain}:${existing_port}/${existing_webBasePath}${plain}"
+            echo -e "${yellow}Panel will restart to apply SSL certificate...${plain}"
+            systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null
+        else
+            echo -e "${red}Error: Certificate or private key file not found for domain: $domain.${plain}"
+        fi
+    else
+        echo -e "${yellow}Skipping panel path setting.${plain}"
+    fi
+    
+    return 0
+}
+
+# Reusable interactive SSL setup (domain or self-signed)
+# Sets global `SSL_HOST` to the chosen domain/IP for Access URL usage
+prompt_and_setup_ssl() {
+    local panel_port="$1"
+    local web_base_path="$2"   # expected without leading slash
+    local server_ip="$3"
+
+    local ssl_choice=""
+
+    echo -e "${yellow}Choose SSL certificate setup method:${plain}"
+    echo -e "${green}1.${plain} Let's Encrypt (domain required, recommended)"
+    echo -e "${green}2.${plain} Self-signed certificate (not publicly trusted)"
+    read -rp "Choose an option (default 2): " ssl_choice
+    ssl_choice="${ssl_choice// /}"  # Trim whitespace
+    
+    # Default to 2 (self-signed) if not 1
+    if [[ "$ssl_choice" != "1" ]]; then
+        ssl_choice="2"
+    fi
+
+    case "$ssl_choice" in
+    1)
+        # User chose Let's Encrypt domain option
+        echo -e "${green}Using ssl_cert_issue() for comprehensive domain setup...${plain}"
+        ssl_cert_issue
+        # Extract the domain that was used from the certificate
+        local cert_domain=$(~/.acme.sh/acme.sh --list 2>/dev/null | tail -1 | awk '{print $1}')
+        if [[ -n "${cert_domain}" ]]; then
+            SSL_HOST="${cert_domain}"
+            echo -e "${green}✓ SSL certificate configured successfully with domain: ${cert_domain}${plain}"
+        else
+            echo -e "${yellow}SSL setup may have completed, but domain extraction failed${plain}"
+            SSL_HOST="${server_ip}"
+        fi
+        ;;
+    2)
+        # User chose self-signed option
+        # Stop panel if running
+        if [[ $release == "alpine" ]]; then
+            rc-service x-ui stop >/dev/null 2>&1
+        else
+            systemctl stop x-ui >/dev/null 2>&1
+        fi
+        echo -e "${yellow}Using server IP for self-signed certificate: ${server_ip}${plain}"
+        setup_self_signed_certificate "${server_ip}"
+        if [ $? -eq 0 ]; then
+            SSL_HOST="${server_ip}"
+            echo -e "${green}✓ Self-signed SSL configured successfully${plain}"
+        else
+            echo -e "${red}✗ Self-signed SSL setup failed${plain}"
+            SSL_HOST="${server_ip}"
+        fi
+        # Start panel after SSL is configured
+        if [[ $release == "alpine" ]]; then
+            rc-service x-ui start >/dev/null 2>&1
+        else
+            systemctl start x-ui >/dev/null 2>&1
+        fi
+        ;;
+    *)
+        echo -e "${red}Invalid option. Skipping SSL setup.${plain}"
+        SSL_HOST="${server_ip}"
+        ;;
+    esac
+}
+
 config_after_install() {
     local existing_hasDefaultCredential=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}')
-    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
+    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}' | sed 's#^/##')
     local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
+    # Properly detect empty cert by checking if cert: line exists and has content after it
+    local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
     local URL_lists=(
         "https://api4.ipify.org"
         "https://ipv4.icanhazip.com"
@@ -111,20 +488,50 @@ config_after_install() {
             fi
             
             /usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
-            echo -e "This is a fresh installation, generating random login info for security concerns:"
-            echo -e "###############################################"
-            echo -e "${green}Username: ${config_username}${plain}"
-            echo -e "${green}Password: ${config_password}${plain}"
-            echo -e "${green}Port: ${config_port}${plain}"
+            
+            echo ""
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${green}     SSL Certificate Setup (MANDATORY)     ${plain}"
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${yellow}For security, SSL certificate is required for all panels.${plain}"
+            echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+            echo ""
+
+            prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
+            
+            # Display final credentials and access information
+            echo ""
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${green}     Panel Installation Complete!         ${plain}"
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${green}Username:    ${config_username}${plain}"
+            echo -e "${green}Password:    ${config_password}${plain}"
+            echo -e "${green}Port:        ${config_port}${plain}"
             echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
-            echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}"
-            echo -e "###############################################"
+            echo -e "${green}Access URL:  https://${SSL_HOST}:${config_port}/${config_webBasePath}${plain}"
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${yellow}⚠ IMPORTANT: Save these credentials securely!${plain}"
+            echo -e "${yellow}⚠ SSL Certificate: Enabled and configured${plain}"
         else
             local config_webBasePath=$(gen_random_string 18)
             echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
             /usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
             echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
-            echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
+
+            # If the panel is already installed but no certificate is configured, prompt for SSL now
+            if [[ -z "${existing_cert}" ]]; then
+                echo ""
+                echo -e "${green}═══════════════════════════════════════════${plain}"
+                echo -e "${green}     SSL Certificate Setup (RECOMMENDED)   ${plain}"
+                echo -e "${green}═══════════════════════════════════════════${plain}"
+                echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+                echo ""
+                prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
+                echo -e "${green}Access URL:  https://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
+            else
+                # If a cert already exists, just show the access URL
+                echo -e "${green}Access URL: https://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
+            fi
         fi
     else
         if [[ "$existing_hasDefaultCredential" == "true" ]]; then
@@ -139,7 +546,23 @@ config_after_install() {
             echo -e "${green}Password: ${config_password}${plain}"
             echo -e "###############################################"
         else
-            echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
+            echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
+        fi
+
+        # Existing install: if no cert configured, prompt user to set domain or self-signed
+        # Properly detect empty cert by checking if cert: line exists and has content after it
+        existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
+        if [[ -z "$existing_cert" ]]; then
+            echo ""
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${green}     SSL Certificate Setup (RECOMMENDED)   ${plain}"
+            echo -e "${green}═══════════════════════════════════════════${plain}"
+            echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+            echo ""
+            prompt_and_setup_ssl "${existing_port}" "${existing_webBasePath}" "${server_ip}"
+            echo -e "${green}Access URL:  https://${SSL_HOST}:${existing_port}/${existing_webBasePath}${plain}"
+        else
+            echo -e "${green}SSL certificate already configured. No action needed.${plain}"
         fi
     fi
     

+ 459 - 8
update.sh

@@ -70,33 +70,415 @@ arch() {
 
 echo "Arch: $(arch)"
 
+# Simple helpers
+is_ipv4() {
+    [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
+}
+is_ipv6() {
+    [[ "$1" =~ : ]] && return 0 || return 1
+}
+is_ip() {
+    is_ipv4 "$1" || is_ipv6 "$1"
+}
+is_domain() {
+    [[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+[A-Za-z]{2,}$ ]] && return 0 || return 1
+}
+
+gen_random_string() {
+    local length="$1"
+    local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | fold -w "$length" | head -n 1)
+    echo "$random_string"
+}
+
 install_base() {
     echo -e "${green}Updating and install dependency packages...${plain}"
     case "${release}" in
         ubuntu | debian | armbian)
-            apt-get update >/dev/null 2>&1 && apt-get install -y -q wget curl tar tzdata >/dev/null 2>&1
+            apt-get update >/dev/null 2>&1 && apt-get install -y -q wget curl tar tzdata openssl socat >/dev/null 2>&1
         ;;
         fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
-            dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata >/dev/null 2>&1
+            dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata openssl socat >/dev/null 2>&1
         ;;
         centos)
             if [[ "${VERSION_ID}" =~ ^7 ]]; then
-                yum -y update >/dev/null 2>&1 && yum install -y -q wget curl tar tzdata >/dev/null 2>&1
+                yum -y update >/dev/null 2>&1 && yum install -y -q wget curl tar tzdata openssl socat >/dev/null 2>&1
             else
-                dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata >/dev/null 2>&1
+                dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata openssl socat >/dev/null 2>&1
             fi
         ;;
         arch | manjaro | parch)
-            pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm wget curl tar tzdata >/dev/null 2>&1
+            pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm wget curl tar tzdata openssl socat >/dev/null 2>&1
         ;;
         opensuse-tumbleweed | opensuse-leap)
-            zypper refresh >/dev/null 2>&1 && zypper -q install -y wget curl tar timezone >/dev/null 2>&1
+            zypper refresh >/dev/null 2>&1 && zypper -q install -y wget curl tar timezone openssl socat >/dev/null 2>&1
         ;;
         alpine)
-            apk update >/dev/null 2>&1 && apk add wget curl tar tzdata >/dev/null 2>&1
+            apk update >/dev/null 2>&1 && apk add wget curl tar tzdata openssl socat >/dev/null 2>&1
         ;;
         *)
-            apt-get update >/dev/null 2>&1 && apt install -y -q wget curl tar tzdata >/dev/null 2>&1
+            apt-get update >/dev/null 2>&1 && apt install -y -q wget curl tar tzdata openssl socat >/dev/null 2>&1
+        ;;
+    esac
+}
+
+install_acme() {
+    echo -e "${green}Installing acme.sh for SSL certificate management...${plain}"
+    cd ~ || return 1
+    curl -s https://get.acme.sh | sh >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Failed to install acme.sh${plain}"
+        return 1
+    else
+        echo -e "${green}acme.sh installed successfully${plain}"
+    fi
+    return 0
+}
+
+setup_ssl_certificate() {
+    local domain="$1"
+    local server_ip="$2"
+    local existing_port="$3"
+    local existing_webBasePath="$4"
+    
+    echo -e "${green}Setting up SSL certificate...${plain}"
+    
+    # Check if acme.sh is installed
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        install_acme
+        if [ $? -ne 0 ]; then
+            echo -e "${yellow}Failed to install acme.sh, skipping SSL setup${plain}"
+            return 1
+        fi
+    fi
+    
+    # Create certificate directory
+    local certPath="/root/cert/${domain}"
+    mkdir -p "$certPath"
+    
+    # Issue certificate
+    echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
+    echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
+    
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
+    ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
+    
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
+        echo -e "${yellow}Please ensure port 80 is open and try again later with: x-ui${plain}"
+        rm -rf ~/.acme.sh/${domain} 2>/dev/null
+        rm -rf "$certPath" 2>/dev/null
+        return 1
+    fi
+    
+    # Install certificate
+    ~/.acme.sh/acme.sh --installcert -d ${domain} \
+        --key-file /root/cert/${domain}/privkey.pem \
+        --fullchain-file /root/cert/${domain}/fullchain.pem \
+        --reloadcmd "systemctl restart x-ui" >/dev/null 2>&1
+    
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Failed to install certificate${plain}"
+        return 1
+    fi
+    
+    # Enable auto-renew
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+    chmod 755 $certPath/* 2>/dev/null
+    
+    # Set certificate for panel
+    local webCertFile="/root/cert/${domain}/fullchain.pem"
+    local webKeyFile="/root/cert/${domain}/privkey.pem"
+    
+    if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+        /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" >/dev/null 2>&1
+        echo -e "${green}SSL certificate installed and configured successfully!${plain}"
+        return 0
+    else
+        echo -e "${yellow}Certificate files not found${plain}"
+        return 1
+    fi
+}
+
+# Fallback: generate a self-signed certificate (not publicly trusted)
+setup_self_signed_certificate() {
+    local name="$1"   # domain or IP to place in SAN
+    local certDir="/root/cert/selfsigned"
+
+    echo -e "${yellow}Generating a self-signed certificate (not publicly trusted)...${plain}"
+
+    mkdir -p "$certDir"
+
+    local sanExt=""
+    if is_ip "$name"; then
+        sanExt="IP:${name}"
+    else
+        sanExt="DNS:${name}"
+    fi
+
+    # Try -addext; fallback to config if not supported
+    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+        -keyout "${certDir}/privkey.pem" \
+        -out "${certDir}/fullchain.pem" \
+        -subj "/CN=${name}" \
+        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
+
+    if [[ $? -ne 0 ]]; then
+        local tmpCfg="${certDir}/openssl.cnf"
+        cat > "$tmpCfg" <<EOF
+[req]
+distinguished_name=req_distinguished_name
+req_extensions=v3_req
+[req_distinguished_name]
+[v3_req]
+subjectAltName=${sanExt}
+EOF
+        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+            -keyout "${certDir}/privkey.pem" \
+            -out "${certDir}/fullchain.pem" \
+            -subj "/CN=${name}" \
+            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
+        rm -f "$tmpCfg"
+    fi
+
+    if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
+        echo -e "${red}Failed to generate self-signed certificate${plain}"
+        return 1
+    fi
+
+    chmod 755 ${certDir}/* 2>/dev/null
+    /usr/local/x-ui/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
+    echo -e "${yellow}Self-signed certificate configured. Browsers will show a warning.${plain}"
+    return 0
+}
+# Comprehensive manual SSL certificate issuance via acme.sh
+ssl_cert_issue() {
+    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
+    local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
+    
+    # check for acme.sh first
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        echo "acme.sh could not be found. Installing now..."
+        cd ~ || return 1
+        curl -s https://get.acme.sh | sh
+        if [ $? -ne 0 ]; then
+            echo -e "${red}Failed to install acme.sh${plain}"
+            return 1
+        else
+            echo -e "${green}acme.sh installed successfully${plain}"
+        fi
+    fi
+
+    # get the domain here, and we need to verify it
+    local domain=""
+    while true; do
+        read -rp "Please enter your domain name: " domain
+        domain="${domain// /}"  # Trim whitespace
+        
+        if [[ -z "$domain" ]]; then
+            echo -e "${red}Domain name cannot be empty. Please try again.${plain}"
+            continue
+        fi
+        
+        if ! is_domain "$domain"; then
+            echo -e "${red}Invalid domain format: ${domain}. Please enter a valid domain name.${plain}"
+            continue
+        fi
+        
+        break
+    done
+    echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
+
+    # check if there already exists a certificate
+    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
+    if [ "${currentCert}" == "${domain}" ]; then
+        local certInfo=$(~/.acme.sh/acme.sh --list)
+        echo -e "${red}System already has certificates for this domain. Cannot issue again.${plain}"
+        echo -e "${yellow}Current certificate details:${plain}"
+        echo "$certInfo"
+        return 1
+    else
+        echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
+    fi
+
+    # create a directory for the certificate
+    certPath="/root/cert/${domain}"
+    if [ ! -d "$certPath" ]; then
+        mkdir -p "$certPath"
+    else
+        rm -rf "$certPath"
+        mkdir -p "$certPath"
+    fi
+
+    # get the port number for the standalone server
+    local WebPort=80
+    read -rp "Please choose which port to use (default is 80): " WebPort
+    if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
+        echo -e "${yellow}Your input ${WebPort} is invalid, will use default port 80.${plain}"
+        WebPort=80
+    fi
+    echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}"
+
+    # Stop panel temporarily
+    echo -e "${yellow}Stopping panel temporarily...${plain}"
+    systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
+
+    # issue the certificate
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
+    ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Issuing certificate failed, please check logs.${plain}"
+        rm -rf ~/.acme.sh/${domain}
+        systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+        return 1
+    else
+        echo -e "${green}Issuing certificate succeeded, installing certificates...${plain}"
+    fi
+
+    # Setup reload command
+    reloadCmd="systemctl restart x-ui || rc-service x-ui restart"
+    echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}"
+    echo -e "${green}This command will run on every certificate issue and renew.${plain}"
+    read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
+    if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
+        echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; systemctl restart x-ui"
+        echo -e "${green}\t2.${plain} Input your own command"
+        echo -e "${green}\t0.${plain} Keep default reloadcmd"
+        read -rp "Choose an option: " choice
+        case "$choice" in
+        1)
+            echo -e "${green}Reloadcmd is: systemctl reload nginx ; systemctl restart x-ui${plain}"
+            reloadCmd="systemctl reload nginx ; systemctl restart x-ui"
+            ;;
+        2)
+            echo -e "${yellow}It's recommended to put x-ui restart at the end${plain}"
+            read -rp "Please enter your custom reloadcmd: " reloadCmd
+            echo -e "${green}Reloadcmd is: ${reloadCmd}${plain}"
+            ;;
+        *)
+            echo -e "${green}Keeping default reloadcmd${plain}"
+            ;;
+        esac
+    fi
+
+    # install the certificate
+    ~/.acme.sh/acme.sh --installcert -d ${domain} \
+        --key-file /root/cert/${domain}/privkey.pem \
+        --fullchain-file /root/cert/${domain}/fullchain.pem --reloadcmd "${reloadCmd}"
+
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Installing certificate failed, exiting.${plain}"
+        rm -rf ~/.acme.sh/${domain}
+        systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+        return 1
+    else
+        echo -e "${green}Installing certificate succeeded, enabling auto renew...${plain}"
+    fi
+
+    # enable auto-renew
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
+        ls -lah /root/cert/${domain}/
+        chmod 755 $certPath/*
+    else
+        echo -e "${green}Auto renew succeeded, certificate details:${plain}"
+        ls -lah /root/cert/${domain}/
+        chmod 755 $certPath/*
+    fi
+
+    # Restart panel
+    systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
+
+    # Prompt user to set panel paths after successful certificate installation
+    read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel
+    if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
+        local webCertFile="/root/cert/${domain}/fullchain.pem"
+        local webKeyFile="/root/cert/${domain}/privkey.pem"
+
+        if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+            /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
+            echo -e "${green}Certificate paths set for the panel${plain}"
+            echo -e "${green}Certificate File: $webCertFile${plain}"
+            echo -e "${green}Private Key File: $webKeyFile${plain}"
+            echo ""
+            echo -e "${green}Access URL: https://${domain}:${existing_port}/${existing_webBasePath}${plain}"
+            echo -e "${yellow}Panel will restart to apply SSL certificate...${plain}"
+            systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null
+        else
+            echo -e "${red}Error: Certificate or private key file not found for domain: $domain.${plain}"
+        fi
+    else
+        echo -e "${yellow}Skipping panel path setting.${plain}"
+    fi
+    
+    return 0
+}
+# Unified interactive SSL setup (domain or self-signed)
+# Sets global `SSL_HOST` to the chosen domain/IP
+prompt_and_setup_ssl() {
+    local panel_port="$1"
+    local web_base_path="$2"   # expected without leading slash
+    local server_ip="$3"
+
+    local ssl_choice=""
+
+    echo -e "${yellow}Choose SSL certificate setup method:${plain}"
+    echo -e "${green}1.${plain} Let's Encrypt (domain required, recommended)"
+    echo -e "${green}2.${plain} Self-signed certificate (for testing/local use)"
+    read -rp "Choose an option (default 2): " ssl_choice
+    ssl_choice="${ssl_choice// /}"  # Trim whitespace
+    
+    # Default to 2 (self-signed) if not 1
+    if [[ "$ssl_choice" != "1" ]]; then
+        ssl_choice="2"
+    fi
+
+    case "$ssl_choice" in
+    1)
+        # User chose Let's Encrypt domain option
+        echo -e "${green}Using ssl_cert_issue() for comprehensive domain setup...${plain}"
+        ssl_cert_issue
+        # Extract the domain that was used from the certificate
+        local cert_domain=$(~/.acme.sh/acme.sh --list 2>/dev/null | tail -1 | awk '{print $1}')
+        if [[ -n "${cert_domain}" ]]; then
+            SSL_HOST="${cert_domain}"
+            echo -e "${green}✓ SSL certificate configured successfully with domain: ${cert_domain}${plain}"
+        else
+            echo -e "${yellow}SSL setup may have completed, but domain extraction failed${plain}"
+            SSL_HOST="${server_ip}"
+        fi
+        ;;
+    2)
+        # User chose self-signed option
+        # Stop panel if running
+        if [[ $release == "alpine" ]]; then
+            rc-service x-ui stop >/dev/null 2>&1
+        else
+            systemctl stop x-ui >/dev/null 2>&1
+        fi
+        echo -e "${yellow}Using server IP for self-signed certificate: ${server_ip}${plain}"
+        setup_self_signed_certificate "${server_ip}"
+        if [ $? -eq 0 ]; then
+            SSL_HOST="${server_ip}"
+            echo -e "${green}✓ Self-signed SSL configured successfully${plain}"
+        else
+            echo -e "${red}✗ Self-signed SSL setup failed${plain}"
+            SSL_HOST="${server_ip}"
+        fi
+        # Start panel after SSL is configured
+        if [[ $release == "alpine" ]]; then
+            rc-service x-ui start >/dev/null 2>&1
+        else
+            systemctl start x-ui >/dev/null 2>&1
+        fi
+        ;;
+    0)
+        echo -e "${yellow}Skipping SSL setup${plain}"
+        SSL_HOST="${server_ip}"
+        ;;
+    *)
+        echo -e "${red}Invalid option. Skipping SSL setup.${plain}"
+        SSL_HOST="${server_ip}"
         ;;
     esac
 }
@@ -105,6 +487,75 @@ config_after_update() {
     echo -e "${yellow}x-ui settings:${plain}"
     /usr/local/x-ui/x-ui setting -show true
     /usr/local/x-ui/x-ui migrate
+    
+    # Properly detect empty cert by checking if cert: line exists and has content after it
+    local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true 2>/dev/null | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
+    local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
+    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}' | sed 's#^/##')
+    
+    # Get server IP
+    local URL_lists=(
+        "https://api4.ipify.org"
+        "https://ipv4.icanhazip.com"
+        "https://v4.api.ipinfo.io/ip"
+        "https://ipv4.myexternalip.com/raw"
+        "https://4.ident.me"
+        "https://check-host.net/ip"
+    )
+    local server_ip=""
+    for ip_address in "${URL_lists[@]}"; do
+        server_ip=$(${curl_bin} -s --max-time 3 "${ip_address}" 2>/dev/null | tr -d '[:space:]')
+        if [[ -n "${server_ip}" ]]; then
+            break
+        fi
+    done
+    
+    # Handle missing/short webBasePath
+    if [[ ${#existing_webBasePath} -lt 4 ]]; then
+        echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
+        local config_webBasePath=$(gen_random_string 18)
+        /usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
+        existing_webBasePath="${config_webBasePath}"
+        echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
+    fi
+    
+    # Check and prompt for SSL if missing
+    if [[ -z "$existing_cert" ]]; then
+        echo ""
+        echo -e "${red}═══════════════════════════════════════════${plain}"
+        echo -e "${red}      ⚠ NO SSL CERTIFICATE DETECTED ⚠     ${plain}"
+        echo -e "${red}═══════════════════════════════════════════${plain}"
+        echo -e "${yellow}For security, SSL certificate is MANDATORY for all panels.${plain}"
+        echo -e "${yellow}Let's Encrypt requires a domain name; IP certs are not issued. Use self-signed for IP.${plain}"
+        echo ""
+        
+        if [[ -z "${server_ip}" ]]; then
+            echo -e "${red}Failed to detect server IP${plain}"
+            echo -e "${yellow}Please configure SSL manually using: x-ui${plain}"
+            return
+        fi
+        
+        # Prompt and setup SSL (domain or self-signed)
+        prompt_and_setup_ssl "${existing_port}" "${existing_webBasePath}" "${server_ip}"
+        
+        echo ""
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+        echo -e "${green}     Panel Access Information              ${plain}"
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+        echo -e "${green}Access URL: https://${SSL_HOST}:${existing_port}/${existing_webBasePath}${plain}"
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+        echo -e "${yellow}⚠ SSL Certificate: Enabled and configured${plain}"
+    else
+        echo -e "${green}SSL certificate is already configured${plain}"
+        # Show access URL with existing certificate
+        local cert_domain=$(basename "$(dirname "$existing_cert")")
+        echo ""
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+        echo -e "${green}     Panel Access Information              ${plain}"
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+        echo -e "${green}Access URL: https://${cert_domain}:${existing_port}/${existing_webBasePath}${plain}"
+        echo -e "${green}═══════════════════════════════════════════${plain}"
+    fi
 }
 
 update_x-ui() {

+ 236 - 17
x-ui.sh

@@ -213,6 +213,57 @@ gen_random_string() {
     echo "$random_string"
 }
 
+# Generate and configure a self-signed SSL certificate
+setup_self_signed_certificate() {
+    local name="$1"   # domain or IP to place in SAN
+    local certDir="/root/cert/selfsigned"
+
+    LOGI "Generating a self-signed certificate (not publicly trusted)..."
+
+    mkdir -p "$certDir"
+
+    local sanExt=""
+    if [[ "$name" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ || "$name" =~ : ]]; then
+        sanExt="IP:${name}"
+    else
+        sanExt="DNS:${name}"
+    fi
+
+    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+        -keyout "${certDir}/privkey.pem" \
+        -out "${certDir}/fullchain.pem" \
+        -subj "/CN=${name}" \
+        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
+
+    if [[ $? -ne 0 ]]; then
+        local tmpCfg="${certDir}/openssl.cnf"
+        cat > "$tmpCfg" <<EOF
+[req]
+distinguished_name=req_distinguished_name
+req_extensions=v3_req
+[req_distinguished_name]
+[v3_req]
+subjectAltName=${sanExt}
+EOF
+        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
+            -keyout "${certDir}/privkey.pem" \
+            -out "${certDir}/fullchain.pem" \
+            -subj "/CN=${name}" \
+            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
+        rm -f "$tmpCfg"
+    fi
+
+    if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
+        LOGE "Failed to generate self-signed certificate"
+        return 1
+    fi
+
+    chmod 755 ${certDir}/* >/dev/null 2>&1
+    /usr/local/x-ui/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
+    LOGI "Self-signed certificate configured. Browsers will show a warning."
+    return 0
+}
+
 reset_webbasepath() {
     echo -e "${yellow}Resetting Web Base Path${plain}"
 
@@ -256,7 +307,7 @@ check_config() {
 
     local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}')
     local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}')
-    local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}')
+    local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
     local server_ip=$(curl -s --max-time 3 https://api.ipify.org)
     if [ -z "$server_ip" ]; then
         server_ip=$(curl -s --max-time 3 https://4.ident.me)
@@ -271,7 +322,22 @@ check_config() {
             echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
         fi
     else
-        echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+        echo -e "${red}⚠ WARNING: No SSL certificate configured!${plain}"
+        read -rp "Generate a self-signed SSL certificate now? [y/N]: " gen_self
+        if [[ "$gen_self" == "y" || "$gen_self" == "Y" ]]; then
+            stop >/dev/null 2>&1
+            setup_self_signed_certificate "${server_ip}"
+            if [[ $? -eq 0 ]]; then
+                restart >/dev/null 2>&1
+                echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+            else
+                LOGE "Self-signed SSL setup failed."
+                echo -e "${yellow}You can try again via option 18 (SSL Certificate Management).${plain}"
+            fi
+        else
+            echo -e "${yellow}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+            echo -e "${yellow}For security, please configure SSL certificate using option 18 (SSL Certificate Management)${plain}"
+        fi
     fi
 }
 
@@ -958,6 +1024,7 @@ ssl_cert_issue_main() {
     echo -e "${green}\t3.${plain} Force Renew"
     echo -e "${green}\t4.${plain} Show Existing Domains"
     echo -e "${green}\t5.${plain} Set Cert paths for the panel"
+    echo -e "${green}\t6.${plain} Auto SSL for Server IP"
     echo -e "${green}\t0.${plain} Back to Main Menu"
 
     read -rp "Choose an option: " choice
@@ -1051,6 +1118,16 @@ ssl_cert_issue_main() {
         fi
         ssl_cert_issue_main
         ;;
+    6)
+        echo -e "${yellow}Automatic SSL Certificate for Server IP${plain}"
+        echo -e "This will automatically obtain and configure an SSL certificate for your server's IP address."
+        echo -e "${yellow}Note: Let's Encrypt supports IP certificates. Make sure port 80 is open.${plain}"
+        confirm "Do you want to proceed?" "y"
+        if [[ $? == 0 ]]; then
+            ssl_cert_issue_for_ip
+        fi
+        ssl_cert_issue_main
+        ;;
 
     *)
         echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
@@ -1059,6 +1136,134 @@ ssl_cert_issue_main() {
     esac
 }
 
+ssl_cert_issue_for_ip() {
+    LOGI "Starting automatic SSL certificate generation for server IP..."
+    
+    local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
+    local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
+    
+    # Get server IP
+    local server_ip=$(curl -s --max-time 3 https://api.ipify.org)
+    if [ -z "$server_ip" ]; then
+        server_ip=$(curl -s --max-time 3 https://4.ident.me)
+    fi
+    
+    if [ -z "$server_ip" ]; then
+        LOGE "Failed to get server IP address"
+        return 1
+    fi
+    
+    LOGI "Server IP detected: ${server_ip}"
+    
+    # check for acme.sh first
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        LOGI "acme.sh not found, installing..."
+        install_acme
+        if [ $? -ne 0 ]; then
+            LOGE "Failed to install acme.sh"
+            return 1
+        fi
+    fi
+    
+    # install socat
+    case "${release}" in
+    ubuntu | debian | armbian)
+        apt-get update >/dev/null 2>&1 && apt-get install socat -y >/dev/null 2>&1
+        ;;
+    fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
+        dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+        ;;
+    centos)
+        if [[ "${VERSION_ID}" =~ ^7 ]]; then
+            yum -y update >/dev/null 2>&1 && yum -y install socat >/dev/null 2>&1
+        else
+            dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+        fi
+        ;;
+    arch | manjaro | parch)
+        pacman -Sy --noconfirm socat >/dev/null 2>&1
+        ;;
+    opensuse-tumbleweed | opensuse-leap)
+        zypper refresh >/dev/null 2>&1 && zypper -q install -y socat >/dev/null 2>&1
+        ;;
+    alpine)
+        apk add socat curl openssl >/dev/null 2>&1
+        ;;
+    *)
+        LOGW "Unsupported OS for automatic socat installation"
+        ;;
+    esac
+    
+    # check if certificate already exists for this IP
+    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
+    if [ "${currentCert}" == "${server_ip}" ]; then
+        LOGI "Certificate already exists for IP: ${server_ip}"
+        certPath="/root/cert/${server_ip}"
+    else
+        # create directory for certificate
+        certPath="/root/cert/${server_ip}"
+        if [ ! -d "$certPath" ]; then
+            mkdir -p "$certPath"
+        else
+            rm -rf "$certPath"
+            mkdir -p "$certPath"
+        fi
+        
+        # Use port 80 for certificate issuance
+        local WebPort=80
+        LOGI "Using port ${WebPort} to issue certificate for IP: ${server_ip}"
+        LOGI "Make sure port ${WebPort} is open and not in use..."
+        
+        # issue the certificate for IP
+        ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
+        ~/.acme.sh/acme.sh --issue -d ${server_ip} --listen-v6 --standalone --httpport ${WebPort} --force
+        if [ $? -ne 0 ]; then
+            LOGE "Failed to issue certificate for IP: ${server_ip}"
+            LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet"
+            rm -rf ~/.acme.sh/${server_ip}
+            return 1
+        else
+            LOGI "Certificate issued successfully for IP: ${server_ip}"
+        fi
+        
+        # install the certificate
+        ~/.acme.sh/acme.sh --installcert -d ${server_ip} \
+            --key-file /root/cert/${server_ip}/privkey.pem \
+            --fullchain-file /root/cert/${server_ip}/fullchain.pem \
+            --reloadcmd "x-ui restart"
+        
+        if [ $? -ne 0 ]; then
+            LOGE "Failed to install certificate"
+            rm -rf ~/.acme.sh/${server_ip}
+            return 1
+        else
+            LOGI "Certificate installed successfully"
+        fi
+        
+        # enable auto-renew
+        ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+        chmod 755 $certPath/*
+    fi
+    
+    # Set certificate paths for the panel
+    local webCertFile="/root/cert/${server_ip}/fullchain.pem"
+    local webKeyFile="/root/cert/${server_ip}/privkey.pem"
+    
+    if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+        /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
+        LOGI "Certificate configured for panel"
+        LOGI "  - Certificate File: $webCertFile"
+        LOGI "  - Private Key File: $webKeyFile"
+        echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+        LOGI "Panel will restart to apply SSL certificate..."
+        restart
+        return 0
+    else
+        LOGE "Certificate files not found after installation"
+        return 1
+    fi
+}
+
 ssl_cert_issue() {
     local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
     local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
@@ -1072,33 +1277,32 @@ ssl_cert_issue() {
         fi
     fi
 
-    # install socat second
+    # install socat
     case "${release}" in
     ubuntu | debian | armbian)
-        apt-get update && apt-get install socat -y
+        apt-get update >/dev/null 2>&1 && apt-get install socat -y >/dev/null 2>&1
         ;;
     fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
-        dnf -y update && dnf -y install socat
+        dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
         ;;
     centos)
-            if [[ "${VERSION_ID}" =~ ^7 ]]; then
-                yum -y update && yum -y install socat
-            else
-                dnf -y update && dnf -y install socat
-            fi
+        if [[ "${VERSION_ID}" =~ ^7 ]]; then
+            yum -y update >/dev/null 2>&1 && yum -y install socat >/dev/null 2>&1
+        else
+            dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+        fi
         ;;
     arch | manjaro | parch)
-        pacman -Sy --noconfirm socat
+        pacman -Sy --noconfirm socat >/dev/null 2>&1
         ;;
-	opensuse-tumbleweed | opensuse-leap)
-        zypper refresh && zypper -q install -y socat
+    opensuse-tumbleweed | opensuse-leap)
+        zypper refresh >/dev/null 2>&1 && zypper -q install -y socat >/dev/null 2>&1
         ;;
     alpine)
-        apk add socat curl openssl
+        apk add socat curl openssl >/dev/null 2>&1
         ;;
     *)
-        echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
-        exit 1
+        LOGW "Unsupported OS for automatic socat installation"
         ;;
     esac
     if [ $? -ne 0 ]; then
@@ -1110,7 +1314,22 @@ ssl_cert_issue() {
 
     # get the domain here, and we need to verify it
     local domain=""
-    read -rp "Please enter your domain name: " domain
+    while true; do
+        read -rp "Please enter your domain name: " domain
+        domain="${domain// /}"  # Trim whitespace
+        
+        if [[ -z "$domain" ]]; then
+            LOGE "Domain name cannot be empty. Please try again."
+            continue
+        fi
+        
+        if ! is_domain "$domain"; then
+            LOGE "Invalid domain format: ${domain}. Please enter a valid domain name."
+            continue
+        fi
+        
+        break
+    done
     LOGD "Your domain is: ${domain}, checking it..."
 
     # check if there already exists a certificate