Files
shell-scripts/0-部署应用/CloudCone-备份中心/b-vault-warden-dsv3.1.sh
2025-09-01 16:52:17 +08:00

343 lines
8.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# =============================================================================
# b-vault-warden备份脚本
# 功能远程执行Vaultwarden备份、同步备份文件、加密压缩及清理
# 版本1.0.0
# 作者Shell脚本工程师
# 许可证MIT License
# 依赖ssh, rsync, 7zip, docker (远程主机)
# =============================================================================
set -euo pipefail
IFS=$'\n\t'
# > 全局常量定义
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly LOCK_FILE="/tmp/${SCRIPT_NAME}.lock"
# > 配置参数(可根据需要调整为环境变量)
readonly REMOTE_HOST="s5"
readonly REMOTE_PORT="22333"
readonly REMOTE_USER="root"
readonly REMOTE_BACKUP_CMD="docker exec vault-warden /vaultwarden backup"
readonly REMOTE_DATA_DIR="/data/vault-warden/persist-data"
readonly LOCAL_STAGE_DIR="/tmp/vault_warden_backup_stage"
readonly LOCAL_BACKUP_DIR="${SCRIPT_DIR}/backups"
readonly BACKUP_PATTERNS=(
"config.json"
"rsa_key*"
"attachments"
"sends"
"db_*.sqlite3"
)
readonly ENCRYPTION_PASSWORD="your_encryption_password_here" # > 请在实际使用时修改
# > 日志配置
readonly LOG_DIR="${SCRIPT_DIR}/logs"
readonly LOG_FILE="${LOG_DIR}/backup_$(date +%Y%m%d).log"
# > 颜色输出定义
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'
# =============================================================================
# 日志函数集
# =============================================================================
###
# 初始化日志系统
# @require 无
# @return 0 成功 | >0 失败
###
init_log_system() {
mkdir -p "${LOG_DIR}" || return 1
touch "${LOG_FILE}" || return 1
return 0
}
###
# 记录日志消息
# @param level string 日志级别DEBUG/INFO/WARN/ERROR
# @param message string 日志消息
# @require LOG_FILE
# @return 0 成功
###
log_message() {
local level="$1"
local message="$2"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "${level}" in
"DEBUG") echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;;
"INFO") echo -e "${GREEN}[INFO]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;;
"WARN") echo -e "${YELLOW}[WARN]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" >&2 ;;
"ERROR") echo -e "${RED}[ERROR]${NC} ${timestamp} - ${message}" | tee -a "${LOG_FILE}" >&2 ;;
*) echo "${timestamp} - ${message}" | tee -a "${LOG_FILE}" ;;
esac
return 0
}
# =============================================================================
# 工具函数集
# =============================================================================
###
# 检查命令是否存在
# @param command_name string 命令名称
# @require 无
# @return 0 存在 | 1 不存在
###
check_command() {
local command_name="$1"
if ! command -v "${command_name}" >/dev/null 2>&1; then
log_message "ERROR" "命令不存在: ${command_name}"
return 1
fi
return 0
}
###
# 执行远程SSH命令
# @param command string 要执行的命令
# @require REMOTE_HOST, REMOTE_PORT, REMOTE_USER
# @return 远程命令的退出码
###
execute_remote_command() {
local command="$1"
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "${command}"
return $?
}
###
# 创建锁文件防止并发执行
# @require LOCK_FILE
# @return 0 成功获取锁 | 1 锁已存在
###
acquire_lock() {
if [ -e "${LOCK_FILE}" ]; then
log_message "ERROR" "备份任务正在运行或异常退出,请检查锁文件: ${LOCK_FILE}"
return 1
fi
echo "$$" > "${LOCK_FILE}"
trap 'release_lock' EXIT
return 0
}
###
# 释放锁文件
# @require LOCK_FILE
# @return 0 成功
###
release_lock() {
[ -e "${LOCK_FILE}" ] && rm -f "${LOCK_FILE}"
return 0
}
# =============================================================================
# 核心备份函数
# =============================================================================
###
# 远程执行Vaultwarden备份命令
# @require execute_remote_command, REMOTE_BACKUP_CMD
# @return 0 成功 | >0 失败
###
remote_execute_backup() {
log_message "INFO" "开始在远程主机执行Vaultwarden备份..."
if ! execute_remote_command "${REMOTE_BACKUP_CMD}"; then
log_message "ERROR" "远程备份命令执行失败"
return 1
fi
log_message "INFO" "远程备份命令执行成功"
return 0
}
###
# 使用rsync同步备份文件到本地
# @require REMOTE_HOST, REMOTE_PORT, REMOTE_USER, REMOTE_DATA_DIR, LOCAL_STAGE_DIR, BACKUP_PATTERNS
# @return 0 成功 | >0 失败
###
sync_backup_files() {
log_message "INFO" "开始同步备份文件到本地..."
# > 创建本地暂存目录
mkdir -p "${LOCAL_STAGE_DIR}" || {
log_message "ERROR" "创建本地暂存目录失败: ${LOCAL_STAGE_DIR}"
return 1
}
# > 构建rsync命令
local rsync_cmd="rsync -avz --progress -e 'ssh -p ${REMOTE_PORT}'"
for pattern in "${BACKUP_PATTERNS[@]}"; do
rsync_cmd+=" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DATA_DIR}/${pattern}"
done
rsync_cmd+=" ${LOCAL_STAGE_DIR}/"
# > 执行rsync同步
if ! eval "${rsync_cmd}"; then
log_message "ERROR" "文件同步失败"
return 1
fi
log_message "INFO" "文件同步完成"
return 0
}
###
# 使用7zip加密压缩备份文件
# @require LOCAL_STAGE_DIR, LOCAL_BACKUP_DIR, ENCRYPTION_PASSWORD
# @return 0 成功 | >0 失败
###
encrypt_and_compress() {
log_message "INFO" "开始加密压缩备份文件..."
# > 检查7zip命令
if ! check_command "7z"; then
log_message "ERROR" "7zip命令不存在请安装p7zip-full包"
return 1
fi
# > 创建备份目录
mkdir -p "${LOCAL_BACKUP_DIR}" || {
log_message "ERROR" "创建备份目录失败: ${LOCAL_BACKUP_DIR}"
return 1
}
local backup_file="${LOCAL_BACKUP_DIR}/vaultwarden-backup-$(date +%Y%m%d-%H%M%S).7z"
# > 执行加密压缩
if ! (cd "${LOCAL_STAGE_DIR}" && 7z a -p"${ENCRYPTION_PASSWORD}" -mhe=on "${backup_file}" . >/dev/null); then
log_message "ERROR" "加密压缩失败"
return 1
fi
log_message "INFO" "加密压缩完成: ${backup_file}"
return 0
}
###
# 远程删除备份数据库文件
# @require execute_remote_command, REMOTE_DATA_DIR
# @return 0 成功 | >0 失败
###
remote_cleanup_backup() {
log_message "INFO" "开始清理远程备份文件..."
local cleanup_cmd="rm -rf ${REMOTE_DATA_DIR}/db_*.sqlite3"
if ! execute_remote_command "${cleanup_cmd}"; then
log_message "ERROR" "远程清理失败"
return 1
fi
log_message "INFO" "远程清理完成"
return 0
}
###
# 清理本地暂存目录
# @require LOCAL_STAGE_DIR
# @return 0 成功
###
local_cleanup() {
log_message "INFO" "清理本地暂存目录..."
[ -d "${LOCAL_STAGE_DIR}" ] && rm -rf "${LOCAL_STAGE_DIR}"
return 0
}
# =============================================================================
# 主执行流程
# =============================================================================
###
# 主备份流程
# @require 所有上述函数
# @return 0 成功 | >0 失败
###
main_backup_process() {
log_message "INFO" "=== 开始Vaultwarden备份任务 ==="
# > 检查依赖命令
local required_commands=("ssh" "rsync" "7z")
for cmd in "${required_commands[@]}"; do
if ! check_command "${cmd}"; then
return 1
fi
done
# > 执行备份流程
local steps=(
remote_execute_backup
sync_backup_files
encrypt_and_compress
remote_cleanup_backup
local_cleanup
)
for step in "${steps[@]}"; do
if ! "${step}"; then
log_message "ERROR" "备份任务在第 ${#steps[@]} 步失败"
return 1
fi
done
log_message "INFO" "=== Vaultwarden备份任务完成 ==="
return 0
}
# =============================================================================
# 脚本入口点
# =============================================================================
# > 设置错误处理
trap 'log_message "ERROR" "脚本异常退出"; release_lock; exit 1' ERR
# > 主执行块
main() {
if ! acquire_lock; then
exit 1
fi
if ! init_log_system; then
log_message "ERROR" "日志系统初始化失败"
exit 1
fi
if ! main_backup_process; then
log_message "ERROR" "备份任务执行失败"
exit 1
fi
release_lock
exit 0
}
# > 脚本执行入口
main "$@"
# =============================================================================
# 函数调用关系图
# =============================================================================
# main
# ├── acquire_lock
# ├── init_log_system
# └── main_backup_process
# ├── check_command (多次调用)
# ├── remote_execute_backup
# │ └── execute_remote_command
# ├── sync_backup_files
# ├── encrypt_and_compress
# │ └── check_command
# ├── remote_cleanup_backup
# │ └── execute_remote_command
# └── local_cleanup