完成CloudCone备份服务器的设置

This commit is contained in:
zeaslity
2025-09-03 14:14:19 +08:00
parent b5e802ebc3
commit 9d93a1ee6e
20 changed files with 1434 additions and 98 deletions

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env bash
# =============================================================================
# Meta : 公共函数与变量库
# Version : 1.0.0
# Author : Bash Shell Senior Development Engineer
# License : MIT
# Description : 为备份脚本体系提供标准化的日志、远程执行、加解密及云存储管理功能。
# =============================================================================
#------------------------------------------------------------------------------
# 脚本严格模式
# -e: 命令失败时立即退出
# -u: 变量未定义时报错
# -o pipefail: 管道中任一命令失败则整个管道失败
#------------------------------------------------------------------------------
set -euo pipefail
IFS=$'\n\t'
#------------------------------------------------------------------------------
# 全局常量定义区
#------------------------------------------------------------------------------
# > 基础路径配置
readonly SCRIPT_RUN_DIR="/root/wdd/backup"
readonly LOG_DIR="/root/wdd/backup/logs"
# > 通用配置
readonly REMOTE_SSH_PORT="22333"
readonly ENCRYPTION_PASSWORD_7ZIP="SuperWdd.CCC.123" # !!!请务必修改为强密码!!!
readonly RCLONE_REMOTE_REPO="gd-zeaslity:CloneCone-BackUp" # rclone配置的远程仓库名及路径
# > 日志级别常量
readonly LOG_LEVEL_DEBUG=0
readonly LOG_LEVEL_INFO=1
readonly LOG_LEVEL_WARN=2
readonly LOG_LEVEL_ERROR=3
# > 默认日志级别 (可被调用脚本覆盖)
CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO}
# > 颜色输出定义
readonly C_RED='\033[0;31m'
readonly C_GREEN='\033[0;32m'
readonly C_YELLOW='\033[1;33m'
readonly C_BLUE='\033[0;34m'
readonly C_NC='\033[0m'
#------------------------------------------------------------------------------
# 模块依赖检查
#------------------------------------------------------------------------------
if ! command -v 7z &> /dev/null || ! command -v rclone &> /dev/null || ! command -v ssh &> /dev/null; then
echo -e "${C_RED}[ERROR] Essential commands (7z, rclone, ssh) are not installed. Aborting.${C_NC}" >&2
exit 1
fi
# =============================================================================
# 函数定义区
# =============================================================================
###
# 功能描述段: 记录标准化的分级日志
# @param level <string> 日志级别 (DEBUG/INFO/WARN/ERROR)
# @param message <string> 要记录的日志消息
# @return <0> 成功
# @require LOG_DIR, CURRENT_LOG_LEVEL
###
log_message() {
local level="$1"
local message="$2"
local log_level_value
local log_file
log_file="${LOG_DIR}/backup_$(date +%Y%m%d).log"
mkdir -p "${LOG_DIR}"
case "${level}" in
"DEBUG") log_level_value=${LOG_LEVEL_DEBUG} ;;
"INFO") log_level_value=${LOG_LEVEL_INFO} ;;
"WARN") log_level_value=${LOG_LEVEL_WARN} ;;
"ERROR") log_level_value=${LOG_LEVEL_ERROR} ;;
*) log_level_value=${LOG_LEVEL_INFO} ;;
esac
if [[ ${CURRENT_LOG_LEVEL} -le ${log_level_value} ]]; then
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local color_prefix="${C_GREEN}"
case "${level}" in
"DEBUG") color_prefix="${C_BLUE}" ;;
"INFO") color_prefix="${C_GREEN}" ;;
"WARN") color_prefix="${C_YELLOW}" ;;
"ERROR") color_prefix="${C_RED}" ;;
esac
# > 格式化日志条目
local log_entry
log_entry=$(printf "[%-5s] %s: %s" "${level}" "${timestamp}" "${message}")
# > 输出到标准输出/错误
echo -e "${color_prefix}${log_entry}${C_NC}"
# > INFO及以上级别写入日志文件
if [[ ${log_level_value} -ge ${LOG_LEVEL_INFO} ]]; then
echo "${log_entry}" >> "${log_file}"
fi
fi
return 0
}
###
# 功能描述段: 通过SSH在远程主机上安全地执行命令
# @param remote_user <string> 远程主机用户名
# @param remote_host <string> 远程主机名或IP地址
# @param remote_command <string> 待执行的命令
# @param ssh_port <string> SSH端口 (可选, 默认22333)
# @return <exit_code> 远程命令的退出码
# @require REMOTE_SSH_PORT, ssh client
###
execute_remote_command() {
local remote_user="$1"
local remote_host="$2"
local remote_command="$3"
local ssh_port=${4:-${REMOTE_SSH_PORT}}
log_message "DEBUG" "Executing on [${remote_user}@${remote_host}:${ssh_port}]: ${remote_command}"
ssh -p "${ssh_port}" "${remote_user}@${remote_host}" "${remote_command}"
local exit_code=$?
if [[ ${exit_code} -ne 0 ]]; then
log_message "ERROR" "Remote command failed with exit code ${exit_code}."
return ${exit_code}
fi
log_message "DEBUG" "Remote command executed successfully."
return 0
}
###
# 功能描述段: 使用7zip加密并压缩指定目录
# @param source_directory <string> 需要压缩的源目录路径
# @param archive_path <string> 生成的加密压缩包完整路径
# @return <0> 成功 | >0 失败
# @require ENCRYPTION_PASSWORD_7ZIP, 7z command
###
encrypt_with_7zip() {
local source_directory="$1"
local archive_path="$2"
if [[ ! -d "${source_directory}" ]]; then
log_message "ERROR" "Source directory for encryption does not exist: ${source_directory}"
return 1
fi
log_message "INFO" "Encrypting '${source_directory}' to '${archive_path}'..."
# > -mhe=on: 加密文件头, 防止泄露文件列表
# > -p: 指定密码
7z a -mhe=on -p"${ENCRYPTION_PASSWORD_7ZIP}" "${archive_path}" "${source_directory}"/*
local exit_code=$?
if [[ ${exit_code} -ne 0 ]]; then
log_message "ERROR" "7zip encryption failed with exit code ${exit_code}."
return ${exit_code}
fi
log_message "INFO" "Encryption completed successfully."
return 0
}
###
# 功能描述段: 使用rclone将本地文件复制到远程仓库
# @param source_file <string> 本地源文件路径
# @param remote_destination <string> rclone远程目标路径 (e.g., "google-drive:backup/app1/")
# @return <0> 成功 | >0 失败
# @require rclone command
###
rclone_copy() {
local source_file="$1"
local remote_destination="$2"
if [[ ! -f "${source_file}" ]]; then
log_message "ERROR" "Source file for rclone copy does not exist: ${source_file}"
return 1
fi
log_message "INFO" "Copying '${source_file}' to remote '${remote_destination}'..."
rclone copy -P "${source_file}" "${remote_destination}"
local exit_code=$?
if [[ ${exit_code} -ne 0 ]]; then
log_message "ERROR" "rclone copy failed with exit code ${exit_code}."
return ${exit_code}
fi
log_message "INFO" "rclone copy completed successfully."
return 0
}
###
# 功能描述段: 控制rclone远程仓库中的副本数量删除最旧的副本
# @param remote_path <string> 远程仓库中的目录路径
# @param file_prefix <string> 需要管理副本数量的文件名前缀
# @param max_replicas <integer> 允许保留的最大副本数量
# @return <0> 成功 | >0 失败
# @require rclone command
###
rclone_control_replicas() {
local remote_path="$1"
local file_prefix="$2"
local max_replicas="$3"
log_message "INFO" "Checking replicas for '${file_prefix}*' in '${remote_path}'. Max allowed: ${max_replicas}."
# > 获取远程文件列表及其修改时间
local remote_files
remote_files=$(rclone lsf --format "tp" "${remote_path}" | grep "${file_prefix}" || true)
if [[ -z "${remote_files}" ]]; then
log_message "INFO" "No remote files found with prefix '${file_prefix}'. Nothing to do."
return 0
fi
local file_count
file_count=$(echo "${remote_files}" | wc -l)
if [[ ${file_count} -le ${max_replicas} ]]; then
log_message "INFO" "Current replica count (${file_count}) is within the limit (${max_replicas})."
return 0
fi
local files_to_delete_count
files_to_delete_count=$((file_count - max_replicas))
log_message "WARN" "Exceeding replica limit. Need to delete ${files_to_delete_count} oldest file(s)."
# > 按时间排序并提取需要删除的文件名
local files_to_delete
files_to_delete=$(echo "${remote_files}" | sort -k2 | head -n "${files_to_delete_count}" | awk -F';' '{print $1}')
for file in ${files_to_delete}; do
log_message "INFO" "Deleting oldest replica: ${file}"
rclone deletefile "${remote_path}/${file}"
if [[ $? -ne 0 ]]; then
log_message "ERROR" "Failed to delete remote file: ${file}"
# > 继续尝试删除其他文件,不立即失败
fi
done
log_message "INFO" "Replica control process finished."
return 0
}
###
# 功能描述段: 清理指定目录下的所有.7z加密压缩包
# @param target_directory <string> 需要清理的目录路径
# @return <0> 成功
# @require find command
###
cleanup_local_encrypted_files() {
local target_directory="$1"
log_message "INFO" "Cleaning up local encrypted files (*.7z) in '${target_directory}'..."
find "${target_directory}" -maxdepth 1 -type f -name "*.7z" -delete
log_message "INFO" "Local cleanup finished."
return 0
}