#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH

: '
Copyright (C) 2022 - now  HaoZi Technology Co., Ltd.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
'

source <(curl -f -s --connect-timeout 10 --retry 3 https://dl.cdn.haozi.net/panel/public.sh)
if [ $? -ne 0 ]; then
    echo "下载 public.sh 失败,请检查网络或稍后重试。"
    echo "Download public.sh failed, please check the network or try again later."
    exit 1
fi

LOGO="+----------------------------------------------------\n| 耗子面板安装脚本\n| Rat Panel install script\n+----------------------------------------------------\n| Copyright © 2022-"$(date +%Y)" 耗子科技 All rights reserved.\n+----------------------------------------------------"
current_path=$(pwd)
ssh_port=$(cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}')
in_china=$(curl --retry 2 -m 10 -L https://www.qualcomm.cn/cdn-cgi/trace 2>/dev/null | grep -qx 'loc=CN' && echo "true" || echo "false")

Prepare_System() {
    if [ $(whoami) != "root" ]; then
        error "请使用root用户运行安装命令(Please run the installation command using the root user)"
    fi

    if [ ${OS} == "unknown" ]; then
        error "系统不支持安装面板(The system does not support installing the panel)"
    fi
    if [ ${ARCH} != "x86_64" ] && [ ${ARCH} != "aarch64" ]; then
        error "系统架构不支持安装面板(The system architecture does not support installing the panel)"
    fi

    if [ ${ARCH} == "x86_64" ]; then
        if [ "$(cat /proc/cpuinfo | grep -c ssse3)" -lt "1" ]; then
            error "CPU至少需支持x86-64-v2指令集(CPU must support at least the x86-64-v2 instruction set)"
        fi
    fi

    kernel_version=$(uname -r | awk -F '.' '{print $1}')
    if [ "${kernel_version}" -lt "4" ]; then
        error "系统内核版本太低,请升级到4.x以上版本(The system kernel version is too low, please upgrade to version 4.x or above)"
    fi

    is_64bit=$(getconf LONG_BIT)
    if [ "${is_64bit}" != '64' ]; then
        error "请更换64位系统安装面板(Please switch to a 64-bit system to install the panel)"
    fi

    if [ -f "${setup_path}/panel/web" ]; then
        error "面板已安装,无需重复安装(Panel is already installed, no need to install again)"
    fi

    if ! id -u "www" >/dev/null 2>&1; then
        groupadd www
        useradd -s /sbin/nologin -g www www
    fi

    if [ ! -d ${setup_path} ]; then
        mkdir ${setup_path}
    fi

    timedatectl set-timezone Asia/Shanghai

    [ -s /etc/selinux/config ] && sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
    setenforce 0 >/dev/null 2>&1

    ulimit -n 1048576
    echo 2147483584 >/proc/sys/fs/file-max
    soft_nofile_check=$(cat /etc/security/limits.conf | grep '^* soft nofile .*$')
    hard_nofile_check=$(cat /etc/security/limits.conf | grep '^* hard nofile .*$')
    soft_nproc_check=$(cat /etc/security/limits.conf | grep '^* soft nproc .*$')
    hard_nproc_check=$(cat /etc/security/limits.conf | grep '^* hard nproc .*$')
    fs_file_max_check=$(cat /etc/sysctl.conf | grep '^fs.file-max.*$')
    if [ "${soft_nofile_check}" == "" ]; then
        echo "* soft nofile 1048576" >>/etc/security/limits.conf
    fi
    if [ "${hard_nofile_check}" == "" ]; then
        echo "* hard nofile 1048576" >>/etc/security/limits.conf
    fi
    if [ "${soft_nproc_check}" == "" ]; then
        echo "* soft nproc 1048576" >>/etc/security/limits.conf
    fi
    if [ "${hard_nproc_check}" == "" ]; then
        echo "* hard nproc 1048576" >>/etc/security/limits.conf
    fi
    if [ "${fs_file_max_check}" == "" ]; then
        echo fs.file-max = 2147483584 >>/etc/sysctl.conf
    fi

    # 自动开启 BBR
    bbr_support_check=$(ls -l /lib/modules/*/kernel/net/ipv4 | grep -c tcp_bbr)
    bbr_open_check=$(sysctl net.ipv4.tcp_congestion_control | grep -c bbr)
    if [ "${bbr_support_check}" != "0" ] && [ "${bbr_open_check}" == "0" ]; then
        qdisc=$(sysctl net.core.default_qdisc | awk '{print $3}')
        echo "net.core.default_qdisc=${qdisc}" >>/etc/sysctl.conf
        echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.conf
        sysctl -p
    fi

    if [ ${OS} == "rhel" ]; then
        if ${in_china}; then
            sed -e 's|^mirrorlist=|#mirrorlist=|g' \
                -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.tencent.com/rocky|g' \
                -e 's|^# baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.tencent.com/rocky|g' \
                -i.bak \
                /etc/yum.repos.d/[Rr]ocky*.repo >/dev/null 2>&1
            sed -e 's|^mirrorlist=|#mirrorlist=|g' \
                -e 's|^#baseurl=https://repo.almalinux.org|baseurl=https://mirrors.tencent.com|g' \
                -e 's|^# baseurl=https://repo.almalinux.org|baseurl=https://mirrors.tencent.com|g' \
                -i.bak \
                /etc/yum.repos.d/[Aa]lmalinux*.repo >/dev/null 2>&1
            sed -e 's|^mirrorlist=|#mirrorlist=|g' \
                -e 's|^#baseurl=http://mirror.centos.org/$contentdir|baseurl=https://mirrors.tencent.com/centos-stream|g' \
                -e 's|^# baseurl=http://mirror.centos.org/$contentdir|baseurl=https://mirrors.tencent.com/centos-stream|g' \
                -i.bak \
                /etc/yum.repos.d/[Cc]ent*.repo >/dev/null 2>&1
        fi
        dnf makecache -y
        dnf install dnf-plugins-core -y
        dnf config-manager --set-enabled epel
        if ${in_china}; then
            sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.tencent.com|' /etc/yum.repos.d/epel* >/dev/null 2>&1
            sed -i 's|^# baseurl=https://download.example/pub|baseurl=https://mirrors.tencent.com|' /etc/yum.repos.d/epel* >/dev/null 2>&1
            sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel* >/dev/null 2>&1
            dnf makecache -y
        fi
        # EL 9
        dnf config-manager --set-enabled crb
        dnf install epel-release epel-next-release -y
        # 部分系统可能没有这两个包,需要手动安装
        # 对于 openEuler 这种大改的系统,下载会 404,这没有影响
        if [ "$?" != "0" ]; then
            if ${in_china}; then
                dnf install -y https://mirrors.tencent.com/epel/epel-release-latest-$(rpm -E %{rhel}).noarch.rpm
                dnf install -y https://mirrors.tencent.com/epel/epel-next-release-latest-$(rpm -E %{rhel}).noarch.rpm
            else
                dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(rpm -E %{rhel}).noarch.rpm
                dnf install -y https://dl.fedoraproject.org/pub/epel/epel-next-release-latest-$(rpm -E %{rhel}).noarch.rpm
            fi
        fi
        # Rocky Linux
        /usr/bin/crb enable >/dev/null 2>&1
        # openEuler
        if [ -f /etc/openEuler-release ]; then
            # 清理旧配置
            grep -rl '^baseurl=https://repo.oepkgs.net' /etc/yum.repos.d/ | xargs -I {} rm -f {}
            dnf config-manager --add-repo https://repo.oepkgs.net/openeuler/rpm/$(awk '{print $1"-"$3"-"$4}' /etc/openEuler-release | sed 's/[()]//g')/extras/$(uname -m)/
            oe_version=$(awk '{print $3}' /etc/openEuler-release | cut -d '.' -f 1)
            case ${oe_version} in
            22)
                dnf config-manager --add-repo https://repo.oepkgs.net/openeuler/rpm/$(awk '{print $1"-"$3"-"$4}' /etc/openEuler-release | sed 's/[()]//g')/extras/$(uname -m)/
                dnf config-manager --add-repo https://repo.oepkgs.net/openeuler/rpm/$(awk '{print $1"-"$3"-"$4}' /etc/openEuler-release | sed 's/[()]//g')/compatible/f33/$(uname -m)/
                ;;
            24)
                # openEuler 24 目前没有 p7zip-plugins,等他们自己修复
                error "openEuler 24 暂不支持安装面板(Panel installation is not supported on openEuler 24)"
                ;;
            *)
                error "不支持的 openEuler 版本(Unsupported openEuler version)"
                ;;
            esac
            # 禁用gpcheck,这仓库缺签名
            grep -rl '^baseurl=https://repo.oepkgs.net' /etc/yum.repos.d/ | xargs -I {} sh -c 'echo "gpgcheck=0" >> "{}"'
        fi
        dnf makecache -y
        dnf install -y bash curl wget zip unzip tar p7zip p7zip-plugins git jq git-core dos2unix make sudo
    elif [ ${OS} == "debian" ] || [ ${OS} == "ubuntu" ]; then
        if ${in_china}; then
            # Debian
            sed -i 's/deb.debian.org/mirrors.tencent.com/g' /etc/apt/sources.list >/dev/null 2>&1
            sed -i 's/deb.debian.org/mirrors.tencent.com/g' /etc/apt/sources.list.d/debian.sources >/dev/null 2>&1
            sed -i -e 's|security.debian.org/\? |security.debian.org/debian-security |g' \
                -e 's|security.debian.org|mirrors.tencent.com|g' \
                -e 's|deb.debian.org/debian-security|mirrors.tencent.com/debian-security|g' \
                /etc/apt/sources.list >/dev/null 2>&1
            # Ubuntu
            sed -i 's@//.*archive.ubuntu.com@//mirrors.tencent.com@g' /etc/apt/sources.list >/dev/null 2>&1
            sed -i 's@//.*archive.ubuntu.com@//mirrors.tencent.com@g' /etc/apt/sources.list.d/ubuntu.sources >/dev/null 2>&1
            sed -i 's/security.ubuntu.com/mirrors.tencent.com/g' /etc/apt/sources.list >/dev/null 2>&1
            sed -i 's/security.ubuntu.com/mirrors.tencent.com/g' /etc/apt/sources.list.d/ubuntu.sources >/dev/null 2>&1
        fi
        apt-get update -y
        apt-get install -y bash curl wget zip unzip tar p7zip p7zip-full git jq git dos2unix make sudo
    fi
    if [ "$?" != "0" ]; then
        error "安装面板依赖软件失败(Installation of panel dependency software failed)"
    fi
}

Auto_Swap() {
    # 判断是否有swap
    if [ "${SWAP}" -gt 1 ]; then
        return
    fi

    # 设置swap
    swap_file="${setup_path}/swap"
    btrfs_check=$(df -T ${setup_path} | awk '{print $2}' | tail -n 1)
    if [ "${btrfs_check}" == "btrfs" ]; then
        btrfs filesystem mkswap_file --size 4G --uuid clear ${swap_file}
    else
        dd if=/dev/zero of=$swap_file bs=1M count=4096
    fi
    chmod 600 $swap_file
    mkswap -f $swap_file
    swapon $swap_file
    echo "$swap_file    swap    swap    defaults    0 0" >>/etc/fstab

    mount -a
    if [ "$?" != "0" ]; then
        error "/etc/fstab 文件配置有误,请检查排除后重试,问题解决前勿重启系统(There is an error in the /etc/fstab file configuration, please check and try again after excluding, do not restart the system until the problem is resolved)"
    fi
}

Init_Panel() {
    systemctl stop panel >/dev/null 2>&1
    systemctl disable panel >/dev/null 2>&1
    rm -f /etc/systemd/system/panel.service
    rm -rf ${setup_path}/panel
    mkdir ${setup_path}/server
    mkdir ${setup_path}/server/cron
    mkdir ${setup_path}/server/cron/logs
    chmod -R 755 ${setup_path}/server
    mkdir ${setup_path}/panel
    # 下载面板zip包并解压
    version=$(curl -fsLm 10 --retry 3 "https://panel.haozi.net/api/version/latest")
    if [ ${ARCH} == "x86_64" ]; then
        panel_url=$(echo "$version" | jq -r '.data.downloads[] | select(.arch == "amd64") | .url')
        panel_checksum=$(echo "$version" | jq -r '.data.downloads[] | select(.arch == "amd64") | .checksum')
    elif [ ${ARCH} == "aarch64" ]; then
        panel_url=$(echo "$version" | jq -r '.data.downloads[] | select(.arch == "arm64") | .url')
        panel_checksum=$(echo "$version" | jq -r '.data.downloads[] | select(.arch == "arm64") | .checksum')
    fi
    if [ "$?" != "0" ] || [ "${panel_url}" == "" ] || [ "${panel_checksum}" == "" ]; then
        error "获取面板下载链接失败(Failed to get the panel download url)"
    fi

    panel_name=$(echo "$panel_url" | awk -F '/' '{print $NF}')
    wget -T 120 -t 3 -O ${setup_path}/panel/${panel_name} "${panel_url}"
    wget -T 20 -t 3 -O ${setup_path}/panel/${panel_name}.sha256 "${panel_checksum}"

    cd ${setup_path}/panel
    if ! sha256sum --status -c ${panel_name}.sha256 --ignore-missing; then
        error "面板压缩包校验失败(The panel zip package verification failed)"
    fi
    unzip -o ${panel_name}
    if [ "$?" != "0" ]; then
        error "解压面板失败(Failed to unzip the panel)"
    fi
    rm -f ${panel_name}
    rm -f ${panel_name}.sha256
    mkdir -p /usr/local/etc/panel
    mv -f config.example.yml /usr/local/etc/panel/config.yml
    sed -i "s|/www|${setup_path}|g" /usr/local/etc/panel/config.yml
    chmod 600 /usr/local/etc/panel/config.yml

    # 设置面板
    chmod -R 700 ${setup_path}/panel
    mv -f ${setup_path}/panel/cli /usr/local/sbin/panel-cli

    # 防火墙放行
    if [ ${OS} == "rhel" ]; then
        dnf install firewalld -y
    elif [ ${OS} == "debian" ] || [ ${OS} == "ubuntu" ]; then
        ufw disable >/dev/null 2>&1
        systemctl stop ufw >/dev/null 2>&1
        systemctl disable ufw >/dev/null 2>&1
        apt-get purge --auto-remove ufw -y
        apt-get install firewalld -y
    fi
    systemctl enable --now firewalld
    firewall-cmd --set-default-zone=public >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=22/tcp >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=80/tcp >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=443/tcp >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=443/udp >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=8888/tcp >/dev/null 2>&1
    firewall-cmd --permanent --zone=public --add-port=${ssh_port}/tcp >/dev/null 2>&1
    firewall-cmd --reload
    if [ "$?" != "0" ]; then
        error "防火墙端口放行失败(Failed to add firewall port)"
    fi

    # 写入服务文件
    wget -O /etc/systemd/system/panel.service ${download_url}/panel.service
    sed -i "s|/www|${setup_path}|g" /etc/systemd/system/panel.service
    chmod 700 /etc/systemd/system/panel.service
    systemctl daemon-reload

    panel-cli init
    systemctl enable --now panel.service
    clear
    echo -e $LOGO
    echo '面板安装成功!'
    echo -e $HR
    panel-cli info

    if [ "$?" != "0" ]; then
        error "面板启动失败(Failed to start the panel)"
    fi

    cd ${current_path}
    rm -f install.sh
}

clear
echo -e $LOGO

# 设置安装目录
while true; do
    read -p "请输入安装目录(默认/www)(Enter the installation directory (default /www)): " setup_path
    setup_path=${setup_path:-/www}
    # 检查目录是否是绝对路径
    if [ "${setup_path:0:1}" != "/" ]; then
        echo "请输入绝对路径(Please enter an absolute path)"
    elif [ "$(ls -A ${setup_path} 2>/dev/null)" ]; then
        echo "目录已存在数据(Directory already has data)"
    else
        break
    fi
done

# 安装确认
read -p "面板将安装至 ${setup_path} 目录,请输入 y 并回车以开始安装 (Enter 'y' to start installation): " install
if [ "$install" != 'y' ]; then
    echo "输入不正确,已退出安装。"
    echo "Incorrect input, installation has been exited."
    exit
fi

clear
echo -e $LOGO
echo "安装面板依赖软件(如报错请检查软件源是否正常)"
echo "Installing panel dependency software (if error, please check the software source)"
echo -e $HR
sleep 1s
Prepare_System
Auto_Swap

echo -e $LOGO
echo "安装面板运行环境(视网络情况可能需要较长时间)"
echo "Installing the panel running environment (may take a long time depending on the network)"
echo -e $HR
sleep 1s
Init_Panel