#!/bin/bash
# ============================================================
# Kangle 主机环境 · 运维工具箱
#   对齐彩虹 kangle.cccyun.cn 的 15 项运维功能
#   支持 CentOS 7 / Ubuntu 22.04+ / Debian 12
#   用法: bash toolbox.sh    （交互菜单）
#         bash toolbox.sh <编号>   （直接执行某项）
# ============================================================
set -uo pipefail

IS_DEB=$([ -f /etc/debian_version ] && echo 1 || echo 0)
REL=$([ "$IS_DEB" = "1" ] && echo "$(. /etc/os-release; echo ${VERSION_ID%%.*})" || rpm -q --qf '%{VERSION}' centos-release 2>/dev/null | cut -d. -f1)
MIRROR_DEB="mirrors.aliyun.com"
MIRROR_YUM="mirrors.aliyun.com"

grn(){ printf "\033[32m%s\033[0m\n" "$*"; }
red(){ printf "\033[31m%s\033[0m\n" "$*"; }
ylw(){ printf "\033[33m%s\033[0m\n" "$*"; }
line(){ printf '%s\n' "------------------------------------------------------------"; }
need_root(){ [ "$(id -u)" = "0" ] || { red "需要 root 运行"; exit 1; }; }

# ---------- 1. 换源 ----------
do_mirror(){
  need_root
  line; grn "1. 更换软件源为国内镜像"
  [ -f /etc/yum.repos.d/CentOS-Base.repo ] && cp -a /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak.$(date +%s)
  if [ "$IS_DEB" = "1" ]; then
    cp -a /etc/apt/sources.list /etc/apt/sources.list.bak.$(date +%s) 2>/dev/null
    if [ "$REL" -ge 24 ]; then
      sed -i "s|http://.*archive.ubuntu.com|http://$MIRROR_DEB|g; s|http://.*security.ubuntu.com|http://$MIRROR_DEB|g" /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null
    fi
    sed -i "s|http://.*archive.ubuntu.com|http://$MIRROR_DEB|g; s|http://.*security.ubuntu.com|http://$MIRROR_DEB|g" /etc/apt/sources.list 2>/dev/null
    apt-get update -qq && grn "apt 源已切换"
  else
    curl -s -o /etc/yum.repos.d/CentOS-Base.repo "http://$MIRROR_YUM/repo/Centos-$REL.repo" 2>/dev/null
    yum clean all >/dev/null 2>&1; yum makecache >/dev/null 2>&1 && grn "yum 源已切换"
  fi
}

# ---------- 2. DNS ----------
do_dns(){
  need_root
  line; grn "2. 设置 DNS（阿里 223.5.5.5 / 腾讯 119.29.29.29）"
  cp -a /etc/resolv.conf /etc/resolv.conf.bak.$(date +%s) 2>/dev/null
  if [ "$IS_DEB" = "1" ] && command -v systemd-resolved >/dev/null; then
    mkdir -p /etc/systemd/resolved.conf.d
    printf '[Resolve]\nDNS=223.5.5.5 119.29.29.29\nFallbackDNS=8.8.8.8\n' > /etc/systemd/resolved.conf.d/dns.conf
    systemctl restart systemd-resolved 2>/dev/null
  fi
  printf 'nameserver 223.5.5.5\nnameserver 119.29.29.29\n' > /etc/resolv.conf
  chattr +i /etc/resolv.conf 2>/dev/null
  grn "DNS 已设置并在部分系统上锁定（解锁: chattr -i /etc/resolv.conf）"
  cat /etc/resolv.conf | grep nameserver
}

# ---------- 3. 时间同步 ----------
do_ntp(){
  need_root
  line; grn "3. 时间同步（阿里云 NTP）"
  if [ "$IS_DEB" = "1" ]; then
    DEBIAN_FRONTEND=noninteractive apt-get install -y chrony >/dev/null 2>&1
    sed -i '/^pool /d;/^server /d' /etc/chrony/chrony.conf 2>/dev/null
    printf 'server ntp.aliyun.com iburst\nserver ntp1.aliyun.com iburst\n' >> /etc/chrony/chrony.conf
    systemctl restart chrony 2>/dev/null || systemctl restart chronyd 2>/dev/null
  else
    yum -y install chrony >/dev/null 2>&1
    sed -i '/^server /d' /etc/chrony.conf 2>/dev/null
    printf 'server ntp.aliyun.com iburst\nserver ntp1.aliyun.com iburst\n' >> /etc/chrony.conf
    systemctl restart chronyd 2>/dev/null
  fi
  sleep 2
  chronyc -a makestep >/dev/null 2>&1
  grn "当前时间: $(date '+%F %T %Z')"
}

