diff --git a/PVE-Tools.sh b/PVE-Tools.sh index 813a4cf..3ca3882 100644 --- a/PVE-Tools.sh +++ b/PVE-Tools.sh @@ -14,26 +14,270 @@ PVE_TOOLS_REMOTE_BASE="${PVE_TOOLS_REMOTE_BASE:-https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main}" PVE_TOOLS_REMOTE_MIRROR_PREFIX="${PVE_TOOLS_REMOTE_MIRROR_PREFIX:-https://ghfast.top/}" PVE_TOOLS_REMOTE_DIST_URL="${PVE_TOOLS_REMOTE_DIST_URL:-$PVE_TOOLS_REMOTE_BASE/dist/PVE-Tools.sh}" +PVE_TOOLS_CONNECT_TIMEOUT="${PVE_TOOLS_CONNECT_TIMEOUT:-10}" +PVE_TOOLS_DOWNLOAD_TIMEOUT="${PVE_TOOLS_DOWNLOAD_TIMEOUT:-120}" +PVE_TOOLS_DOWNLOAD_RETRIES="${PVE_TOOLS_DOWNLOAD_RETRIES:-2}" + +PVE_TOOLS_RELEASE_PAGE_URL="https://github.com/PVE-Tools/PVE-Tools-9/releases/tag/v10.2.0" +PVE_TOOLS_RELEASE_ASSET_URL="https://github.com/PVE-Tools/PVE-Tools-9/releases/download/v10.2.0/PVE-Tools.sh" +PVE_TOOLS_ENTRY_LAST_ERROR="" + +pve_tools_entry_normalize_positive_integer() { + local variable_name="$1" + local default_value="$2" + local value="${!variable_name:-}" + + if [[ ! "$value" =~ ^[1-9][0-9]*$ ]]; then + echo "警告:$variable_name 必须是正整数,已使用默认值 $default_value。" >&2 + printf -v "$variable_name" '%s' "$default_value" + fi +} + +pve_tools_entry_curl_error() { + local status="$1" + local http_code="${2:-}" + + case "$status" in + 5|6) PVE_TOOLS_ENTRY_LAST_ERROR="DNS 解析失败(curl $status)" ;; + 7) PVE_TOOLS_ENTRY_LAST_ERROR="无法连接下载服务器(curl $status)" ;; + 18) PVE_TOOLS_ENTRY_LAST_ERROR="下载内容不完整(curl $status)" ;; + 22) + if [[ "$http_code" =~ ^[1-9][0-9][0-9]$ ]]; then + PVE_TOOLS_ENTRY_LAST_ERROR="服务器返回 HTTP $http_code(curl $status)" + else + PVE_TOOLS_ENTRY_LAST_ERROR="服务器返回 HTTP 错误(curl $status)" + fi + ;; + 23) PVE_TOOLS_ENTRY_LAST_ERROR="无法写入临时文件(curl $status)" ;; + 28) PVE_TOOLS_ENTRY_LAST_ERROR="连接或下载超时(curl $status)" ;; + 35|51|60) PVE_TOOLS_ENTRY_LAST_ERROR="TLS 证书或握手失败(curl $status)" ;; + 47) PVE_TOOLS_ENTRY_LAST_ERROR="服务器重定向次数过多(curl $status)" ;; + 56) PVE_TOOLS_ENTRY_LAST_ERROR="接收下载数据失败(curl $status)" ;; + 124) PVE_TOOLS_ENTRY_LAST_ERROR="下载超过 ${PVE_TOOLS_DOWNLOAD_TIMEOUT} 秒(curl $status)" ;; + *) PVE_TOOLS_ENTRY_LAST_ERROR="curl 下载失败(错误码 $status)" ;; + esac +} + +pve_tools_entry_wget_error() { + local status="$1" + + case "$status" in + 3) PVE_TOOLS_ENTRY_LAST_ERROR="无法写入临时文件(wget $status)" ;; + 4) PVE_TOOLS_ENTRY_LAST_ERROR="网络连接失败(wget $status)" ;; + 5) PVE_TOOLS_ENTRY_LAST_ERROR="TLS 证书或握手失败(wget $status)" ;; + 6) PVE_TOOLS_ENTRY_LAST_ERROR="服务器认证失败(wget $status)" ;; + 7) PVE_TOOLS_ENTRY_LAST_ERROR="服务器协议错误(wget $status)" ;; + 8) PVE_TOOLS_ENTRY_LAST_ERROR="服务器返回错误状态(wget $status)" ;; + 124) PVE_TOOLS_ENTRY_LAST_ERROR="下载超过 ${PVE_TOOLS_DOWNLOAD_TIMEOUT} 秒(wget $status)" ;; + *) PVE_TOOLS_ENTRY_LAST_ERROR="wget 下载失败(错误码 $status)" ;; + esac +} + +pve_tools_entry_download_with_curl() { + local url="$1" + local output="$2" + local status=0 + local http_code="" + local retry_count=$((PVE_TOOLS_DOWNLOAD_RETRIES - 1)) + local -a curl_args=( + --fail + --location + --show-error + --connect-timeout "$PVE_TOOLS_CONNECT_TIMEOUT" + --max-time "$PVE_TOOLS_DOWNLOAD_TIMEOUT" + --speed-limit 1 + --speed-time 20 + --retry "$retry_count" + --retry-delay 1 + --retry-connrefused + --output "$output" + --write-out '%{http_code}' + ) + + if [[ -t 2 ]]; then + curl_args+=(--progress-bar) + else + curl_args+=(--silent) + fi + + if command -v timeout >/dev/null 2>&1; then + if http_code="$(timeout --kill-after=5s "$PVE_TOOLS_DOWNLOAD_TIMEOUT" curl "${curl_args[@]}" "$url")"; then + return 0 + else + status=$? + fi + elif http_code="$(curl "${curl_args[@]}" "$url")"; then + return 0 + else + status=$? + fi + + pve_tools_entry_curl_error "$status" "$http_code" + return "$status" +} + +pve_tools_entry_download_with_wget() { + local url="$1" + local output="$2" + local status=0 + local -a wget_args=( + --connect-timeout="$PVE_TOOLS_CONNECT_TIMEOUT" + --read-timeout=20 + --dns-timeout="$PVE_TOOLS_CONNECT_TIMEOUT" + --tries="$PVE_TOOLS_DOWNLOAD_RETRIES" + --waitretry=1 + -O "$output" + ) + + if [[ -t 2 ]]; then + wget_args+=(--show-progress --progress=bar:force:noscroll) + else + wget_args+=(--no-verbose) + fi + + if command -v timeout >/dev/null 2>&1; then + if timeout --kill-after=5s "$PVE_TOOLS_DOWNLOAD_TIMEOUT" wget "${wget_args[@]}" "$url"; then + return 0 + else + status=$? + fi + elif wget "${wget_args[@]}" "$url"; then + return 0 + else + status=$? + fi + + pve_tools_entry_wget_error "$status" + return "$status" +} + +pve_tools_entry_validate_script() { + local script_path="$1" + + if [[ ! -s "$script_path" ]]; then + PVE_TOOLS_ENTRY_LAST_ERROR="下载文件为空" + return 1 + fi + if ! bash -n "$script_path" >/dev/null 2>&1; then + PVE_TOOLS_ENTRY_LAST_ERROR="下载内容不是有效的 Bash 脚本,可能是代理错误页" + return 1 + fi + if ! grep -q '^CURRENT_VERSION=' "$script_path"; then + PVE_TOOLS_ENTRY_LAST_ERROR="下载内容不是 PVE-Tools 主程序完整版" + return 1 + fi + + return 0 +} pve_tools_entry_download_file() { local url="$1" local output="$2" + local output_dir="" + local part_file="${output}.part" + local mirror_url="" + local downloaded_bytes="" + local download_status=0 + local index=0 + local source_count=0 + local source_name="" + local source_url="" + local -a source_names=("GitHub 原始源") + local -a source_urls=("$url") - mkdir -p "$(dirname "$output")" - if command -v curl >/dev/null 2>&1; then - curl -fsSL "$url" -o "$output" && return 0 - curl -fsSL "${PVE_TOOLS_REMOTE_MIRROR_PREFIX}${url}" -o "$output" && return 0 - elif command -v wget >/dev/null 2>&1; then - wget -qO "$output" "$url" && return 0 - wget -qO "$output" "${PVE_TOOLS_REMOTE_MIRROR_PREFIX}${url}" && return 0 - else - echo "错误:未找到 curl 或 wget,无法下载 PVE-Tools。" >&2 + output_dir="$(dirname "$output")" + if ! mkdir -p "$output_dir"; then + PVE_TOOLS_ENTRY_LAST_ERROR="无法创建临时下载目录:$output_dir" + echo "错误:$PVE_TOOLS_ENTRY_LAST_ERROR" >&2 return 1 fi + if [[ -n "$PVE_TOOLS_REMOTE_MIRROR_PREFIX" && "$url" != "${PVE_TOOLS_REMOTE_MIRROR_PREFIX}"* ]]; then + mirror_url="${PVE_TOOLS_REMOTE_MIRROR_PREFIX}${url}" + source_names+=("GitHub 加速源") + source_urls+=("$mirror_url") + fi + + if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then + PVE_TOOLS_ENTRY_LAST_ERROR="未找到 curl 或 wget" + echo "错误:未找到 curl 或 wget,无法下载 PVE-Tools。" >&2 + echo "请先执行:apt update && apt install -y curl" >&2 + return 1 + fi + + source_count="${#source_urls[@]}" + for ((index = 0; index < source_count; index++)); do + source_name="${source_names[$index]}" + source_url="${source_urls[$index]}" + rm -f -- "$part_file" + + echo "[$((index + 1))/$source_count] 正在通过${source_name}下载主程序完整版..." + echo "下载地址:$source_url" + + if command -v curl >/dev/null 2>&1; then + if pve_tools_entry_download_with_curl "$source_url" "$part_file"; then + download_status=0 + else + download_status=$? + fi + elif pve_tools_entry_download_with_wget "$source_url" "$part_file"; then + download_status=0 + else + download_status=$? + fi + + if [[ "$download_status" -eq 0 ]]; then + echo "下载完成,正在校验脚本完整性..." + if pve_tools_entry_validate_script "$part_file"; then + if ! mv -f -- "$part_file" "$output"; then + PVE_TOOLS_ENTRY_LAST_ERROR="无法保存已下载的主程序" + else + downloaded_bytes="$(wc -c < "$output")" + downloaded_bytes="${downloaded_bytes//[[:space:]]/}" + echo "${source_name}下载成功:${downloaded_bytes:-未知} 字节。" + return 0 + fi + fi + fi + + rm -f -- "$part_file" + echo "${source_name}下载失败:$PVE_TOOLS_ENTRY_LAST_ERROR" >&2 + if ((index + 1 < source_count)); then + echo "将自动切换到下一个下载源..." >&2 + fi + done + return 1 } +pve_tools_entry_print_release_help() { + cat >&2 <&2 + exit 1 + fi + trap pve_tools_entry_cleanup EXIT if pve_tools_entry_download_file "$PVE_TOOLS_REMOTE_DIST_URL" "$tmp_dir/PVE-Tools.sh"; then + echo "主程序校验通过,正在启动 PVE-Tools..." bash "$tmp_dir/PVE-Tools.sh" "$@" exit $? fi - echo "错误:无法下载 PVE-Tools。请检查网络连接或稍后重试。" >&2 - echo "下载地址: $PVE_TOOLS_REMOTE_DIST_URL" >&2 + pve_tools_entry_print_release_help exit 1 fi diff --git a/VERSION b/VERSION index 4149c39..2bd6f7e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.1.0 +10.2.0 diff --git a/dist/PVE-Tools.sh b/dist/PVE-Tools.sh index ae91692..c7ec5ce 100644 --- a/dist/PVE-Tools.sh +++ b/dist/PVE-Tools.sh @@ -1,7 +1,7 @@ #!/bin/bash -# PVE-Tools Pro v10.1.0 -# Build: 2026-07-08T07:56:40Z +# PVE-Tools Pro v10.2.0 +# Build: 2026-07-11T12:09:24Z # SPDX-License-Identifier: GPL-3.0-only # Copyright (C) 2026 Ciriu Networks @@ -11,7 +11,7 @@ # Copyright (C) 2026 Ciriu Networks # 版本信息 -CURRENT_VERSION="10.1.0" +CURRENT_VERSION="10.2.0" BUILD_NICKNAME="Evanescia" VERSION_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/VERSION" UPDATE_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/UPDATE" @@ -1386,6 +1386,8 @@ show_menu() { show_menu_footer echo echo -e " ${YELLOW}Tips: ${SESSION_TIP:-一言获取失败,本次会话不再重试。}${NC}" + echo -e "本项目正在收集用户意见,如您愿意,请前往填写问卷,这能帮到整个项目!" + echo -e "-> https://wj.qq.com/s2/27286538/9d9d/" echo echo -ne " ${PRIMARY}请输入您的选择 [0-10]: ${NC}" } diff --git a/lib/config.sh b/lib/config.sh index 884d922..0c3e25b 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -3,7 +3,7 @@ # Copyright (C) 2026 Ciriu Networks # 版本信息 -CURRENT_VERSION="10.1.0" +CURRENT_VERSION="10.2.0" BUILD_NICKNAME="Evanescia" VERSION_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/VERSION" UPDATE_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/UPDATE" diff --git a/lib/runtime.sh b/lib/runtime.sh index 2cc1fc0..3979831 100644 --- a/lib/runtime.sh +++ b/lib/runtime.sh @@ -158,6 +158,8 @@ show_menu() { show_menu_footer echo echo -e " ${YELLOW}Tips: ${SESSION_TIP:-一言获取失败,本次会话不再重试。}${NC}" + echo -e "本项目正在收集用户意见,如您愿意,请前往填写问卷,这能帮到整个项目!" + echo -e "-> https://wj.qq.com/s2/27286538/9d9d/" echo echo -ne " ${PRIMARY}请输入您的选择 [0-10]: ${NC}" }