File size: 7,398 Bytes
7f2e1a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#!/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 "$@"