# ---------- 4. 系统清理 ----------
do_clean(){
  need_root
  line; grn "4. 系统清理"
  if [ "$IS_DEB" = "1" ]; then
    apt-get clean; apt-get autoremove -y >/dev/null 2>&1
  else
    yum clean all >/dev/null 2>&1
  fi
  journalctl --vacuum-time=7d >/dev/null 2>&1
  find /tmp -type f -atime +7 -delete 2>/dev/null
  find /var/log -type f -name '*.gz' -mtime +14 -delete 2>/dev/null
  find /var/log -type f -name '*.log' -size +100M -exec truncate -s 0 {} \; 2>/dev/null
  grn "清理完成，当前磁盘: $(df -h / | tail -1 | awk '{print $3"/"$2" ("$5")"}')"
}

# ---------- 6. BBR ----------
do_bbr(){
  need_root
  line; grn "6. 开启 BBR TCP 加速"
  modprobe tcp_bbr 2>/dev/null
  if ! sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then
    red "当前内核不支持 BBR（需 4.9+）。查看: uname -r"
    return 1
  fi
  grep -q 'net.core.default_qdisc' /etc/sysctl.conf || cat >> /etc/sysctl.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
  sysctl -p >/dev/null 2>&1
  a=$(sysctl -n net.ipv4.tcp_congestion_control)
  b=$(sysctl -n net.core.default_qdisc)
  [ "$a" = "bbr" ] && grn "BBR 已启用（拥塞控制=$a, 队列=$b）" || red "启用失败，当前=$a"
}

# ---------- 7. Swap ----------
do_swap(){
  need_root
  line; grn "7. 自动配置 Swap"
  cur=$(free -m | awk '/Swap:/{print $2}')
  if [ "$cur" -gt 0 ]; then
    ylw "已存在 Swap ${cur}MB，跳过"; return 0
  fi
  mem=$(free -m | awk '/Mem:/{print $2}')
  size=1024; [ "$mem" -ge 2048 ] && size=2048; [ "$mem" -le 1024 ] && size=1024
  # 小内存机器给 2G，避免编译 OOM
  [ "$mem" -lt 1200 ] && size=2048
  swapfile=/swapfile
  [ -f "$swapfile" ] && { ylw "$swapfile 已存在"; }
  if [ ! -f "$swapfile" ]; then
    fallocate -l ${size}M "$swapfile" 2>/dev/null || dd if=/dev/zero of="$swapfile" bs=1M count=$size 2>/dev/null
    chmod 600 "$swapfile"
    mkswap "$swapfile" >/dev/null 2>&1
  fi
  swapon "$swapfile" 2>/dev/null
  grep -q "$swapfile" /etc/fstab || echo "$swapfile swap swap defaults 0 0" >> /etc/fstab
  grn "Swap 已启用: $(free -m | awk '/Swap:/{print $2}')MB"
}

# ---------- 8. 防火墙/IP 规则 ----------
do_iptables(){
  need_root
  line; grn "8. 防火墙常用端口放行"
  ports="80 443 21 22 3306 3311 3312"
  if command -v ufw >/dev/null 2>&1; then
    for p in $ports; do ufw allow $p/tcp >/dev/null 2>&1; done
    ufw --force enable >/dev/null 2>&1; grn "ufw 规则已添加"
  elif command -v firewall-cmd >/dev/null 2>&1; then
    systemctl start firewalld 2>/dev/null
    for p in $ports; do firewall-cmd --permanent --add-port=$p/tcp >/dev/null 2>&1; done
    firewall-cmd --reload >/dev/null 2>&1; grn "firewalld 规则已添加"
  else
    for p in $ports; do iptables -C INPUT -p tcp --dport $p -j ACCEPT 2>/dev/null || iptables -I INPUT -p tcp --dport $p -j ACCEPT; done
    grn "iptables 规则已添加（注意：未持久化，需 iptables-save）"
  fi
}

