Ctrl K
ComunidadGuías…

bm.sh

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# Add domain name and email

CONTAINER_PROJECT_NAME=billionmail
PGSQL_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-pgsql-billionmail-1"
DOVECOT_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-dovecot-billionmail-1"
POSTFIX_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-postfix-billionmail-1"
RSPAMD_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-rspamd-billionmail-1"
create_time=$(date +%s)
time=$(date +%Y_%m_%d_%H_%M_%S)

if [ $(whoami) != "root" ];then
    echo -e "Non-root install, please try the following solutions: \n   1.Please switch to root user install \n   2.Try executing the following install commands: \n     sudo bash $0 $@"
    exit 1
fi

PWD_d=`pwd`

SWITCH_TO_BILLIONMAIL_DIR(){
    if [ -f "/opt/PWD-Billion-Mail.txt" ]; then
        DIR=$(cat /opt/PWD-Billion-Mail.txt)
        if [ -d "${DIR}" ]; then
            cd "${DIR}"
            echo "Enter the BillionMail project directory: ${DIR}"
        fi
    fi
}

if [ -s ".env" ]; then
   CHECK_BILLIONMAIL=$(grep "BILLIONMAIL_HOSTNAME" .env)
   if [ -z "${CHECK_BILLIONMAIL}" ]; then
        SWITCH_TO_BILLIONMAIL_DIR
   fi
else
    SWITCH_TO_BILLIONMAIL_DIR
fi

if [ ! -s ".env" ]; then
    ls -al
    echo " The .env file does not exist. Cannot continue operation, please operate in the BillionMail project directory"
    exit 1
fi

source .env
if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then
    echo "The obtained .env configuration exception is empty."   
    exit 1
fi

Red_Error(){
    echo '=================================================';
    printf '\033[1;31;40m%b\033[0m\n' "$@";
    exit 1;
}

Command_Exists() {
    command -v "$@" >/dev/null 2>&1
}


Docker_Compose_Check(){

    if Command_Exists docker-compose ; then
        DOCKER_COMPOSE="docker-compose"
    else 
        if Command_Exists docker; then
            Docker_compose="docker compose version"
            if $Docker_compose >/dev/null 2>&1; then
                DOCKER_COMPOSE="docker compose"
            fi
        else
            Red_Error "ERROR: Docker Compose does not exist"
        fi

    fi
}
Docker_Compose_Check

