ocngx / backup-scripts /backup.sh
tanbushi's picture
update
7f2e1a6
#!/bin/bash
# OCNGX 完整备份脚本
# 支持多环境部署的数据备份和恢复
set -euo pipefail
# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/ocngx}"
BACKUP_DATE=$(date '+%Y%m%d_%H%M%S')
BACKUP_ID="ocngx_backup_${BACKUP_DATE}"
TEMP_BACKUP_DIR="/tmp/${BACKUP_ID}"
# 环境检测
detect_environment() {
echo "🔍 检测运行环境..."
if [[ -f /.dockerenv ]]; then
ENVIRONMENT="docker"
echo " 🐳 Docker 容器环境"
elif [[ -d /app && -f /app/opencode.json ]]; then
ENVIRONMENT="huggingface"
echo " 🤗 HuggingFace Spaces 环境"
else
ENVIRONMENT="local"
echo " 💻 本地环境"
fi
# 检测 OpenCode 安装位置
if command -v opencode &> /dev/null; then
OPENCODE_CMD="opencode"
OPENCODE_DATA_DIR="${HOME}/.opencode"
elif npm list -g opencode-ai &> /dev/null; then
OPENCODE_CMD="opencode"
OPENCODE_DATA_DIR="${HOME}/.opencode"
else
echo " ❌ 未找到 OpenCode 安装"
exit 1
fi
echo " 📂 OpenCode 数据目录: ${OPENCODE_DATA_DIR}"
}
# 创建备份目录
prepare_backup() {
echo "📁 准备备份环境..."
mkdir -p "$BACKUP_DIR"
mkdir -p "$TEMP_BACKUP_DIR"
# 创建备份元数据
cat > "${TEMP_BACKUP_DIR}/backup_metadata.json" << EOF
{
"backup_id": "${BACKUP_ID}",
"backup_date": "$(date -Iseconds)",
"environment": "${ENVIRONMENT}",
"hostname": "$(hostname)",
"opencode_version": "$(opencode --version 2>/dev/null || echo 'unknown')",
"system_info": {
"os": "$(uname -s)",
"arch": "$(uname -m)",
"kernel": "$(uname -r)"
},
"disk_usage": {
"total": "$(df -h / | awk 'NR==2 {print $2}')",
"used": "$(df -h / | awk 'NR==2 {print $3}')",
"available": "$(df -h / | awk 'NR==2 {print $4}')"
}
}
EOF
}
# 备份配置文件
backup_configs() {
echo "⚙️ 备份配置文件..."
local config_dir="${TEMP_BACKUP_DIR}/configs"
mkdir -p "$config_dir"
# 备份项目配置文件
[[ -f "${SCRIPT_DIR}/opencode.json" ]] && cp "${SCRIPT_DIR}/opencode.json" "$config_dir/"
[[ -f "${SCRIPT_DIR}/AGENT.md" ]] && cp "${SCRIPT_DIR}/AGENT.md" "$config_dir/"
[[ -f "${SCRIPT_DIR}/docker-start.sh" ]] && cp "${SCRIPT_DIR}/docker-start.sh" "$config_dir/"
[[ -f "${SCRIPT_DIR}/Dockerfile" ]] && cp "${SCRIPT_DIR}/Dockerfile" "$config_dir/"
# 备份 Nginx 配置
if [[ -d "${SCRIPT_DIR}/nginx" ]]; then
cp -r "${SCRIPT_DIR}/nginx" "$config_dir/"
fi
# 备份 Cron 配置
if [[ -d "${SCRIPT_DIR}/cron-jobs" ]]; then
cp -r "${SCRIPT_DIR}/cron-jobs" "$config_dir/"
fi
# 备份环境变量(排除敏感信息)
env | grep -E '^(OPENCODE_|PUBLIC_|BASE_|GATEWAY_)' > "$config_dir/environment.txt" 2>/dev/null || true
echo " ✅ 配置文件备份完成"
}
# 备份 OpenCode 数据
backup_opencode_data() {
echo "🤖 备份 OpenCode 数据..."
if [[ -d "$OPENCODE_DATA_DIR" ]]; then
local data_dir="${TEMP_BACKUP_DIR}/opencode_data"
mkdir -p "$data_dir"
# 备份核心数据目录
for subdir in "projects" "sessions" "config" "cache" "logs"; do
if [[ -d "${OPENCODE_DATA_DIR}/${subdir}" ]]; then
cp -r "${OPENCODE_DATA_DIR}/${subdir}" "$data_dir/"
echo " 📦 已备份: $subdir"
fi
done
# 备份全局配置文件
[[ -f "${OPENCODE_DATA_DIR}/config.json" ]] && cp "${OPENCODE_DATA_DIR}/config.json" "$data_dir/"
[[ -f "${OPENCODE_DATA_DIR}/preferences.json" ]] && cp "${OPENCODE_DATA_DIR}/preferences.json" "$data_dir/"
echo " ✅ OpenCode 数据备份完成"
else
echo " ⚠️ OpenCode 数据目录不存在,跳过"
fi
}
# 备份运行时状态
backup_runtime_state() {
echo "🔄 备份运行时状态..."
local runtime_dir="${TEMP_BACKUP_DIR}/runtime"
mkdir -p "$runtime_dir"
# 备份当前运行的进程信息
ps aux | grep -E "(opencode|nginx|openresty)" > "$runtime_dir/running_processes.txt" 2>/dev/null || true
# 备份网络连接状态
netstat -tlnp 2>/dev/null | grep -E ":(3000|7860)" > "$runtime_dir/network_state.txt" || true
# 备份最近的日志
if [[ -d "/var/log" ]]; then
find /var/log -name "*nginx*" -o -name "*opencode*" -mtime -7 -exec cp {} "$runtime_dir/" \; 2>/dev/null || true
fi
echo " ✅ 运行时状态备份完成"
}
# 创建备份归档
create_archive() {
echo "🗜️ 创建备份归档..."
local archive_file="${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
# 创建压缩归档
tar -czf "$archive_file" -C "/tmp" "$BACKUP_ID"
# 验证归档
if [[ -f "$archive_file" ]]; then
local archive_size=$(du -h "$archive_file" | cut -f1)
echo " ✅ 备份归档创建成功: ${archive_file} (${archive_size})"
# 生成校验和
sha256sum "$archive_file" > "${archive_file}.sha256"
echo " 🔐 校验和已生成: ${archive_file}.sha256"
else
echo " ❌ 备份归档创建失败"
exit 1
fi
}
# 清理旧备份
cleanup_old_backups() {
echo "🧹 清理旧备份..."
cd "$BACKUP_DIR"
# 保留最近 10 个备份
local backup_count=$(ls -1 ocngx_backup_*.tar.gz 2>/dev/null | wc -l)
if [[ $backup_count -gt 10 ]]; then
echo " 🗑️ 删除超过 10 个的旧备份..."
ls -t ocngx_backup_*.tar.gz 2>/dev/null | tail -n +11 | while read -r old_backup; do
echo " 删除: $old_backup"
rm -f "$old_backup" "${old_backup}.sha256"
done
fi
# 显示备份状态
local remaining_count=$(ls -1 ocngx_backup_*.tar.gz 2>/dev/null | wc -l)
local total_size=$(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1)
echo " 📊 当前备份状态: $remaining_count 个文件, 总大小: $total_size"
}
# 上传到外部存储(可选)
upload_to_external() {
if [[ -n "${BACKUP_S3_BUCKET:-}" ]]; then
echo "☁️ 上传备份到 S3..."
local archive_file="${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
if command -v aws &> /dev/null; then
aws s3 cp "$archive_file" "s3://${BACKUP_S3_BUCKET}/ocngx-backups/" --storage-class STANDARD_IA
aws s3 cp "${archive_file}.sha256" "s3://${BACKUP_S3_BUCKET}/ocngx-backups/"
echo " ✅ S3 上传完成"
else
echo " ⚠️ AWS CLI 未安装,跳过 S3 上传"
fi
fi
}
# 主函数
main() {
echo "🚀 OCNGX 完整备份开始..."
echo "📅 备份时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "🆔 备份ID: ${BACKUP_ID}"
echo ""
detect_environment
prepare_backup
backup_configs
backup_opencode_data
backup_runtime_state
create_archive
cleanup_old_backups
upload_to_external
# 清理临时目录
rm -rf "$TEMP_BACKUP_DIR"
echo ""
echo "✅ 备份完成!"
echo "📁 备份位置: ${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
echo "🔍 验证命令: tar -tzf ${BACKUP_DIR}/${BACKUP_ID}.tar.gz | head -10"
}
# 执行主函数
main "$@"