# ---------- 9. MySQL 调优 ----------
do_mysql_tune(){
  need_root
  line; grn "9. 生成 MySQL/MariaDB 优化配置"
  mem=$(free -m | awk '/Mem:/{print $2}')
  if [ "$mem" -lt 2048 ]; then BP=128M; elif [ "$mem" -lt 4096 ]; then BP=256M
  elif [ "$mem" -lt 8192 ]; then BP=512M; else BP=1024M; fi
  cat > /etc/my.cnf.d/kangle-tune.cnf <<EOF 2>/dev/null || cat > /root/my.cnf.tune <<EOF
[mysqld]
innodb_buffer_pool_size = $BP
innodb_flush_log_at_trx_commit = 2
innodb_file_per_table = 1
max_connections = 300
slow_query_log = 1
slow_query_log_file = /var/log/mysql-slow.log
long_query_time = 1
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
skip-name-resolve = 1
[client]
default-character-set = utf8mb4
EOF
  grn "配置已生成（buffer pool = $BP，按内存 ${mem}MB 计算）"
  [ -f /etc/my.cnf.d/kangle-tune.cnf ] && ylw "重启生效: systemctl restart mysqld 或 mariadb"
}

# ---------- 10. 更新软件包 ----------
do_update(){
  need_root
  line; grn "10. 更新系统软件包"
  if [ "$IS_DEB" = "1" ]; then
    apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
  else
    yum -y update
  fi
  grn "更新完成"
}

# ---------- 11. 自动挂载磁盘 ----------
do_disk(){
  need_root
  line; grn "11. 检测未挂载的独立磁盘"
  lsblk -dpno NAME,SIZE,TYPE | grep disk | while read -r dev size type; do
    base=$(basename "$dev")
    [ "$base" = "$(lsblk -no PKNAME "$(findmnt -n -o SOURCE /)" 2>/dev/null)" ] && continue
    used=$(lsblk -no MOUNTPOINT "$dev" | grep -c .)
    [ "$used" -gt 0 ] && continue
    echo "发现未使用磁盘: $dev ($size)"
    echo -n "  是否格式化并挂载到 /www/data？(y/N) "
    read -r ans
    [ "$ans" != "y" ] && continue
    mkfs.ext4 -F "$dev" >/dev/null 2>&1
    mkdir -p /www/data
    mount "$dev" /www/data
    grep -q "/www/data" /etc/fstab || echo "$dev /www/data ext4 defaults 0 0" >> /etc/fstab
    grn "  $dev 已挂载到 /www/data"
  done
  grn "磁盘检查完毕"
}

# ---------- 12. Redis ----------
do_redis(){
  need_root
  line; grn "12. 安装 Redis"
  if [ "$IS_DEB" = "1" ]; then
    DEBIAN_FRONTEND=noninteractive apt-get install -y redis-server >/dev/null 2>&1
    sed -i 's/^# maxmemory .*/maxmemory 256mb/; s/^# maxmemory-policy .*/maxmemory-policy allkeys-lru/' /etc/redis/redis.conf 2>/dev/null
    sed -i 's/^bind 127.0.0.1.*/bind 127.0.0.1 ::1/' /etc/redis/redis.conf 2>/dev/null
    systemctl enable --now redis-server >/dev/null 2>&1
  else
    yum -y install redis >/dev/null 2>&1
    sed -i 's/^bind .*/bind 127.0.0.1/' /etc/redis.conf 2>/dev/null
    systemctl enable --now redis >/dev/null 2>&1
  fi
  sleep 1
  pidof redis-server >/dev/null && grn "Redis 已启动（仅监听 127.0.0.1）" || red "Redis 启动失败"
}

