diff --git a/server/app/shell/README.md b/server/app/shell/README.md new file mode 100644 index 0000000..0b966b9 --- /dev/null +++ b/server/app/shell/README.md @@ -0,0 +1,7 @@ +# 说明 + +> 参考部分开源代码结合AI生成单功能shell脚本,仅经过开发者测试 + +致谢: +- [科技Lion的Shell脚本工具箱](https://kejilion.sh/) +- [NS主机论坛](https://www.nodeseek.com/post-183694-1) diff --git a/server/app/shell/docker-install-cn.sh b/server/app/shell/docker-install-cn.sh new file mode 100644 index 0000000..c185f8d --- /dev/null +++ b/server/app/shell/docker-install-cn.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [ "${EUID:-$(id -u)}" -ne 0 ]; then + echo "请使用 root 身份运行此脚本" + exit 1 +fi + +log() { + printf '[INFO] %s\n' "$1" +} + +warn() { + printf '[WARN] %s\n' "$1" +} + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +detect_os() { + if [ ! -f /etc/os-release ]; then + echo "无法识别系统:缺少 /etc/os-release" + exit 1 + fi + + . /etc/os-release + OS_ID="${ID:-}" + OS_VERSION_ID="${VERSION_ID:-}" + OS_CODENAME="${VERSION_CODENAME:-}" + + case "$OS_ID" in + debian|ubuntu|raspbian) + PKG_TYPE="apt" + ;; + centos|rhel|rocky|almalinux|ol|fedora) + PKG_TYPE="dnf" + ;; + *) + echo "当前脚本暂不支持该系统:$OS_ID" + exit 1 + ;; + esac +} + +install_prereqs_apt() { + export DEBIAN_FRONTEND=noninteractive + apt-get update -y + apt-get install -y ca-certificates curl gnupg lsb-release + install -m 0755 -d /etc/apt/keyrings +} + +install_docker_apt() { + local repo_url="https://mirrors.aliyun.com/docker-ce/linux/${OS_ID}" + local gpg_url="${repo_url}/gpg" + + install_prereqs_apt + + if [ -z "$OS_CODENAME" ]; then + OS_CODENAME=$(awk -F'[=(]' '/VERSION_CODENAME|DISTRIB_CODENAME/{print $2; exit}' /etc/os-release 2>/dev/null || true) + fi + + if [ -z "$OS_CODENAME" ] && [ "$OS_ID" = "debian" ]; then + case "$OS_VERSION_ID" in + 12) OS_CODENAME="bookworm" ;; + 11) OS_CODENAME="bullseye" ;; + 10) OS_CODENAME="buster" ;; + esac + fi + + if [ -z "$OS_CODENAME" ] && [ "$OS_ID" = "ubuntu" ]; then + case "$OS_VERSION_ID" in + 24.04) OS_CODENAME="noble" ;; + 22.04) OS_CODENAME="jammy" ;; + 20.04) OS_CODENAME="focal" ;; + 18.04) OS_CODENAME="bionic" ;; + esac + fi + + if [ -z "$OS_CODENAME" ]; then + echo "无法识别系统代号,请手动检查 /etc/os-release" + exit 1 + fi + + log "配置 Docker APT 国内源: $repo_url" + curl -fsSL "$gpg_url" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + cat > /etc/apt/sources.list.d/docker.list < /etc/docker/daemon.json <<'EOF' +{ + "registry-mirrors": [ + "https://docker.1ms.run", + "https://docker.m.daocloud.io", + "https://docker.hlmirror.com", + "https://docker.amingg.com", + "https://dockerproxy.net", + "https://docker.1panel.live", + "https://docker.kejilion.pro" + ] +} +EOF +} + +enable_docker_service() { + systemctl daemon-reload + systemctl enable docker + systemctl restart docker +} + +verify_installation() { + log "Docker 版本信息" + docker --version + docker compose version + echo + log "当前镜像加速配置" + cat /etc/docker/daemon.json +} + +main() { + detect_os + log "检测到系统: ${OS_ID} ${OS_VERSION_ID:-unknown}" + + case "$PKG_TYPE" in + apt) + install_docker_apt + ;; + dnf) + install_docker_dnf + ;; + esac + + configure_daemon_mirror + enable_docker_service + verify_installation + + log "Docker 安装完成,已启用大陆镜像加速环境" +} + +main "$@" diff --git a/server/app/shell/reinstall-os.sh b/server/app/shell/reinstall-os.sh new file mode 100644 index 0000000..ea71367 --- /dev/null +++ b/server/app/shell/reinstall-os.sh @@ -0,0 +1,271 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [ "${EUID:-$(id -u)}" -ne 0 ]; then + echo "请使用 root 身份运行此脚本" + exit 1 +fi + +SCRIPT_URL_CN="https://cnb.cool/bin456789/reinstall/-/git/raw/main/reinstall.sh" +WORK_DIR="/tmp/reinstall-bin456789" +SCRIPT_PATH="$WORK_DIR/reinstall.sh" + +mkdir -p "$WORK_DIR" + +log() { + printf '[INFO] %s\n' "$1" +} + +warn() { + printf '[WARN] %s\n' "$1" +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +install_basic_tools() { + if need_cmd apt-get; then + apt-get update -y + apt-get install -y curl wget + elif need_cmd dnf; then + dnf install -y curl wget + elif need_cmd yum; then + yum install -y curl wget + elif need_cmd apk; then + apk add --no-cache curl wget + else + warn "未识别包管理器,请确保系统已安装 curl 或 wget" + fi +} + +download_reinstall_script() { + log "下载 bin456789/reinstall 脚本" + if need_cmd curl; then + curl -fsSL -o "$SCRIPT_PATH" "$SCRIPT_URL_CN" + else + wget -O "$SCRIPT_PATH" "$SCRIPT_URL_CN" + fi + chmod +x "$SCRIPT_PATH" +} + +confirm_reinstall() { + echo "--------------------------------" + echo "该脚本仅封装 bin456789/reinstall 项目。" + echo "重装会清空目标硬盘数据,存在失联风险。" + echo "请提前备份重要数据,并确认可以接受自动重启。" + echo "如果误操作,可在重启前执行: bash reinstall.sh reset" + echo "--------------------------------" + read -r -p "确认继续吗?(Y/N): " answer + case "$answer" in + [Yy]) ;; + *) + echo "已取消" + exit 0 + ;; + esac +} + +read_password_args() { + local prompt="$1" + local user_label="$2" + local pass + echo "默认用户名: $user_label" + read -r -s -p "$prompt" pass + echo + if [ -n "$pass" ]; then + PASSWORD_ARGS=(--password "$pass") + else + PASSWORD_ARGS=() + echo "未输入密码,将由上游脚本自动生成随机密码" + fi +} + +run_reinstall() { + install_basic_tools + download_reinstall_script + cd "$WORK_DIR" + bash "$SCRIPT_PATH" "$@" +} + +show_menu() { + cat <<'EOF' +-------------------------------- +bin456789/reinstall 独立封装脚本 +-------------------------------- +Linux 重装 + 1. Debian 13 + 2. Debian 12 + 3. Debian 11 + 4. Debian 10 +11. Ubuntu 24.04 +12. Ubuntu 22.04 +13. Ubuntu 20.04 +14. Ubuntu 18.04 +21. Alpine 3.23 +22. Rocky 10 +23. AlmaLinux 10 +24. Oracle 10 +25. Fedora 43 +26. openEuler 24.03 +27. openSUSE tumbleweed +28. Kali +29. Arch +30. Gentoo +31. fnOS 1 +-------------------------------- +Windows 安装 +41. Windows 11 Pro +42. Windows 11 Enterprise LTSC 2024 +43. Windows Server 2025 Datacenter +44. Windows Server 2022 Datacenter +-------------------------------- +其它功能 +51. DD 指定 Raw 镜像 +52. 启动到 Alpine Live OS (--hold 1) +53. 启动到 netboot.xyz +54. 取消重装 (reset) +-------------------------------- + 0. 退出 +-------------------------------- +EOF +} + +main() { + local choice + local img_url + local image_name + + confirm_reinstall + show_menu + read -r -p "请选择功能: " choice + + case "$choice" in + 1) + read_password_args "请输入 Debian 13 的 root 密码(留空则随机): " "root" + run_reinstall debian 13 "${PASSWORD_ARGS[@]}" + ;; + 2) + read_password_args "请输入 Debian 12 的 root 密码(留空则随机): " "root" + run_reinstall debian 12 "${PASSWORD_ARGS[@]}" + ;; + 3) + read_password_args "请输入 Debian 11 的 root 密码(留空则随机): " "root" + run_reinstall debian 11 "${PASSWORD_ARGS[@]}" + ;; + 4) + read_password_args "请输入 Debian 10 的 root 密码(留空则随机): " "root" + run_reinstall debian 10 "${PASSWORD_ARGS[@]}" + ;; + 11) + read_password_args "请输入 Ubuntu 24.04 的 root 密码(留空则随机): " "root" + run_reinstall ubuntu 24.04 "${PASSWORD_ARGS[@]}" + ;; + 12) + read_password_args "请输入 Ubuntu 22.04 的 root 密码(留空则随机): " "root" + run_reinstall ubuntu 22.04 "${PASSWORD_ARGS[@]}" + ;; + 13) + read_password_args "请输入 Ubuntu 20.04 的 root 密码(留空则随机): " "root" + run_reinstall ubuntu 20.04 "${PASSWORD_ARGS[@]}" + ;; + 14) + read_password_args "请输入 Ubuntu 18.04 的 root 密码(留空则随机): " "root" + run_reinstall ubuntu 18.04 "${PASSWORD_ARGS[@]}" + ;; + 21) + read_password_args "请输入 Alpine 3.23 的 root 密码(留空则随机): " "root" + run_reinstall alpine 3.23 "${PASSWORD_ARGS[@]}" + ;; + 22) + read_password_args "请输入 Rocky 10 的 root 密码(留空则随机): " "root" + run_reinstall rocky 10 "${PASSWORD_ARGS[@]}" + ;; + 23) + read_password_args "请输入 AlmaLinux 10 的 root 密码(留空则随机): " "root" + run_reinstall almalinux 10 "${PASSWORD_ARGS[@]}" + ;; + 24) + read_password_args "请输入 Oracle 10 的 root 密码(留空则随机): " "root" + run_reinstall oracle 10 "${PASSWORD_ARGS[@]}" + ;; + 25) + read_password_args "请输入 Fedora 43 的 root 密码(留空则随机): " "root" + run_reinstall fedora 43 "${PASSWORD_ARGS[@]}" + ;; + 26) + read_password_args "请输入 openEuler 24.03 的 root 密码(留空则随机): " "root" + run_reinstall openeuler 24.03 "${PASSWORD_ARGS[@]}" + ;; + 27) + read_password_args "请输入 openSUSE tumbleweed 的 root 密码(留空则随机): " "root" + run_reinstall opensuse tumbleweed "${PASSWORD_ARGS[@]}" + ;; + 28) + read_password_args "请输入 Kali 的 root 密码(留空则随机): " "root" + run_reinstall kali "${PASSWORD_ARGS[@]}" + ;; + 29) + read_password_args "请输入 Arch 的 root 密码(留空则随机): " "root" + run_reinstall arch "${PASSWORD_ARGS[@]}" + ;; + 30) + read_password_args "请输入 Gentoo 的 root 密码(留空则随机): " "root" + run_reinstall gentoo "${PASSWORD_ARGS[@]}" + ;; + 31) + read_password_args "请输入 fnOS 1 的 root 密码(留空则随机): " "root" + run_reinstall fnos 1 "${PASSWORD_ARGS[@]}" + ;; + + 41) + read_password_args "请输入 Windows 11 Pro 的 administrator 密码(留空则随机): " "administrator" + run_reinstall windows --image-name "Windows 11 Pro" --lang zh-cn "${PASSWORD_ARGS[@]}" + ;; + 42) + read_password_args "请输入 Windows 11 Enterprise LTSC 2024 的 administrator 密码(留空则随机): " "administrator" + run_reinstall windows --image-name "Windows 11 Enterprise LTSC 2024" --lang zh-cn "${PASSWORD_ARGS[@]}" + ;; + 43) + read_password_args "请输入 Windows Server 2025 Datacenter 的 administrator 密码(留空则随机): " "administrator" + run_reinstall windows --image-name "Windows Server 2025 SERVERDATACENTER" --lang zh-cn "${PASSWORD_ARGS[@]}" + ;; + 44) + read_password_args "请输入 Windows Server 2022 Datacenter 的 administrator 密码(留空则随机): " "administrator" + run_reinstall windows --image-name "Windows Server 2022 SERVERDATACENTER" --lang zh-cn "${PASSWORD_ARGS[@]}" + ;; + + 51) + read -r -p "请输入 Raw/VHD 镜像链接: " img_url + if [ -z "$img_url" ]; then + echo "镜像链接不能为空" + exit 1 + fi + run_reinstall dd --img "$img_url" + ;; + 52) + read_password_args "请输入 Alpine Live OS 的 root 密码(留空则随机): " "root" + run_reinstall alpine --hold 1 "${PASSWORD_ARGS[@]}" + ;; + 53) + run_reinstall netboot.xyz + ;; + 54) + install_basic_tools + download_reinstall_script + cd "$WORK_DIR" + bash "$SCRIPT_PATH" reset + ;; + 0) + echo "已退出" + exit 0 + ;; + *) + echo "无效选择" + exit 1 + ;; + esac +} + +main "$@" diff --git a/server/app/shell/system-info.sh b/server/app/shell/system-info.sh new file mode 100644 index 0000000..1c33ac5 --- /dev/null +++ b/server/app/shell/system-info.sh @@ -0,0 +1,246 @@ +#!/usr/bin/env bash + +set -u + +get_public_json() { + curl -fsSL --max-time 5 https://ipinfo.io/json 2>/dev/null || true +} + +get_public_ipv4() { + local ip + ip=$(curl -fsSL --max-time 5 https://ipinfo.io/ip 2>/dev/null | tr -d '\r') || true + printf '%s' "$ip" +} + +get_public_ipv6() { + local ip + ip=$(curl -fsSL --max-time 5 https://v6.ipinfo.io/ip 2>/dev/null | tr -d '\r') || true + printf '%s' "$ip" +} + +json_get() { + local key="$1" + awk -F': ' -v k="\"$key\"" '$1 ~ k {gsub(/^[[:space:]]+|,$/, "", $2); gsub(/^"|"$/, "", $2); print $2; exit}' +} + +format_bytes_human() { + awk -v bytes="${1:-0}" 'BEGIN { + split("B K M G T P", u, " "); + v = bytes + 0; + i = 1; + while (v >= 1024 && i < 6) { + v /= 1024; + i++; + } + if (i == 1) { + printf "%d%s", v, u[i]; + } else { + printf "%.2f%s", v, u[i]; + } + }' +} + +format_bytes_compact() { + awk -v bytes="${1:-0}" 'BEGIN { + split("B K M G T P", u, " "); + v = bytes + 0; + i = 1; + while (v >= 1024 && i < 6) { + v /= 1024; + i++; + } + if (i == 1) { + printf "%d%s", v, u[i]; + } else if (v >= 100) { + printf "%.0f%s", v, u[i]; + } else if (v >= 10) { + printf "%.1f%s", v, u[i]; + } else { + printf "%.2f%s", v, u[i]; + } + }' +} + +format_mem_usage() { + awk -v used_b="$1" -v total_b="$2" 'BEGIN { + used = used_b / 1024 / 1024; + total = total_b / 1024 / 1024; + pct = (total_b > 0 ? used_b * 100 / total_b : 0); + printf "%.2f/%.2fM (%.2f%%)", used, total, pct; + }' +} + +format_swap_usage() { + awk -v used_m="$1" -v total_m="$2" 'BEGIN { + pct = (total_m > 0 ? used_m * 100 / total_m : 0); + if (used_m == int(used_m) && total_m == int(total_m)) { + printf "%dM/%dM (%.0f%%)", used_m, total_m, pct; + } else { + printf "%.2fM/%.2fM (%.2f%%)", used_m, total_m, pct; + } + }' +} + +format_uptime_cn() { + local total seconds days hours minutes + total=$(cut -d. -f1 /proc/uptime 2>/dev/null) + total=${total:-0} + days=$((total / 86400)) + hours=$(((total % 86400) / 3600)) + minutes=$(((total % 3600) / 60)) + + if [ "$days" -gt 0 ]; then + printf '%d天%d时%d分' "$days" "$hours" "$minutes" + elif [ "$hours" -gt 0 ]; then + printf '%d时%d分' "$hours" "$minutes" + else + printf '%d分' "$minutes" + fi +} + +get_cpu_usage_percent() { + local a b + a=$(grep '^cpu ' /proc/stat) + sleep 1 + b=$(grep '^cpu ' /proc/stat) + awk -v A="$a" -v B="$b" 'BEGIN { + split(A, x, " "); + split(B, y, " "); + idle1 = x[5] + x[6]; + idle2 = y[5] + y[6]; + total1 = 0; + total2 = 0; + for (i = 2; i <= 11; i++) { + total1 += x[i]; + total2 += y[i]; + } + diff_total = total2 - total1; + diff_idle = idle2 - idle1; + usage = (diff_total > 0 ? (diff_total - diff_idle) * 100 / diff_total : 0); + printf "%.0f", usage; + }' +} + +get_total_traffic() { + awk 'BEGIN {rx=0; tx=0} + NR > 2 { + gsub(":", "", $1); + if ($1 != "lo") { + rx += $2; + tx += $10; + } + } + END {printf "%s %s", rx, tx}' /proc/net/dev +} + +get_dns_servers() { + awk '/^nameserver[[:space:]]+/ {printf "%s ", $2} END {print ""}' /etc/resolv.conf 2>/dev/null +} + +get_disk_usage() { + df -B1 / 2>/dev/null | awk 'NR==2 { + used=$3; total=$2; pct=(total>0?used*100/total:0); + split("B K M G T P", u, " "); + uv=used; tv=total; ui=1; ti=1; + while (uv>=1024 && ui<6) {uv/=1024; ui++} + while (tv>=1024 && ti<6) {tv/=1024; ti++} + uf=(ui==1?sprintf("%d%s", uv, u[ui]):(uv>=100?sprintf("%.0f%s", uv, u[ui]):(uv>=10?sprintf("%.1f%s", uv, u[ui]):sprintf("%.2f%s", uv, u[ui])))); + tf=(ti==1?sprintf("%d%s", tv, u[ti]):(tv>=100?sprintf("%.0f%s", tv, u[ti]):(tv>=10?sprintf("%.1f%s", tv, u[ti]):sprintf("%.2f%s", tv, u[ti])))); + printf "%s/%s (%.0f%%)", uf, tf, pct; + }' +} + +main() { + local hostname os_info kernel_version cpu_arch cpu_model cpu_cores cpu_freq + local cpu_usage load_avg tcp_count udp_count mem_total mem_available mem_used + local swap_total swap_used disk_info rx tx congestion_algorithm queue_algorithm + local ipinfo isp_info ipv4_address ipv6_address dns_addresses country city timezone current_time runtime + + hostname=$(hostname 2>/dev/null || uname -n) + os_info=$(grep '^PRETTY_NAME=' /etc/os-release 2>/dev/null | cut -d= -f2- | tr -d '"') + kernel_version=$(uname -r 2>/dev/null) + cpu_arch=$(uname -m 2>/dev/null) + cpu_model=$(awk -F': *' '/^Model name:/{print $2; exit}' < <(lscpu 2>/dev/null)) + [ -n "$cpu_model" ] || cpu_model=$(awk -F': *' '/^model name[[:space:]]*:/{print $2; exit}' /proc/cpuinfo 2>/dev/null) + cpu_cores=$(nproc 2>/dev/null) + cpu_freq=$(awk -F': *' '/^CPU max MHz:/{printf "%.1f GHz", $2/1000; found=1; exit} + /^CPU MHz:/{printf "%.1f GHz", $2/1000; found=1; exit} + END{if (!found) exit 1}' < <(lscpu 2>/dev/null) 2>/dev/null) + [ -n "$cpu_freq" ] || cpu_freq=$(awk -F': *' '/^cpu MHz[[:space:]]*:/{printf "%.1f GHz", $2/1000; exit}' /proc/cpuinfo 2>/dev/null) + [ -n "$cpu_freq" ] || cpu_freq="N/A" + + cpu_usage=$(get_cpu_usage_percent) + load_avg=$(awk '{print $1", "$2", "$3}' /proc/loadavg 2>/dev/null) + tcp_count=$(ss -tanH 2>/dev/null | wc -l | awk '{print $1}') + udp_count=$(ss -uanH 2>/dev/null | wc -l | awk '{print $1}') + + mem_total=$(awk '/MemTotal:/ {print $2*1024; exit}' /proc/meminfo 2>/dev/null) + mem_available=$(awk '/MemAvailable:/ {print $2*1024; exit}' /proc/meminfo 2>/dev/null) + mem_total=${mem_total:-0} + mem_available=${mem_available:-0} + mem_used=$((mem_total - mem_available)) + + swap_total=$(free -m 2>/dev/null | awk 'NR==3{print $2}') + swap_used=$(free -m 2>/dev/null | awk 'NR==3{print $3}') + swap_total=${swap_total:-0} + swap_used=${swap_used:-0} + + disk_info=$(get_disk_usage) + + read -r rx tx <<< "$(get_total_traffic)" + rx=$(format_bytes_compact "${rx:-0}") + tx=$(format_bytes_compact "${tx:-0}") + + congestion_algorithm=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "N/A") + queue_algorithm=$(sysctl -n net.core.default_qdisc 2>/dev/null || echo "N/A") + + ipinfo=$(get_public_json) + isp_info=$(printf '%s +' "$ipinfo" | json_get org) + ipv4_address=$(get_public_ipv4) + ipv6_address=$(get_public_ipv6) + dns_addresses=$(get_dns_servers) + country=$(printf '%s +' "$ipinfo" | json_get country) + city=$(printf '%s +' "$ipinfo" | json_get city) + + timezone=$(timedatectl show --property=Timezone --value 2>/dev/null) + [ -n "$timezone" ] || timezone=$(readlink /etc/localtime 2>/dev/null | sed 's#^.*/zoneinfo/##') + [ -n "$timezone" ] || timezone=$(date +%Z) + current_time=$(date '+%Y-%m-%d %I:%M %p') + runtime=$(format_uptime_cn) + + echo "-------------" + printf "%-16s %s\n" "主机名:" "$hostname" + printf "%-16s %s\n" "系统版本:" "$os_info" + printf "%-16s %s\n" "Linux版本:" "$kernel_version" + echo "-------------" + printf "%-16s %s\n" "CPU架构:" "$cpu_arch" + printf "%-16s %s\n" "CPU型号:" "$cpu_model" + printf "%-16s %s\n" "CPU核心数:" "$cpu_cores" + printf "%-16s %s\n" "CPU频率:" "$cpu_freq" + echo "-------------" + printf "%-16s %s%%\n" "CPU占用:" "$cpu_usage" + printf "%-16s %s\n" "系统负载:" "$load_avg" + printf "%-16s %s|%s\n" "TCP|UDP连接数:" "$tcp_count" "$udp_count" + printf "%-16s %s\n" "物理内存:" "$(format_mem_usage "$mem_used" "$mem_total")" + printf "%-16s %s\n" "虚拟内存:" "$(format_swap_usage "$swap_used" "$swap_total")" + printf "%-16s %s\n" "硬盘占用:" "$disk_info" + echo "-------------" + printf "%-16s %s\n" "总接收:" "$rx" + printf "%-16s %s\n" "总发送:" "$tx" + echo "-------------" + printf "%-16s %s %s\n" "网络算法:" "$congestion_algorithm" "$queue_algorithm" + echo "-------------" + printf "%-16s %s\n" "运营商:" "$isp_info" + printf "%-16s %s\n" "IPv4地址:" "$ipv4_address" + printf "%-16s %s\n" "IPv6地址:" "$ipv6_address" + printf "%-16s %s\n" "DNS地址:" "$dns_addresses" + printf "%-16s %s %s\n" "地理位置:" "$country" "$city" + printf "%-16s %s %s\n" "系统时间:" "$timezone" "$current_time" + echo "-------------" + printf "%-16s %s\n" "运行时长:" "$runtime" +} + +main "$@"