GET_SERVICE_NAME() {

    # ALL_SERVICE_NAME=$(${DOCKER_COMPOSE} config --services |grep "${SERVICE}")
    # echo "${ALL_SERVICE_NAME}"

    if [[ "${SERVICE}" == "core" ]] || [[ "${SERVICE}" == "manage" ]]; then
        #echo "Getting the "${SERVICE}" service..."
        SERVICE_NAME=$(${DOCKER_COMPOSE} ps -a --format " {{.Service}} {{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    
    elif [[ "${SERVICE}" == "postfix" ]] || [[ "${SERVICE}" == "postfix-billionmail" ]]; then
        SERVICE_NAME="postfix-billionmail"
    
    elif [[ "${SERVICE}" == "dovecot" ]] || [[ "${SERVICE}" == "dovecot-billionmail" ]]; then
        SERVICE_NAME="dovecot-billionmail"
    
    elif [[ "${SERVICE}" == "rspamd" ]] || [[ "${SERVICE}" == "rspamd-billionmail" ]]; then
        SERVICE_NAME="rspamd-billionmail"
    
    elif [[ "${SERVICE}" == "pgsql" ]] || [[ "${SERVICE}" == "postgres" ]] || [[ "${SERVICE}" == "pgsql-billionmail" ]]; then
        SERVICE_NAME="pgsql-billionmail"
    
    elif [[ "${SERVICE}" == "redis" ]] || [[ "${SERVICE}" == "redis-billionmail" ]]; then
        SERVICE_NAME="redis-billionmail"

    elif [[ "${SERVICE}" == "webmail" ]] || [[ "${SERVICE}" == "roundcube" ]] || [[ "${SERVICE}" == "webmail-billionmail" ]]; then
        SERVICE_NAME="webmail-billionmail"
    
    else
        echo "Please use: core|postfix|dovecot|rspamd|pgsql|redis|webmail"
        Red_Error "ERROR: The "${SERVICE}" service does not exist"

    fi

    if [ -z "${SERVICE_NAME}" ]; then
        Red_Error "ERROR: The "${SERVICE}" service does not exist"
    fi

}


GET_CONTAINER_ID() {

    if [ -z "${CONTAINER}" ]; then
        CONTAINER="$1"
    fi

    if [[ "${CONTAINER}" == "core" ]] || [[ "${CONTAINER}" == "manage" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    
    elif [[ ""${CONTAINER}"" == "postfix" ]] || [[ ""${CONTAINER}"" == "postfix-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/postfix:" | awk '{print $1}' )
    
    elif [[ ""${CONTAINER}"" == "dovecot" ]] || [[ ""${CONTAINER}"" == "dovecot-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/dovecot:" | awk '{print $1}' )
    
    elif [[ ""${CONTAINER}"" == "rspamd" ]] || [[ ""${CONTAINER}"" == "rspamd-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/rspamd:" | awk '{print $1}' )
    
    elif [[ ""${CONTAINER}"" == "pgsql" ]] || [[ ""${CONTAINER}"" == "postgres" ]] || [[ ""${CONTAINER}"" == "pgsql-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "postgres:" | awk '{print $1}' )
    
    elif [[ ""${CONTAINER}"" == "redis" ]] || [[ ""${CONTAINER}"" == "redis-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "redis:" | awk '{print $1}' )

    elif [[ ""${CONTAINER}"" == "webmail" ]] || [[ ""${CONTAINER}"" == "roundcube" ]] || [[ ""${CONTAINER}"" == "webmail-billionmail" ]]; then
        CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "roundcubemail:" | awk '{print $1}' )
    
    else
        echo "Please use: core|postfix|dovecot|rspamd|pgsql|redis|webmail"
        Red_Error "ERROR: The ""${CONTAINER}"" container does not exist"
    fi

}


Init_Email() {
    input="$2"

    # Regular expression verification email address format
    if [[ "$input" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
        mailbox=$(echo "$input" | cut -d '@' -f 1 | tr '[:upper:]' '[:lower:]')
        BILLIONMAIL_HOSTNAME=$(echo "$input" | cut -d '@' -f 2 | tr '[:upper:]' '[:lower:]')
        
    else
        echo "Enter an email address that is not a legal one: $input"
        echo "Example: user@example.com"
        exit 1
    fi
}


Init_Domain() {
    BILLIONMAIL_HOSTNAME="$2"

    # Regular expression check domain name format
    # if [[ ! "${BILLIONMAIL_HOSTNAME}" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*$ ]]; then
    if [[ ! "${BILLIONMAIL_HOSTNAME}" =~ ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]]; then
        echo -e "\e[31m(${BILLIONMAIL_HOSTNAME}) is not a FQDN!\e[0m"
        echo "Please change it to a FQDN"
        exit 1
    elif [[ "${BILLIONMAIL_HOSTNAME: -1}" == "." ]]; then
        echo "(${BILLIONMAIL_HOSTNAME}) is ending with a dot. This is not a valid FQDN!"
        exit 1

    fi

    # Convert to lowercase
    BILLIONMAIL_HOSTNAME=$(echo "${BILLIONMAIL_HOSTNAME}" | tr '[:upper:]' '[:lower:]')

}

Domain_DKIM_record(){
    if [ -z "${BILLIONMAIL_HOSTNAME}" ]; then
        echo "Please enter the domain name"
        exit 1
    fi

    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}")
    if [ -z "${Check_domain}" ]; then
        echo "Error: ${BILLIONMAIL_HOSTNAME} Domain does not exist."
        exit 1
    fi

    ## DKIM key generation
    docker exec -i -e BILLIONMAIL_HOSTNAME=${BILLIONMAIL_HOSTNAME} ${RSPAMD_CONTAINER_NAME} bash -c 'cat << "EOF" > /tmp/1.sh
#!/bin/bash
if [ ! -d "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/" ]; then
    mkdir -p "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/"
fi
if [ -f "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.private" ] && [ -f "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub" ]; then
    #echo "DKIM key already exists, skipping generation."
    exit 0
fi

rspamadm dkim_keygen -s 'default' -b 1024 -d {domain} -k "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.private" > "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub"
if [ $? -eq 0 ]; then
    # Define the root directory for private keys
    DKIM_KEYS_DIR="/var/lib/rspamd/dkim"
    # Configuration file path
    CONFIG_FILE="/etc/rspamd/local.d/dkim_signing.conf"
    # Check if the configuration file exists, if not, create it
    if [ ! -f "${CONFIG_FILE}" ]; then
        touch "${CONFIG_FILE}"
        echo "${CONFIG_FILE}"
        echo "domain {" > "${CONFIG_FILE}"
        echo "#BT_DOMAIN_DKIM_BEGIN" >> "${CONFIG_FILE}"
        echo "#BT_DOMAIN_DKIM_END" >> "${CONFIG_FILE}"
        echo "}" >> "${CONFIG_FILE}"
    fi
    # Scan the DKIM_KEYS_DIR directory to get all domains
    DOMAINS=()
    for DOMAIN_DIR in "${DKIM_KEYS_DIR}"/*; do
        if [ -d "${DOMAIN_DIR}" ]; then
            DOMAIN_NAME=$(basename "${DOMAIN_DIR}")
            PRIVATE_KEY_PATH="${DOMAIN_DIR}/default.private"
            if [ -f "${PRIVATE_KEY_PATH}" ]; then
                DOMAINS+=("${DOMAIN_NAME}:${PRIVATE_KEY_PATH}")
            else
                echo "Warning: Private key file ${PRIVATE_KEY_PATH} for domain ${DOMAIN_NAME} does not exist, skipping configuration."
            fi
        fi
    done
    # Add domain configurations
    for DOMAIN in "${DOMAINS[@]}"; do
        DOMAIN_NAME=$(echo "${DOMAIN}" | cut -d':' -f1)
        PRIVATE_KEY_PATH=$(echo "${DOMAIN}" | cut -d':' -f2)
        # Check if the domain already exists
        if grep -wq "${DOMAIN_NAME}" "${CONFIG_FILE}"; then
            # echo "Domain ${DOMAIN_NAME} already exists, skipping configuration."
            continue
        fi
        # Insert domain configuration between #BT_DOMAIN_DKIM_BEGIN and #BT_DOMAIN_DKIM_END
        sed -i "/^#BT_DOMAIN_DKIM_BEGIN$/a #${DOMAIN_NAME}_DKIM_BEGIN\n  ${DOMAIN_NAME} {\n    selectors [\n     {\n       path: \"${PRIVATE_KEY_PATH}\";\n       selector: \"default\"\n     }\n   ]\n }\n#${DOMAIN_NAME}_DKIM_END" "${CONFIG_FILE}"
        # echo "Domain ${DOMAIN_NAME} has been successfully added to the configuration file."
    done
    # echo "DKIM configuration update completed."
else
    echo -e "DKIM key generation failed!"
    exit 1
fi
chmod 755 -R "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/"
EOF'
    docker exec -i -e BILLIONMAIL_HOSTNAME=${BILLIONMAIL_HOSTNAME} ${RSPAMD_CONTAINER_NAME} bash /tmp/1.sh && rm -f /tmp/1.sh
    DKIM_RECORD=$(docker exec ${RSPAMD_CONTAINER_NAME} cat "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub")
    # echo "DKIM RECORD: ${DKIM_RECORD}"
    
}


Domain_record() {

    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}")
    if [ "${Check_domain}" ]; then

        IPV4_ADDRESS=$(curl -sS -4 --connect-timeout 10 -m 20 https://ifconfig.me)
        if [ -z "${IPV4_ADDRESS}" ]; then
            IPV4_ADDRESS=$(curl -sSk --connect-timeout 10 -m 20 https://www.aapanel.com/api/common/getClientIP)
        fi
        ipv4_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
        if [[ ${IPV4_ADDRESS} =~ ${ipv4_regex} ]]; then        
            echo "${IPV4_ADDRESS}" >/dev/null 2>&1
        elif [ -z "${IPV4_ADDRESS}" ]; then
            IPV4_ADDRESS="YOUR_SERVER_IPV4_ADDRESS"
        fi
        echo -e ""
        echo -e "\e[31mPlease add the following record to your domain name\e[0m"
        echo -e "==========================================================="
        echo -e " Type | Host record    |    IPv4 address   |"
        echo -e "  \e[1;33mA\e[0m   | \e[1;33mmail.${BILLIONMAIL_HOSTNAME}\e[0m | \e[1;33m${IPV4_ADDRESS}\e[0m |"
        echo -e "==========================================================="
        echo -e " Type | Host record | MX priority |  Record value    "
        echo -e "  \e[1;33mMX\e[0m  |     \e[1;33m@\e[0m       |      \e[1;33m10\e[0m     | \e[1;33mmail.${BILLIONMAIL_HOSTNAME}\e[0m "
        echo -e "==========================================================="
        if [ "${IPV4_ADDRESS}" ]; then
            echo -e " Type | Host record |    Record value   |"
            echo -e "  \e[1;33mTXT\e[0m |     \e[1;33m@\e[0m       | \e[1;33mv=spf1 +a +mx +ip4:${IPV4_ADDRESS} -all\e[0m |"
        else
            echo -e " Type | Host record |    Record value   |"
            echo -e "  \e[1;33mTXT\e[0m |     \e[1;33m@\e[0m       | \e[1;33mv=spf1 +a +mx -all\e[0m |"
        fi
        echo -e "==========================================================="
        echo -e " Type | Host record |    Record value     |"
        echo -e "  \e[1;33mTXT\e[0m |   \e[1;33m_dmarc\e[0m    | \e[1;33mv=DMARC1;p=quarantine;rua=mailto:admin@${BILLIONMAIL_HOSTNAME}\e[0m |"
        echo -e "==========================================================="

        Domain_DKIM_record

        if [ "${DKIM_RECORD}" ]; then
            DKIM_RECORD=$(echo "${DKIM_RECORD}" | awk -F'"' '{print $2 $4}' | tr -d '[:space:]')
            #echo "DKIM RECORD: ${DKIM_RECORD}"
            echo -e " Type |  Host record       |    Record value     |"
            echo -e "  \e[1;33mTXT\e[0m | \e[1;33mdefault._domainkey\e[0m | \e[1;33m${DKIM_RECORD}\e[0m |<-- Start from \"v=DKIM1\" end, A single line."
            echo -e "==========================================================="
        else
            echo -e "${BILLIONMAIL_HOSTNAME} DKIM key generation failed!"
        fi
    else
        echo -e "Domain ${BILLIONMAIL_HOSTNAME} does not exist."
    fi
}


Select_Domain_data(){

    # Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain;")
    # echo "${Check_domain}"
    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT domain FROM domain;")
    echo "${Check_domain}"
}

Select_Email_data(){

    Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT username FROM mailbox;")
    echo "${Check_mailbox}"
}

Add_Domain() {

    echo "Creating domain..."
    
    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}")
    if [ -z "${Check_domain}" ]; then
        # Create a domain
        
        docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "INSERT INTO domain (domain, a_record, mailboxes, mailbox_quota, quota, rate_limit, create_time, active)
        VALUES ('${BILLIONMAIL_HOSTNAME}', 'mail.${BILLIONMAIL_HOSTNAME}', 500, 5368709120, 5368709120, 12, ${create_time}, 1);"
        if [ $? -eq 0 ]; then
            echo "${BILLIONMAIL_HOSTNAME} Domain creation was successful!"
            Domain_record
        else
            Red_Error "Domain creation failed!"
        fi
    else
        echo ""${Check_domain}" Domain already exists!"
    fi
}

Del_Domain() {
    echo "Deleting domain..."

    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}")
    
    if [ -z "${Check_domain}" ]; then
        echo "Domain '${BILLIONMAIL_HOSTNAME}' does not exist!"
    else
        # Delete the domain
        docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "DELETE FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';"
        if [ $? -eq 0 ]; then
            echo "Domain '${BILLIONMAIL_HOSTNAME}' deleted successfully!"
        else
            Red_Error "Failed to delete domain '${BILLIONMAIL_HOSTNAME}'!"
        fi
    fi
}


Add_Email() {
    echo "Creating mailbox..."

    Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}")
    if [ -z "${Check_domain}" ]; then
        Red_Error "Domain '${BILLIONMAIL_HOSTNAME}' does not exist, please create it first!"
    fi

    # Create a mailbox
    if [ -z "${mailbox}" ]; then
        mailbox=$(LC_ALL=C </dev/urandom tr -dc a-z0-9 2> /dev/null | head -c 6)
        echo "Generate mailbox: ${mailbox}"
    else
        mailbox=$(echo "${mailbox}" | tr '[:upper:]' '[:lower:]')
    fi
    Generate_mailbox_password=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 16)
    #echo "Generate_mailbox_password: ${Generate_mailbox_password}"
    Encrypt_mailbox_password=$(docker exec -i ${DOVECOT_CONTAINER_NAME} doveadm pw -s MD5-CRYPT -p "${Generate_mailbox_password}" | sed 's/{MD5-CRYPT}//')
    if [ $? -eq 0 ]; then
        echo "Generate_mailbox_password: ${Generate_mailbox_password}"
        echo "mailbox_password: ${Encrypt_mailbox_password}"
    else
        # Generate the default password after failure: BILLIONMAIL
        Generate_mailbox_password="BILLIONMAIL"
        Encrypt_mailbox_password='$1$ELBUCcYE$TbdGKBvLkFbjQguDbi3s01'
        echo "Generate_mailbox_password--default: ${Generate_mailbox_password}"
        Default_password=1
    fi

    if [ "${Default_password}" != 1 ]; then
        # password_encode 
        # Base64 encoding of strings
        b64_data=$(echo -n "${Generate_mailbox_password}" | base64)
        # Convert characters to hexadecimal
        password_encode=$(echo -n "${b64_data}" | while IFS= read -r -n1 char; do printf "%02x" "'$char"; done)
        echo "password_encode: ${password_encode}"
        if [ -z ${password_encode} ]; then
            b64_data=$(echo -n "${Generate_mailbox_password}" | base64)
            password_encode=$(echo -n "${b64_data}" | od -A n -t x1 | tr -d ' \n')
            echo "password_encode: ${password_encode}"
        fi
    else
        password_encode="516b6c4d54456c50546b31425355773d"
    fi

    Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" | grep -w "${mailbox}@${BILLIONMAIL_HOSTNAME}")
    if [ -z "${Check_mailbox}" ]; then
        INSERT_mailbox='INSERT INTO mailbox (username, password, password_encode, full_name, is_admin, maildir, quota, local_part, domain, create_time, update_time, active)
        VALUES (
            '\'${mailbox}@${BILLIONMAIL_HOSTNAME}\'',
            '\'${Encrypt_mailbox_password}\'',
            '\'${password_encode}\'',
            '\'${mailbox}\'',
            0,
            '\'${mailbox}@${BILLIONMAIL_HOSTNAME}/\'',
            5368709120,
            '\'${mailbox}\'',
            '\'${BILLIONMAIL_HOSTNAME}\'',
            '${create_time}',
            '${create_time}',
            1
        );'
        echo "$INSERT_mailbox"
        docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "$INSERT_mailbox"
        if [ $? -eq 0 ]; then
            echo "Mailbox creation was successful!"
        else
            Red_Error "Mailbox creation failed!"
        fi
        #docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';"
    else
        echo ""${Check_mailbox}" Mailbox already exists!"
    fi
    
    echo -e "\e[31mPlease save the following information:\e[0m"
    echo -e "Mailbox (e-mail): \e[33m${mailbox}@${BILLIONMAIL_HOSTNAME}\e[0m \nPassword: \e[33m${Generate_mailbox_password}\e[0m" 
}


Del_Email() {
    
    echo "Deleting mailbox..."

    Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" | grep -w "${mailbox}@${BILLIONMAIL_HOSTNAME}")
    
    if [ -z "${Check_mailbox}" ]; then
        echo "Mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}' does not exist!"
    else
        docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "DELETE FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';"
        if [ $? -eq 0 ]; then
            echo "Mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}' deleted successfully!"
        else
            Red_Error "Failed to delete mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}'!"
        fi
    fi
}

Update_BillionMail() {
    BRANCH="main"
    if [ -f "update.sh" ]; then
        echo -e "Checking for update.sh script..."
        MD5_1="$(md5sum update.sh)"
        git fetch origin
        git checkout "origin/${BRANCH}" update.sh
        MD5_2=$(md5sum update.sh)
        if [[ "${MD5_1}" != "${MD5_2}" ]]; then
            #echo -e "\033[33m update.sh is changed, please run update.sh script again.\033[0m"
            chmod +x update.sh
            echo y | ./update.sh
            #exit 1
        else
            chmod +x update.sh
            echo y | ./update.sh
        fi
    else
        Red_Error "Error: update.sh script does not exist!"
    fi
}


Default_info() {

    ipv4_address=""
    ipv6_address=""
    if [ "$address" = "" ];then
            
        ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://ifconfig.me 2>&1)
        if [ -z "${ipv4_address}" ];then
                ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://www.aapanel.com/api/common/getClientIP 2>&1)
                if [ -z "${ipv4_address}" ];then
                    ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://www.bt.cn/Api/getIpAddress 2>&1)
                fi
        fi
        IPV4_REGEX="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
        if ! [[ $ipv4_address =~ $IPV4_REGEX ]]; then
                ipv4_address=""
        fi
        
        ipv6_address=$(curl -6 -sSf --connect-timeout 10 -m 15 https://ifconfig.me 2>&1)
        IPV6_REGEX="^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$"
        if ! [[ $ipv6_address =~ $IPV6_REGEX ]]; then
                ipv6_address=""
        else
            if [[ ! $ipv6_address =~ ^\[ ]]; then
                ipv6_address="[$ipv6_address]"
            fi
        fi

        if [ "$address" = "" ] && [ "$ipv4_address" = "" ] && [ "$ipv6_address" = "" ];then
            address="SERVER_IP"
            echo -e "\033[33mFailed to obtain Internet IP, please use the server Internet IP+PORT to access.\033[0m"
        fi
    fi

    LOCAL_IP=$(ip addr | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -E -v "^127\.|^255\.|^0\." | head -n 1)
    echo -e "=================================================================="
    echo -e "\033[32mBillionMail default info!\033[0m"
    echo -e "=================================================================="
    pool=https

    if [ -f "core-data/billionmail_hostname.txt" ];then
        BILLIONMAIL_Domain=$(cat core-data/billionmail_hostname.txt)
        if [ "${HTTPS_PORT}" = "443" ];then
            echo  "BillionMail Domain Address:        ${pool}://${BILLIONMAIL_Domain}/${SafePath}"
        else
            echo  "BillionMail Domain Address:        ${pool}://${BILLIONMAIL_Domain}:${HTTPS_PORT}/${SafePath}"
        fi
    fi
    
    if [ "${ipv6_address}" ];then
        if [ "${HTTPS_PORT}" = "443" ];then
            echo  "BillionMail Internet IPv6 Address: ${pool}://${ipv6_address}/${SafePath}"
        else
            echo  "BillionMail Internet IPv6 Address: ${pool}://${ipv6_address}:${HTTPS_PORT}/${SafePath}"
        fi
    fi
    if [ "${ipv4_address}" ];then
        if [ "${HTTPS_PORT}" = "443" ];then
            echo  "BillionMail Internet IPv4 Address: ${pool}://${ipv4_address}/${SafePath}"
        else
            echo  "BillionMail Internet IPv4 Address: ${pool}://${ipv4_address}:${HTTPS_PORT}/${SafePath}"
        fi
    fi
    if [ "${address}" ];then
        if [ "${HTTPS_PORT}" = "443" ];then
            echo  "BillionMail Internet Address:      ${pool}://${address}/${SafePath}"
        else
            echo  "BillionMail Internet Address:      ${pool}://${address}:${HTTPS_PORT}/${SafePath}"
        fi

    fi

    if [ "${HTTPS_PORT}" = "443" ];then
        echo  "BillionMail Internal Address:      ${pool}://${LOCAL_IP}/${SafePath}"
    else
        echo  "BillionMail Internal Address:      ${pool}://${LOCAL_IP}:${HTTPS_PORT}/${SafePath}"
    fi
    
    echo -e "Username: ${ADMIN_USERNAME} \nPassword: ${ADMIN_PASSWORD}"
    echo -e "\033[33mWarning:\033[0m"
    echo -e "\033[33mIf you cannot access the BillionMail, \033[0m"
    echo -e "\033[33mrelease the following port ${SMTP_PORT}|${SMTPS_PORT}|${SUBMISSION_PORT}|${POP_PORT}|${IMAP_PORT}|${IMAPS_PORT}|${POPS_PORT}|${HTTP_PORT}|${HTTPS_PORT} in the security group\033[0m"
    echo -e "=================================================================="
}

# Modify the apply SSL interface access port
MODIFY_HTTP_SSL_PORT() { 

    NEW_PORT="$2"
    if [ -z "${NEW_PORT}" ]; then
        echo -e "\033[31m Let's Encrypt SSL certificate can only be applied using port 80, other ports cannot be applied.\033[0m"
        read -p "Please enter the new apply SSL port: " NEW_PORT
    fi

    # Verify that the input is a number
    if ! echo "${NEW_PORT}" | grep -qE '^[0-9]+$' || ((NEW_PORT < 1 || NEW_PORT > 65535)); then
        echo -e "\033[31m Error: The port number must be a number between 1-65535 \033[0m"
        exit 1
    fi

    Check_Port=$(ss -tlnp | grep -E ":(${NEW_PORT})\b")
    if [ ! -z "${Check_Port}" ]; then
        echo "${Check_Port}"
        echo -e "\033[31m Error: The ${NEW_PORT} port is already in use. \033[0m"
        exit 1
    fi

    # Perform modification
    sed -i 's/^HTTP_PORT=.*/HTTP_PORT='"${NEW_PORT}"'/' .env
    echo -e "The BillionMail apply SSL port has been modified to: ${NEW_PORT} \n Rebuild the container, please wait..."

    sleep 3
    # Find the container ID of the core image in the current project and rebuild the container
    # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Rebuilding Manage Container..."
        docker stop ${CONTAINER_ID}
        docker rm -f ${CONTAINER_ID}
        ${DOCKER_COMPOSE} up -d
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi

    if [ -f "/usr/sbin/ufw" ]; then
        /usr/sbin/ufw allow ${NEW_PORT}/tcp >/dev/null 2>&1
        /usr/sbin/ufw reload
    elif [ -f "/usr/sbin/firewall-cmd" ]; then
        /usr/sbin/firewall-cmd --permanent --zone=public --add-port=${NEW_PORT}/tcp >/dev/null 2>&1
        /usr/sbin/firewall-cmd --reload
    elif [ -f "/etc/sysconfig/iptables" ]; then
        iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${NEW_PORT} -j ACCEPT
        service iptables save
    fi

    if [[ ${NEW_PORT} != "80" ]]; then 
        echo -e "\033[31m Let's Encrypt SSL certificate can only be applied using port 80, other ports cannot be applied.\033[0m"
    fi
}

# Modify the management interface access port
MODIFY_HTTPS_PORT() { 

    NEW_PORT="$2"
    if [ -z "${NEW_PORT}" ]; then
        read -p "Please enter the new BillionMail management port: " NEW_PORT
    fi

    # Verify that the input is a number
    if ! echo "${NEW_PORT}" | grep -qE '^[0-9]+$' || ((NEW_PORT < 1 || NEW_PORT > 65535)); then
        echo -e "\033[31m Error: The port number must be a number between 1-65535 \033[0m"
        exit 1
    fi

    Check_Port=$(ss -tlnp | grep -E ":(${NEW_PORT})\b")
    if [ ! -z "${Check_Port}" ]; then
        echo "${Check_Port}"
        echo -e "\033[31m Error: The ${NEW_PORT} port is already in use. \033[0m"
        exit 1
    fi

    # Perform modification
    sed -i 's/^HTTPS_PORT=.*/HTTPS_PORT='"${NEW_PORT}"'/' .env
    if [ $? -ne 0 ]; then
        echo -e "\033[31m Error: The BillionMail management port modification failed! \033[0m"
        exit 1
    fi
    
    echo -e "The BillionMail management port has been modified to: ${NEW_PORT} \n Rebuild the container, please wait..."
    sleep 3
    # Find the container ID of the core image in the current project and rebuild the container
    # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Rebuilding Manage Container..."
        docker stop ${CONTAINER_ID}
        docker rm -f ${CONTAINER_ID}
        ${DOCKER_COMPOSE} up -d
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi

    if [ -f "/usr/sbin/ufw" ]; then
        /usr/sbin/ufw allow ${NEW_PORT}/tcp >/dev/null 2>&1
        /usr/sbin/ufw reload
    elif [ -f "/usr/sbin/firewall-cmd" ]; then
        /usr/sbin/firewall-cmd --permanent --zone=public --add-port=${NEW_PORT}/tcp >/dev/null 2>&1
        /usr/sbin/firewall-cmd --reload
    elif [ -f "/etc/sysconfig/iptables" ]; then
        iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${NEW_PORT} -j ACCEPT
        service iptables save
    fi

    # Display login information
    bash bm.sh default
}

# Modify time zone
MODIFY_TZ() { 
    if [ -a /etc/timezone ]; then
        SYSTEM_TIME_ZONE=$(cat /etc/timezone)
    elif [ -a /etc/localtime ]; then
        SYSTEM_TIME_ZONE=$(readlink /etc/localtime|sed -n 's|^.*zoneinfo/||p')
    fi
    NEW_TZ="$2"
    echo -e ""
    echo -e "See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a list of timezones"
    echo -e "Use a column named "TZ identifier" + note the column named "Notes""
    echo -e "Please enter your time zone"
    echo -e ""
    if [ -z "${SYSTEM_TIME_ZONE}" ]; then
        read -p "Timezone: " -e NEW_TZ
    else
        read -p "Timezone [${SYSTEM_TIME_ZONE}]: " -e NEW_TZ
        [ -z "${NEW_TZ}" ] && NEW_TZ=${SYSTEM_TIME_ZONE}
    fi

    if [ -z "${NEW_TZ}" ]; then
        echo -e "\033[31m Error: Please enter the time zone \033[0m"
        exit 1
    fi

    # Perform modification
    sed -i "s|^TZ=.*|TZ=${NEW_TZ}|" .env
    if [ $? -ne 0 ]; then
        echo -e "\033[31m Error: The BillionMail time zone modification failed! \033[0m"
        exit 1
    fi

    if [ -f "postgresql-data/postgresql.conf" ]; then
        cp -arpf postgresql-data/postgresql.conf  postgresql-data/postgresql.conf_${time}
        sed -i "s|^log_timezone = .*|log_timezone = \'${NEW_TZ}\'|" postgresql-data/postgresql.conf
        sed -i "s|^timezone = .*|timezone = \'${NEW_TZ}\'|" postgresql-data/postgresql.conf
    fi

    echo -e "The BillionMail time zone has been modified to: ${NEW_TZ} \n Rebuild the container, please wait..."
    sleep 3
    ${DOCKER_COMPOSE} down
    ${DOCKER_COMPOSE} up -d

}


# Modify Admin Username
MODIFY_ADMIN_USERNAME() { 

    NEW_ADMIN="$2"
    if [ -z "${NEW_ADMIN}" ]; then
        read -p "Please enter the new BillionMail administrator username (minimum 5 characters): " NEW_ADMIN
    fi
    if [ -z "${NEW_ADMIN}" ]; then
        echo -e "\033[31mError: Administrator username is required!\033[0m"
        exit 1
    fi
    if [ ${#NEW_ADMIN} -lt 5 ]; then
        echo -e "\033[31mError: Username must be at least 5 characters long!\033[0m"
        exit 1
    fi
    if ! [[ "${NEW_ADMIN}" =~ ^[a-zA-Z0-9@#_.!+-]+$ ]]; then
        echo -e "\033[31mError: Username can only contain letters, numbers, symbols: @ # _ - + . !\033[0m"
        exit 1
    fi
    # Perform modification
    sed -i "s|^ADMIN_USERNAME=.*|ADMIN_USERNAME="${NEW_ADMIN}"|" .env
    if [ $? -ne 0 ]; then
        echo -e "\033[31mError: Failed to update BillionMail administrator username!\033[0m"
        exit 1
    fi
    echo -e "BillionMail administrator username has been updated. Restarting container, please wait..."
    sleep 3
    # Find the container ID of the core image in the current project and rebuild the container
    # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Restarting Manage Container..."
        docker restart ${CONTAINER_ID}
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi
    # Display login information
    bash bm.sh default
}


# Modify Admin Password
MODIFY_ADMIN_PASSWORD() { 

    NEW_PASSWORD="$2"
    if [ -z "${NEW_PASSWORD}" ]; then
        read -p "Please enter the new BillionMail administrator password (minimum 5 characters): " NEW_PASSWORD
    fi
    if [ -z "${NEW_PASSWORD}" ]; then
        echo -e "\033[31mError: Administrator password is required!\033[0m"
        exit 1
    fi
    if [ ${#NEW_PASSWORD} -lt 5 ]; then
        echo -e "\033[31mError: Password must be at least 5 characters long!\033[0m"
        exit 1
    fi
    if ! [[ "${NEW_PASSWORD}" =~ ^[a-zA-Z0-9@#_.!+-]+$ ]]; then
        echo -e "\033[31mError: Password can only contain letters, numbers, symbols: @ # _ - + . !\033[0m"
        exit 1
    fi
    # Perform modification
    sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD="${NEW_PASSWORD}"|" .env
    if [ $? -ne 0 ]; then
        echo -e "\033[31mError: Failed to update BillionMail administrator password!\033[0m"
        exit 1
    fi
    echo -e "BillionMail administrator password has been updated. Restarting container, please wait..."
    sleep 3
    # Find the container ID of the core image in the current project and rebuild the container
    #CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Restarting Manage Container..."
        docker restart ${CONTAINER_ID}
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi
    # Display login information
    bash bm.sh default

}


# Modify Safe entrance
MODIFY_SAFE_ENTRANCE() { 

    NEW_ENTRANCE="$2"
    if [ -z "${NEW_ENTRANCE}" ]; then
        read -p "Please enter the new BillionMail security entrance path (minimum 5 characters): " NEW_ENTRANCE
    fi
    if [ -z "${NEW_ENTRANCE}" ]; then
        echo -e "\033[31mError: Security entrance path is required!\033[0m"
        exit 1
    fi
    if [ ${#NEW_ENTRANCE} -lt 5 ]; then
        echo -e "\033[31mError: Security entrance path must be at least 5 characters long!\033[0m"
        exit 1
    fi
    if ! [[ "${NEW_ENTRANCE}" =~ ^[a-zA-Z0-9]+$ ]]; then
        echo -e "\033[31mError: Security entrance path can only contain letters and numbers!\033[0m"
        exit 1
    fi
    # Perform modification
    sed -i "s|^SafePath=.*|SafePath=${NEW_ENTRANCE}|" .env
    if [ $? -ne 0 ]; then
        echo -e "\033[31mError: Failed to update BillionMail security entrance path!\033[0m"
        exit 1
    fi
    echo -e "BillionMail security entrance path has been updated. Restarting container, please wait..."
    sleep 3
    # Find the container ID of the core image in the current project and rebuild the container
    # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' )
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Restarting Manage Container..."
        docker restart ${CONTAINER_ID}
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi
    # Display login information
    bash bm.sh default
}

# Rebuild the BillionMail project
 REBUILD_PROJECT() {
    echo "Rebuilding BillionMail..."
    echo -e "\033[31mWarning: This operation will rebuild all containers, service will be unavailable during the rebuild process. \033[0m"
    read -p "Are you sure you want to continue? (yes/no): " NEW_REBUILD
    if [[ "$NEW_REBUILD" =~ ^(yes|y|Y)$ ]]; then
        sleep 3
        ${DOCKER_COMPOSE} down
        ${DOCKER_COMPOSE} up -d
        echo "Rebuild completed."
    else
        echo "Rebuild cancel."
    fi
 }

# Restart the BillionMail project
RESTART_PROJECT() {
    echo "Restarting BillionMail..."
    sleep 1
    ${DOCKER_COMPOSE} restart
}

# Stop the BillionMail project
STOP_PROJECT() {
    echo "Stop BillionMail..."
    sleep 1
    ${DOCKER_COMPOSE} stop
}

START_PROJECT() {
    echo "Start BillionMail..."
    sleep 1
    ${DOCKER_COMPOSE} up -d
}

DOWN_PROJECT() {
    echo "Stop all BillionMail services and delete all BillionMail containers..."
    sleep 3
    ${DOCKER_COMPOSE} down
}

# Restart the specified service
RESTART_SERVICE() { 
    SERVICE="$2"
    if [ -z "${SERVICE}" ]; then
        echo ""
        echo "Support input: postfix|core|dovecot|rspamd|redis|webmail|pgsql"
        read -p "Please enter service: " SERVICE
    fi

    GET_SERVICE_NAME ${SERVICE}
    echo "Restarting ${SERVICE} service..."
    sleep 3
    ${DOCKER_COMPOSE} restart ${SERVICE_NAME}
}

RESTART_SERVICE_ADMIN() { 
    SERVICE="core"
    GET_SERVICE_NAME ${SERVICE}
    echo "Restarting ${SERVICE} service..."
    sleep 3
    ${DOCKER_COMPOSE} restart ${SERVICE_NAME}
}

# Get container log 
GET_CONTAINER_LOG() { 
    CONTAINER="$2"
    if [ -z "${CONTAINER}" ]; then
        echo ""
        echo "Support input: postfix|core|dovecot|rspamd|redis|webmail|pgsql"
        read -p "Please enter container: " CONTAINER
    fi

    log_num="$3"
    follow=""

    if [ "$log_num" == "-f" ]; then
        follow="-f"
        log_num="$4"
    elif [ "$4" == "-f" ]; then
        follow="-f"
    fi

    if [[ "$log_num" =~ ^[0-9]+$ ]]; then
        log_num="${log_num}"
    else
        log_num=25
    fi

    GET_CONTAINER_ID ${CONTAINER}
    if [ -z "${CONTAINER_ID}" ]; then
        Red_Error "ERROR: The "${CONTAINER}" container does not exist, Please execute '${DOCKER_COMPOSE} up -d' to start BillionMail"
    fi
    docker logs ${follow} --tail="${log_num}" ${CONTAINER_ID}
}

# Get file log
GET_FILE_LOG() {
    NAME_FILE="$2"
    
    if [ -z "${NAME_FILE}" ]; then
        echo ""
        echo "Support input: postfix|core|core-access|core-error|dovecot|rspamd|fail2ban"
        read -p "Please enter name: " NAME_FILE
    fi

    log_num="$3"
    follow=""

    if [ "$log_num" == "-f" ]; then
        follow="-f"
        log_num="$4"
    elif [ "$4" == "-f" ]; then
        follow="-f"
    fi

    if [[ "$log_num" =~ ^[0-9]+$ ]]; then
        log_num="${log_num}"
    else
        log_num=25
    fi

    # Define log files
    declare -A LOG_FILES=(
        ["core"]="$(date +%Y-%m-%d).log"
        ["core-access"]="access-$(date +%Y%m%d).log"
        ["core-error"]="error-$(date +%Y%m%d).log"
        ["postfix"]="mail.log"
        ["dovecot"]="mail.log"
        ["rspamd"]="rspamd.log"
        ["fail2ban"]="fail2ban.log"
    )

    # Define log directory mapping 
    declare -A LOG_DIRS=(
        ["core"]="core"
        ["core-access"]="core"
        ["core-error"]="core"
        ["postfix"]="postfix"
        ["dovecot"]="dovecot"
        ["rspamd"]="rspamd"
        ["fail2ban"]="fail2ban"
    )

    # Check whether the service name is valid
    if [[ ! " ${!LOG_FILES[@]} " =~ " ${NAME_FILE} " ]]; then
        echo "How to use: $0 log_file core 100"
        Red_Error "Error: Invalid name. Please enter name: (postfix|core|core-access|core-error|dovecot|rspamd|fail2ban) "
    fi

    # Get log file name and directory
    LOG_FILE="${LOG_FILES[$NAME_FILE]}"
    LOG_DIR="${LOG_DIRS[$NAME_FILE]}"
    
    # Check and display log files
    if [ -f "logs/${LOG_DIR}/${LOG_FILE}" ]; then
        tail ${follow} -n ${log_num} "logs/${LOG_DIR}/${LOG_FILE}"
    elif [ -f "data/logs/${LOG_DIR}/${LOG_FILE}" ]; then
        tail ${follow} -n ${log_num} "data/logs/${LOG_DIR}/${LOG_FILE}"
    else
        Red_Error "logs file does not exist!"
    fi
}

# Get service TOP 
GET_SERVICE_TOP() {

    SERVICE="$2"
    if [ -z "${SERVICE}" ]; then
        read -p "Please enter service: " SERVICE
    fi

    GET_SERVICE_NAME ${SERVICE}
    ${DOCKER_COMPOSE} top ${SERVICE_NAME}
}

GET_SERVICE_TOP_ALL() {

    ${DOCKER_COMPOSE} top

}

GET_SERVICE_PS() {

    ${DOCKER_COMPOSE} ps

}

# Get containers status
GET_CONTAINER_STATUS() {
    CONTAINERS=("core" "postfix" "dovecot" "rspamd" "pgsql" "redis" "webmail")
        
    for cc in "${CONTAINERS[@]}"; do
        
        CONTAINER="${cc}"
        GET_CONTAINER_ID "${CONTAINER}"
                
        status=$(docker inspect --format='{{.State.Status}}' "${CONTAINER_ID}" 2>/dev/null)
        if [ "$status" = "running" ]; then
            echo -e "\033[32m ${CONTAINER} container is running\033[0m"
        else
            echo -e "\033[31m ${CONTAINER} container is not running. Status: ${status} \033[0m"
        fi
        
    done
}

CLEAR_OLD_IMAGE() {
    # Remove only outdated versions of images defined in docker-compose.yml

    # Check if docker-compose.yml exists
    if [ ! -f "docker-compose.yml" ]; then
        Red_Error "Error: docker-compose.yml file not found"      
    fi

    # Extract complete image names (including tags) defined in compose file
    # echo "Extracting billionmail images from docker-compose.yml..."
    # COMPOSE_IMAGES=$(grep -oP 'image:\s*\K[^"\s]+' docker-compose.yml | sort -u)
    COMPOSE_IMAGES=$(grep -oP 'image:\s*\K(?:billionmail|ghcr\.io/aapanel)[^"\s]+' docker-compose.yml | sort -u)

    # Check if any images were found
    if [ -z "${COMPOSE_IMAGES}" ]; then
        echo "Warning: No image definitions found in docker-compose.yml"
        exit 1
    fi

    # Get all local Docker images (excluding <none> images)
    # echo "Getting all local Docker images..."
    ALL_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>:<none>")

    # Find outdated images to remove
    IMAGES_TO_REMOVE=""

    while IFS= read -r compose_image; do
        # Extract image name (without tag)
        image_name=$(echo "${compose_image}" | awk -F: '{print $1}')
        
        # Extract tag defined in compose file
        compose_tag=$(echo "${compose_image}" | awk -F: '{print $2}')
        
        # Find all local images with the same name
        local_images=$(echo "${ALL_IMAGES}" | grep "^${image_name}:")
        
        # Filter images with different tags
        for local_image in ${local_images}; do
            local_tag=$(echo "${local_image}" | awk -F: '{print $2}')
            
            # If local tag differs from compose tag, add to removal list
            if [ "${local_tag}" != "${compose_tag}" ]; then
                IMAGES_TO_REMOVE="${IMAGES_TO_REMOVE} ${local_image}"
            fi
        done
    done <<< "${COMPOSE_IMAGES}"

    # Display results
    if [ -z "${IMAGES_TO_REMOVE}" ]; then
        echo ""
        echo "No outdated images found to remove"
        exit 0
    fi

    echo -e "\n--- Outdated images to remove ---"
    echo "${IMAGES_TO_REMOVE}" | tr ' ' '\n' | grep -v '^$'
    IMAGE_COUNT=$(echo "${IMAGES_TO_REMOVE}" | wc -w)
    echo -e "\nTotal: ${IMAGE_COUNT} outdated images to remove"

    # Ask for confirmation
    read -p "Remove these outdated images? (y/n): " confirm
    if [ "${confirm}" = "y" ] || [ "${confirm}" = "Y" ]; then
        sleep 5
        for image in ${IMAGES_TO_REMOVE}; do
            echo "Removing image: ${image}"
            docker rmi "${image}" || echo "Warning: Failed to remove ${image}"
        done
        echo "Outdated images removed successfully"
        echo -e "For deeper Docker system cleanup, ensure all containers to keep are running before executing:\n docker system prune"
    else
        echo "Operation cancelled"
    fi
}

# Turn off IP access whitelist restrictions
CANCEL_IP_WHITELIST_LIMIT() { 

    # Perform modification
    sed -i 's/^IP_WHITELIST_ENABLE=.*/IP_WHITELIST_ENABLE=false/' .env
    
    echo -e "The BillionMail IP access whitelist Restrictions: Closed \n Restart the container, please wait..."
    sleep 3
    CONTAINER="core"
    GET_CONTAINER_ID ${CONTAINER}
    if [ "${CONTAINER_ID}" ]; then
        echo "Rebuilding Manage Container..."
        docker stop ${CONTAINER_ID}
        docker rm -f ${CONTAINER_ID}
        ${DOCKER_COMPOSE} up -d
    else
        echo "The "core" container does not exist"
        echo "Starting BillionMail..."
        ${DOCKER_COMPOSE} up -d
    fi
}


APPLY_MULTI_IP() {
    echo "🚀 Starting multi-IP configuration application..."

    # Initialize_Project

    echo "📁 Current directory: $PWD_d"
    PROJECT_DIR="$PWD_d"

    # Temporary backup path (under project root)
    BACKUP_COMPOSE="docker-compose.yml.bak.$(date +%Y%m%d-%H%M%S)"

    # 🔐 Load .env file
    ENV_FILE="./.env"
    if [ -f "$ENV_FILE" ]; then
        echo "🔐 Loading environment variables: $ENV_FILE"
        source "$ENV_FILE"
        if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then
            Red_Error "❌ .env configuration error, database settings are empty"
        fi
    else
        Red_Error "❌ .env file not found: $ENV_FILE, please ensure configuration exists"
    fi

    # Database configuration
    DB_HOST="127.0.0.1"
    DB_PORT="25432"
    DB_NAME="${DBNAME}"
    DB_USER="${DBUSER}"
    DB_PASS="${DBPASS}"

    # Record initial state for rollback
    ORIGINAL_COMPOSE="docker-compose.yml"
    ADDNETWORK_COMPOSE="docker-compose_addnetwork.yml"

    # Create temporary file
    TEMP_FILE=$(mktemp) || { Red_Error "❌ Unable to create temporary file"; }

    # ============ Cleanup and rollback functions ============
    cleanup_apply() {
        if [[ -n "$TEMP_FILE" && -f "$TEMP_FILE" ]]; then
            rm -f "$TEMP_FILE"
        fi
    }

    rollback_compose() {
        echo "⚠️ Error detected, rolling back docker-compose.yml..."
        if [[ -f "$BACKUP_COMPOSE" ]]; then
            cp "$BACKUP_COMPOSE" "$ORIGINAL_COMPOSE"
            echo "✅ Successfully rolled back to original configuration"
        else
            echo "❌ Backup file $BACKUP_COMPOSE does not exist, unable to rollback!"
        fi
    }

    # Set trap to handle exceptions and cleanup
    trap 'echo "💥 Script execution failed, triggering rollback"; rollback_compose; cleanup_apply; exit 1' ERR
    trap 'cleanup_apply' EXIT

    # ============ 1. Check if new compose file exists ============
    echo "🔍 Checking if docker-compose_addnetwork.yml exists..."
    if [ ! -f "${ADDNETWORK_COMPOSE}" ]; then
        Red_Error "❌ Error: ${ADDNETWORK_COMPOSE} file does not exist, please verify"
    fi

    # ============ 2. Backup and replace docker-compose.yml ============
    echo "🔄 Backing up and replacing docker-compose.yml"
    if [[ -f "$ORIGINAL_COMPOSE" ]]; then
        cp "$ORIGINAL_COMPOSE" "$BACKUP_COMPOSE"
        echo "✅ Backup created as: $BACKUP_COMPOSE"
    fi

    # Use cp instead of mv to keep original file for repeated execution
    cp "$ADDNETWORK_COMPOSE" "$ORIGINAL_COMPOSE"
    echo "✅ docker-compose.yml has been updated"

    # ============ 3. Insert domain → smtp_name mapping into bm_domain_smtp_transport ============
    echo "🔄 Reading data from bm_multi_ip_domain and inserting into bm_domain_smtp_transport..."

    # Query database for domain and smtp_server_name using secure query method
    if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A -F'|' \
        -c "SELECT domain, smtp_server_name FROM bm_multi_ip_domain WHERE active = 1;" > "$TEMP_FILE"; then
        Red_Error "❌ Database query failed, please check connection or table structure"
    fi

    # Check if temporary file has content
    if [[ ! -s "$TEMP_FILE" ]]; then
        echo "⚠️ No active multi-IP domain configurations found, skipping sender rule setup"
    else
        echo "📋 Found $(wc -l < "$TEMP_FILE") configuration records"

        # Safely process query results
        while IFS='|' read -r domain smtp_name; do
            # Clean whitespace and validate
            domain=$(echo "$domain" | xargs)
            smtp_name=$(echo "$smtp_name" | xargs)

            # Skip empty lines or invalid data
            [[ -z "$domain" || -z "$smtp_name" ]] && continue

            # Validate domain format (basic security check)
            if [[ ! "$domain" =~ ^[a-zA-Z0-9.-]+$ ]]; then
                echo "⚠️ Skipping invalid domain format: $domain"
                continue
            fi

            atype="dedicated_ip"
            domain_with_at="@${domain}"

            echo "🔍 Checking if sender rule exists for domain: $domain_with_at..."

            # Use secure SQL query
            escaped_domain=$(printf '%s\n' "$domain_with_at" | sed "s/'/''/g")
            EXISTS=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
                -c "SELECT 1 FROM bm_domain_smtp_transport WHERE domain = '$escaped_domain';")

            # If exists, delete it
            if [[ -n "$EXISTS" && "$EXISTS" != "" ]]; then
                echo "🟡 Sender rule already exists for domain: $domain_with_at, deleting..."
                if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \
                    -c "DELETE FROM bm_domain_smtp_transport WHERE domain = '$escaped_domain';"; then
                    Red_Error "❌ Failed to delete old record: $domain_with_at"
                fi
                echo "✅ Old rule deleted: $domain_with_at"
            fi

            # Execute insertion (using secure string escaping)
            echo "📝 Inserting: $domain_with_at → $smtp_name"
            escaped_smtp=$(printf '%s\n' "$smtp_name" | sed "s/'/''/g")
            escaped_atype=$(printf '%s\n' "$atype" | sed "s/'/''/g")
            if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \
                -c "INSERT INTO bm_domain_smtp_transport (atype, domain, smtp_name) VALUES ('$escaped_atype', '$escaped_domain', '$escaped_smtp');"; then
                Red_Error "❌ Insertion failed: $domain_with_at"
            fi

            echo "✅ Successfully inserted: $domain_with_at → $smtp_name"

        done < "$TEMP_FILE"

        echo "📝 All domain mappings have been successfully written to bm_domain_smtp_transport"
    fi

    # Update status of all records in bm_multi_ip_domain table to 'applied'
      echo "🔄 Resetting multi-IP domain status to 'applied'..."

      # First, query how many records need updating
      CURRENT_DOMAIN_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
          -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status != 'applied';" 2>/dev/null || echo "0")

      echo "📊 Current number of domains needing status reset: ${CURRENT_DOMAIN_COUNT}"

      if [[ "${CURRENT_DOMAIN_COUNT}" -gt 0 ]]; then
          # Execute update operation
          if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \
              -c "UPDATE bm_multi_ip_domain SET status = 'applied';" >/dev/null 2>&1; then
              echo "✅ Successfully reset ${CURRENT_DOMAIN_COUNT} multi-IP domain statuses to 'applied'"
          else
              echo "❌ Failed to reset multi-IP domain statuses"
              exit 1
          fi
      else
          echo "✅ All multi-IP domain statuses are already 'applied'"
      fi

    # ============ 4. Restart services ============
    echo "🔄 Restarting BillionMail services"
    if ! ${DOCKER_COMPOSE} down; then
        Red_Error "❌ docker-compose down failed"
    fi

    if ! ${DOCKER_COMPOSE} up -d; then
        Red_Error "❌ docker-compose up -d failed: docker-compose.yml has errors, please check"
    fi

    # Wait for services to start and check health status
    echo "🔄 Waiting for services to start..."
    sleep 5

    echo "📊 Checking service status..."
    ${DOCKER_COMPOSE} ps

    # Check if critical services are running properly
    CORE_STATUS=$(${DOCKER_COMPOSE} ps core-billionmail --format "{{.State}}" 2>/dev/null || echo "missing")
    POSTFIX_STATUS=$(${DOCKER_COMPOSE} ps postfix-billionmail --format "{{.State}}" 2>/dev/null || echo "missing")

    if [[ "$CORE_STATUS" != "running" || "$POSTFIX_STATUS" != "running" ]]; then
        echo "⚠️ Warning: Some critical services may not have started properly"
        echo "   Core status: $CORE_STATUS"
        echo "   Postfix status: $POSTFIX_STATUS"
        echo "💡 Please check 'docker compose logs' for details"
    fi

    # ============ 5. Execution Summary ============
    echo ""
    echo "🎉 =============== Execution Summary ==============="
    echo "✅ All operations completed! Services have been successfully restarted"
    echo "📁 Original configuration backed up as: $BACKUP_COMPOSE"
    echo "🔄 New configuration applied: $ORIGINAL_COMPOSE"
    echo "📋 Source configuration file preserved: $ADDNETWORK_COMPOSE"
    echo "🕐 Completion time: $(date '+%Y-%m-%d %H:%M:%S')"

    # Optional: Clean up old backups (keep latest 3)
    OLD_BACKUPS=$(find . -name 'docker-compose.yml.bak.*' -type f | wc -l)
    if [[ $OLD_BACKUPS -gt 3 ]]; then
        echo "🧹 Cleaning up old backup files (keeping latest 3)..."
        find . -name 'docker-compose.yml.bak.*' -type f | sort -r | tail -n +4 | xargs rm -f 2>/dev/null || true
    fi

    echo "========================================="

    # Successfully completed, remove ERR trap
    trap - ERR
}


FIX_MULTI_IP() {
    echo "🔧 Starting multi-IP configuration fix..."

    # ============ Initialization: Automatically locate project root directory ============
    # Initialize_Project

    # Load environment variables
    if [ ! -f ".env" ]; then
        Red_Error "❌ .env file not found, please ensure execution from project root directory"
    fi

    source .env
    if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then
        Red_Error "❌ .env configuration error, database settings are empty"
    fi

    echo "📁 Project directory: ${PWD_d}"

    # ============ 1. Find the latest backup file ============
    echo "🔍 Searching for the latest docker-compose.yml backup file..."
    LATEST_BACKUP=$(find . -name 'docker-compose.yml.bak.*' -type f | sort -r | head -n 1)

    if [ -z "${LATEST_BACKUP}" ]; then
        echo "⚠️ No docker-compose.yml backup file found"
        echo "📋 List of available backup files:"
        find . -name 'docker-compose.yml.bak.*' -type f 2>/dev/null || echo "   No backup files"

        read -p "Proceed with database cleanup operation? (y/n): " continue_cleanup
        if [[ "$continue_cleanup" != "y" && "$continue_cleanup" != "Y" ]]; then
            echo "❌ Operation cancelled"
            exit 1
        fi
    else
        echo "✅ Found latest backup: ${LATEST_BACKUP}"
        echo "🔄 Restoring docker-compose.yml..."

        # Create a backup of current file (in case rollback fails)
        cp docker-compose.yml "docker-compose.yml.before-fix.$(date +%Y%m%d-%H%M%S)" 2>/dev/null || true

        # Restore from backup
        if cp "${LATEST_BACKUP}" docker-compose.yml; then
            echo "✅ docker-compose.yml has been restored"
        else
            Red_Error "❌ Failed to restore docker-compose.yml"
        fi
    fi

    # ============ 2. Clean up multi-IP configurations in database ============
    echo "🗄️ Starting database configuration cleanup..."

    # Check database connection (using container method)
    echo "🔗 Testing database connection..."
    if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT 1;" >/dev/null 2>&1; then
        Red_Error "❌ Database connection failed, please check if service is running properly"
    fi
    echo "✅ Database connection is normal"

    # Delete records with atype="dedicated_ip" from bm_domain_smtp_transport table
    echo "🗑️ Deleting dedicated IP records from bm_domain_smtp_transport table..."

    # First, query the current number of records
    CURRENT_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "0")

    echo "📊 Current number of dedicated IP sender rules: ${CURRENT_SMTP_COUNT}"

    if [[ "${CURRENT_SMTP_COUNT}" -gt 0 ]]; then
        # Execute deletion
        if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \
            -c "DELETE FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" >/dev/null 2>&1; then
            echo "✅ Deleted ${CURRENT_SMTP_COUNT} dedicated IP sender rules"
        else
            echo "❌ Failed to delete dedicated IP sender rules"
            exit 1
        fi
    else
        echo "✅ No dedicated IP sender rules found to delete"
    fi

    # Update status of all records in bm_multi_ip_domain table to 'pending'
    echo "🔄 Resetting multi-IP domain status to 'pending'..."

    # First, query the current number of records needing update
    CURRENT_DOMAIN_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status != 'pending';" 2>/dev/null || echo "0")

    echo "📊 Current number of domains needing status reset: ${CURRENT_DOMAIN_COUNT}"

    if [[ "${CURRENT_DOMAIN_COUNT}" -gt 0 ]]; then
        # Execute update operation
        if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \
            -c "UPDATE bm_multi_ip_domain SET status = 'pending';" >/dev/null 2>&1; then
            echo "✅ Reset ${CURRENT_DOMAIN_COUNT} multi-IP domain statuses to 'pending'"
        else
            echo "❌ Failed to reset multi-IP domain statuses"
            exit 1
        fi
    else
        echo "✅ All multi-IP domain statuses are already 'pending'"
    fi

    # ============ 3. Verify cleanup results ============
    echo "🔍 Verifying cleanup results..."

    # Verify dedicated IP sender rules have been cleaned
    REMAINING_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "unknown")

    # Verify multi-IP domain statuses have been reset
    REMAINING_APPLIED_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status = 'applied';" 2>/dev/null || echo "unknown")

    echo "📊 Verification results:"
    echo "   Remaining dedicated IP sender rules: ${REMAINING_SMTP_COUNT}"
    echo "   Remaining applied status domains: ${REMAINING_APPLIED_COUNT}"

    # ============ 4. Restart services ============
    echo "🔄 Restarting BillionMail services..."

    echo "🛑 Stopping services..."
    if ! ${DOCKER_COMPOSE} down; then
        echo "⚠️ Warning during service stop, continuing execution..."
    fi

    echo "🚀 Starting services..."
    if ! ${DOCKER_COMPOSE} up -d; then
        Red_Error "❌ Failed to start services, please check docker-compose.yml configuration"
    fi

    # Wait for services to start
    echo "⏳ Waiting for services to start..."
    sleep 5

    # Check service status
    echo "📊 Checking service status..."
    ${DOCKER_COMPOSE} ps

    # ============ 5. Final verification ============
    echo "🔍 Final verification of database status..."
    sleep 2

    # Final verification of dedicated IP sender rules
    FINAL_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "unknown")

    # Final verification of multi-IP domain statuses
    FINAL_APPLIED_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \
        -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status = 'applied';" 2>/dev/null || echo "unknown")

    # ============ 6. Execution Summary ============
    echo ""
    echo "🎉 =============== Fix Summary ==============="
    echo "✅ Multi-IP configuration fix completed!"
    if [ -n "${LATEST_BACKUP}" ]; then
        echo "📁 Restored backup: ${LATEST_BACKUP} → docker-compose.yml"
    fi
    echo "🗑️ Dedicated IP sender rule cleanup result: ${CURRENT_SMTP_COUNT} → ${FINAL_SMTP_COUNT}"
    echo "🔄 applied status domain reset result: ${CURRENT_DOMAIN_COUNT} → ${FINAL_APPLIED_COUNT}"
    echo "🚀 Services restarted"
    echo "🕐 Completion time: $(date '+%Y-%m-%d %H:%M:%S')"
    echo "========================================="

    # Provide detailed verification information
    if [[ "${FINAL_SMTP_COUNT}" == "0" && "${FINAL_APPLIED_COUNT}" == "0" ]]; then
        echo ""
        echo "🎉 Congratulations! Database cleanup completed successfully:"
        echo "   ✅ All dedicated IP sender rules have been deleted"
        echo "   ✅ All multi-IP domain statuses have been reset to pending"
    else
        echo ""
        echo "⚠️ Note: Some data may not have been fully cleaned:"
        if [[ "${FINAL_SMTP_COUNT}" != "0" ]]; then
            echo "   ❌ Still ${FINAL_SMTP_COUNT} dedicated IP sender rules remaining"
        fi
        if [[ "${FINAL_APPLIED_COUNT}" != "0" ]]; then
            echo "   ❌ Still ${FINAL_APPLIED_COUNT} domains with applied status"
        fi
        echo "   💡 Suggest manually checking the database or re-running the fix command"
    fi

    echo ""
    echo "💡 Tips:"
    echo "   1. To reconfigure multi-IP, please re-run: bm.sh multi_ip"
    echo "   2. Suggest checking email sending functionality"
    echo "   3. If issues occur, check container logs: bm.sh l-c core"
}



SHOW_HELP() {
        echo "Help Information:"
        echo "  default                   - Show BillionMail login default info: $0 default"
        echo "  update                    - Update BillionMail: $0 update"
        echo "  change-port               - Modify BillionMail access management port: $0 change-port"
        echo "  change-tz                 - Modify BillionMail time zone: $0 change-tz"
        echo "  change-user               - Modify BillionMail Administrator user: $0 change-user"
        echo "  change-password           - Modify BillionMail Administrator password: $0 change-password"
        echo "  change-safe-path          - Modify BillionMail security entrance path: $0 change-safe-path"
        echo "  change-apply-ssl-port     - Modify BillionMail apply ssl port: $0 change-apply-ssl-port"
        echo "  start                     - Start BillionMail: $0 start"
        echo "  stop                      - Stop BillionMail: $0 stop"
        echo "  restart                   - Restart BillionMail: $0 restart"
        echo "  status                    - Show BillionMail containers running status : $0 status"
        echo "  down                      - Stop and remove containers, networks: $0 down"
        echo "  rebuild                   - Rebuild all BillionMail containers: $0 rebuild"
        echo "  top                       - Show all BillionMail processes: $0 top"
        echo "  ps                        - Show all BillionMail containers: $0 ps"
        echo "  service-top               - Show processes of a specific BillionMail service: $0 s-t postfix"
        echo "  log-file <service>            - View logs of a specific service: $0 l-f postfix"
        echo "  log-container <container>     - View logs of a specific container: $0 l-c postfix"
        echo "  restart-service <service>     - Restart a specific service and its container: $0 r-s postfix"
        echo "  clear                     - Clear BillionMail old images: $0 clear"
        echo "  cancel-ip-limit           - Cancel IP access limit : $0 c-i-l"
        # echo "  add-domain <domain>       - Add domain. Example: $0 add-domain example.com"
        # echo "  del-domain <domain>       - Delete domain. Example: $0 del-domain example.com"
        # echo "  add-email <email>         - Add email. Example: $0 add-email user@example.com"
        # echo "  del-email <email>         - Delete email. Example: $0 del-email user@example.com"
        echo "  show-domain               - Show domain data."
        echo "  show-email                - Show email data."
        # echo "  show-record               - Show domain DNS record."
        
        exit
}


case "$1" in
    h | -h | help | --help | -help)
        SHOW_HELP
        ;;
    default | info)
        Default_info
        ;;
    update)
        Update_BillionMail
        ;;
    add-domain)
        Init_Domain "$@"
        Add_Domain
        ;;
    del-domain)
        Init_Domain "$@"
        Del_Domain
        ;;
    add-email)
        Init_Email "$@"
        Add_Email
        ;;
    del-email)
        Init_Email "$@"
        Del_Email
        ;;
    show-domain)
        Select_Domain_data
        ;;
    show-email)
        Select_Email_data
        ;;
    show-record|show_dns_record)
        Init_Domain "$@"
        Domain_record
        ;;
    MODIFY_HTTP_PORT|modify_http_port|change-apply-ssl-port)
        MODIFY_HTTP_SSL_PORT
        ;;
    MODIFY_HTTPS_PORT|modify_https_port|change-port)
        MODIFY_HTTPS_PORT
        ;;
    MODIFY_TZ|modify_tz|change-tz)
        MODIFY_TZ
        ;;
    MODIFY_ADMIN_USERNAME|modify_user|change-user)
        MODIFY_ADMIN_USERNAME
        ;;
    MODIFY_ADMIN_PASSWORD|modify_password|change-password)
        MODIFY_ADMIN_PASSWORD
        ;;
    MODIFY_SAFE_ENTRANCE|modify_safe_path|change-safe-path)
        MODIFY_SAFE_ENTRANCE
        ;;
    REBUILD_PROJECT|rebuild_project|rebuild)
        REBUILD_PROJECT
        ;;
    RESTART_PROJECT|restart_project|restart)
        RESTART_PROJECT
        ;;
    START_PROJECT|start_project|start)
        START_PROJECT
        ;;
    STOP_PROJECT|stop_project|stop)
        STOP_PROJECT
        ;;
    GET_CONTAINER_STATUS|get_container_status|status)
        GET_CONTAINER_STATUS
        ;;
    DOWN_PROJECT|down_project|down)
        DOWN_PROJECT
        ;;
    RESTART_SERVICE|restart_service|restart-service|r-s)
        RESTART_SERVICE "$@"
        ;;
    GET_CONTAINER_LOG|log_container|log-container|l-c)
        GET_CONTAINER_LOG "$@"
        ;;
    GET_FILE_LOG|log_file|log-file|l-f)
        GET_FILE_LOG "$@"
        ;;
    GET_SERVICE_TOP|service_top|service-top|s-t)
        GET_SERVICE_TOP "$@"
        ;;
    GET_SERVICE_TOP_ALL|service_top_all|service-top-all|top)
        GET_SERVICE_TOP_ALL
        ;;    
    GET_SERVICE_PS|service-ps|ps)
        GET_SERVICE_PS
        ;;  
    RESTART_SERVICE_ADMIN|restart_service_admin|restart-service-admin|restart-admin)
        RESTART_SERVICE_ADMIN
        ;;
    clear)
    CLEAR_OLD_IMAGE
    ;;    
    cancel-ip-limit|c-i-l)
    CANCEL_IP_WHITELIST_LIMIT
    ;;  


    Domain_multi_send_IP|apply_multi_ip|multi_ip)
        APPLY_MULTI_IP
        ;;

    fix_multi_ip|fix-multi-ip)
        FIX_MULTI_IP
        ;;


    *)
        echo "=============== BillionMail CLI =================="
        echo "1) Restart BillionMail          2) View login info"
        echo ""
        echo "3) View running status          4) Stop BillionMail"
        echo ""
        echo "5) Start BillionMail            6) Restart manage only (mail unaffected)"
        echo ""
        echo "7) Change manage password       8) Change manage username"
        echo ""
        echo "9) Change secure entry          10) Change manage access port"
        echo ""
        echo "11) View all processes          12) Update BillionMail"
        echo ""
        echo "13) Cancel IP access limit          "
        echo ""
        echo "0) Exit      For more commands: help"
        echo "=================================================="
        read -p "Enter number to execute command [0-12]: " input  
        case ${input} in
        1)
            RESTART_PROJECT
            ;;
        2)
            Default_info
            ;;
        3)
            GET_CONTAINER_STATUS
            ;;
        4)
            STOP_PROJECT
            ;;
        5)
            START_PROJECT
            ;;
        6)
            RESTART_SERVICE_ADMIN
            ;;
        7)
            MODIFY_ADMIN_PASSWORD
            ;;
        8)
            MODIFY_ADMIN_USERNAME
            ;;
        9)
            MODIFY_SAFE_ENTRANCE
            ;;
        10)
            MODIFY_HTTPS_PORT
            ;;
        11)
            GET_SERVICE_TOP_ALL
            ;;
        12)
            Update_BillionMail
            ;;
        13)
            CANCEL_IP_WHITELIST_LIMIT
            ;;            
        help)
            SHOW_HELP
            ;;
        0 | * )
            echo "Cancelled!"
            exit
            ;;
        esac
        ;;
esac