# ---------- 13. Fail2ban ----------
do_fail2ban(){
  need_root
  line; grn "13. 安装 Fail2ban（SSH 防爆破）"
  if [ "$IS_DEB" = "1" ]; then
    DEBIAN_FRONTEND=noninteractive apt-get install -y fail2ban >/dev/null 2>&1
  else
    yum -y install epel-release >/dev/null 2>&1; yum -y install fail2ban >/dev/null 2>&1
  fi
  cat > /etc/fail2ban/jail.d/sshd.local <<'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 5
findtime = 600
bantime = 3600
EOF
  systemctl enable --now fail2ban >/dev/null 2>&1
  sleep 1
  systemctl is-active fail2ban >/dev/null && grn "Fail2ban 已启用（5 次失败封 1 小时）" || red "启动失败"
}

# ---------- 14. SSL 证书 ----------
do_ssl(){
  need_root
  line; grn "14. 申请 SSL 证书（acme.sh + Let's Encrypt）"
  echo -n "请输入域名（多个用空格分隔）: "; read -r DOMAINS
  [ -z "$DOMAINS" ] && { red "未输入域名"; return 1; }
  main=$(echo "$DOMAINS" | awk '{print $1}')
  if [ ! -f /root/.acme.sh/acme.sh ]; then
    curl -s https://get.acme.sh | sh -s email=admin@$main >/dev/null 2>&1
  fi
  /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
  cd /www/wwwroot/$main 2>/dev/null || cd /tmp
  dargs=""; for d in $DOMAINS; do dargs="$dargs -d $d"; done
  /root/.acme.sh/acme.sh --issue $dargs -w "$(pwd)" --keylength ec-256 2>&1 | tail -5
  grn "证书目录: /root/.acme.sh/$main_ecc/"
}

# ---------- 15. 一键初始化 ----------
do_init(){
  need_root
  line; grn "15. 系统初始化（换源 + DNS + 时间 + 清理 + BBR + Swap + Fail2ban）"
  do_mirror; do_dns; do_ntp; do_clean; do_bbr; do_swap; do_fail2ban
  line; grn "初始化完成"; do_status
}

do_status(){
  line; grn "当前状态"
  echo "系统    : $(. /etc/os-release 2>/dev/null; echo ${PRETTY_NAME:-unknown})"
  echo "内核    : $(uname -r)"
  echo "CPU/内存: $(nproc) 核 / $(free -m | awk '/Mem:/{print $2}')MB"
  echo "磁盘    : $(df -h / | tail -1 | awk '{print $3"/"$2" ("$5")"}')"
  echo "Swap    : $(free -m | awk '/Swap:/{print $2}')MB"
  echo "BBR     : $(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null)"
  echo "时间    : $(date '+%F %T %Z')"
  line
}

menu(){
  while true; do
    clear 2>/dev/null
    cat <<EOF
============================================================
        Kangle 主机环境 · 运维工具箱
============================================================
  $(do_status 2>/dev/null | head -0)
  1)  更换软件源（国内镜像）          9)  MySQL/MariaDB 优化
  2)  设置 DNS                        10) 更新系统软件包
  3)  时间同步                        11) 自动挂载磁盘
  4)  系统清理                        12) 安装 Redis
  5)  安全基线检查                    13) 安装 Fail2ban
  6)  开启 BBR 加速                   14) 申请 SSL 证书
  7)  配置 Swap                       15) 一键初始化
  8)  防火墙端口放行                   0)  退出
============================================================
EOF
    echo -n "请选择 [0-15]: "; read -r c
    case "$c" in
      1) do_mirror;; 2) do_dns;; 3) do_ntp;; 4) do_clean;;
      5) do_status;; 6) do_bbr;; 7) do_swap;; 8) do_iptables;;
      9) do_mysql_tune;; 10) do_update;; 11) do_disk;; 12) do_redis;;
      13) do_fail2ban;; 14) do_ssl;; 15) do_init;;
      0) exit 0;; *) red "无效选项";;
    esac
    echo; echo -n "按回车继续..."; read -r _
  done
}

case "${1:-}" in
  1) do_mirror;; 2) do_dns;; 3) do_ntp;; 4) do_clean;; 5) do_status;;
  6) do_bbr;; 7) do_swap;; 8) do_iptables;; 9) do_mysql_tune;; 10) do_update;;
  11) do_disk;; 12) do_redis;; 13) do_fail2ban;; 14) do_ssl;; 15) do_init;;
  "") menu;;
  *) echo "用法: bash toolbox.sh [1-15]";;
esac
