tanbushi commited on
Commit
7f2e1a6
·
1 Parent(s): 701ccad
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
BACKUP_RECOVERY_GUIDE.md ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OCNGX 备份和恢复方案
2
+
3
+ ## 概述
4
+
5
+ OCNGX(OpenCode + Nginx)备份和恢复方案支持在同一个版本的不同 HuggingFace Space 之间进行无缝迁移。该方案确保数据完整性、配置一致性和服务连续性。
6
+
7
+ ## 系统架构
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ OCNGX System │
12
+ ├─────────────────────────────────────────────────────────────┤
13
+ │ Frontend (Nginx:7860) │ Backend (OpenCode:3000) │
14
+ ├─────────────────────────────────────────────────────────────┤
15
+ │ 配置文件 │ 用户数据 │
16
+ │ - opencode.json │ - projects/ │
17
+ │ - nginx/配置 │ - sessions/ │
18
+ │ - AGENT.md │ - config/ │
19
+ │ - 启动脚本 │ - cache/ │
20
+ ├─────────────────────────────────────────────────────────────┤
21
+ │ 运行时数据 │ 日志 │
22
+ │ - 进程状态 │ - nginx日志 │
23
+ │ - 网络连接 │ - opencode日志 │
24
+ └─────────────────────────────────────────────────────────────┘
25
+ ```
26
+
27
+ ## 备份内容
28
+
29
+ ### 1. 核心配置文件
30
+ - `opencode.json` - OpenCode 主配置
31
+ - `AGENT.md` - 系统代理配置
32
+ - `docker-start.sh` - 容器启动脚本
33
+ - `Dockerfile` - 容器构建文件
34
+ - `nginx/` - Nginx 配置目录
35
+ - `cron-jobs/` - 定时任务配置
36
+
37
+ ### 2. OpenCode 数据
38
+ - `~/.opencode/projects/` - 项目数据
39
+ - `~/.opencode/sessions/` - 会话数据
40
+ - `~/.opencode/config/` - 用户配置
41
+ - `~/.opencode/cache/` - 缓存数据
42
+ - `~/.opencode/logs/` - 运行日志
43
+
44
+ ### 3. 运行时状态
45
+ - 当前运行的进程信息
46
+ - 网络连接状态
47
+ - 最近的系统日志
48
+
49
+ ### 4. 元数据
50
+ - 备份时间戳
51
+ - 环境信息
52
+ - 系统版本信息
53
+ - 磁盘使用情况
54
+
55
+ ## 使用指南
56
+
57
+ ### 1. 快速开始
58
+
59
+ #### 创建备份
60
+ ```bash
61
+ # 执行完整备份
62
+ ./backup-scripts/backup.sh
63
+
64
+ # 查看备份文件
65
+ ls -la /var/backups/ocngx/
66
+ ```
67
+
68
+ #### 恢复系统
69
+ ```bash
70
+ # 恢复到新环境
71
+ ./backup-scripts/restore.sh /path/to/backup.tar.gz
72
+
73
+ # 强制覆盖现有配置
74
+ ./backup-scripts/restore.sh --force backup.tar.gz
75
+
76
+ # 跳过配置文件恢复
77
+ ./backup-scripts/restore.sh --skip-config backup.tar.gz
78
+ ```
79
+
80
+ ### 2. 部署到新环境
81
+
82
+ #### 自动配置部署环境
83
+ ```bash
84
+ # 自动检测并配置当前环境
85
+ source deploy-config.sh
86
+ configure_for_deployment
87
+
88
+ # 验证配置
89
+ verify_configuration
90
+ ```
91
+
92
+ #### 手动部署到 HuggingFace Spaces
93
+ ```bash
94
+ # 1. 克隆项目到新 Space
95
+ git clone <项目地址> .
96
+ cd <项目目录>
97
+
98
+ # 2. 配置环境变量
99
+ export SPACE_ID="your-space-name"
100
+ export ADMIN_USERNAME="your-admin-user"
101
+ export ADMIN_PASSWORD="your-admin-password"
102
+
103
+ # 3. 自动配置
104
+ source deploy-config.sh
105
+ configure_for_deployment
106
+
107
+ # 4. 恢复数据(如果有备份)
108
+ ./backup-scripts/restore.sh backup.tar.gz
109
+
110
+ # 5. 启动服务
111
+ docker-compose up -d
112
+ ```
113
+
114
+ ### 3. 使用 Docker Compose
115
+
116
+ #### 启动完整服务
117
+ ```bash
118
+ # 使用默认配置启动
119
+ docker-compose up -d
120
+
121
+ # 使用自定义环境变量
122
+ OPENCODE_PUBLIC_URL=https://your-space.hf.space \
123
+ docker-compose up -d
124
+
125
+ # 查看服务状态
126
+ docker-compose ps
127
+
128
+ # 查看日志
129
+ docker-compose logs -f ocngx
130
+ ```
131
+
132
+ #### 配置外部备份存储
133
+ ```bash
134
+ # 创建 .env 文件
135
+ cat >> .env << EOF
136
+ BACKUP_S3_BUCKET=your-backup-bucket
137
+ AWS_ACCESS_KEY_ID=your-access-key
138
+ AWS_SECRET_ACCESS_KEY=your-secret-key
139
+ AWS_DEFAULT_REGION=us-east-1
140
+ EOF
141
+
142
+ # 启动包含备份服务的完整系统
143
+ docker-compose up -d
144
+ ```
145
+
146
+ ## 迁移流程
147
+
148
+ ### 从 Source Space 到 Target Space
149
+
150
+ ```bash
151
+ # 1. 在 Source Space 创建备份
152
+ ssh source-space
153
+ ./backup-scripts/backup.sh
154
+
155
+ # 2. 下载备份文件
156
+ scp user@source-space:/var/backups/ocngx/latest.tar.gz ./
157
+
158
+ # 3. 在 Target Space 准备环境
159
+ git clone <项目仓库> target-space
160
+ cd target-space
161
+ export SPACE_ID="target-space-name"
162
+
163
+ # 4. 配置新环境
164
+ source deploy-config.sh
165
+ configure_for_deployment
166
+
167
+ # 5. 恢复数据
168
+ ./backup-scripts/restore.sh latest.tar.gz
169
+
170
+ # 6. 启动服务
171
+ docker-compose up -d
172
+
173
+ # 7. 验证迁移
174
+ curl https://target-space.hf.space/health
175
+ ```
176
+
177
+ ## 备份策略
178
+
179
+ ### 自动备份
180
+ - **���率**: 每日凌晨 2:00
181
+ - **保留**: 最近 10 个备份
182
+ - **压缩**: gzip 压缩
183
+ - **校验**: SHA256 校验和
184
+
185
+ ### 手动备份
186
+ ```bash
187
+ # 立即备份
188
+ ./backup-scripts/backup.sh
189
+
190
+ # 指定备份目录
191
+ BACKUP_DIR=/custom/backup/path ./backup-scripts/backup.sh
192
+ ```
193
+
194
+ ### 外部存储集成
195
+ 支持自动上传到 S3 存储:
196
+ ```bash
197
+ # 配置 S3 环境变量
198
+ export BACKUP_S3_BUCKET="my-backup-bucket"
199
+ export AWS_ACCESS_KEY_ID="your-key"
200
+ export AWS_SECRET_ACCESS_KEY="your-secret"
201
+
202
+ # 备份时自动上传
203
+ ./backup-scripts/backup.sh
204
+ ```
205
+
206
+ ## 故障恢复
207
+
208
+ ### 恢复损坏的配置
209
+ ```bash
210
+ # 恢复最近的备份
211
+ ./backup-scripts/restore.sh --force /var/backups/ocngx/latest.tar.gz
212
+
213
+ # 重启服务
214
+ docker-compose restart ocngx
215
+ ```
216
+
217
+ ### 数据库损坏恢复
218
+ ```bash
219
+ # 只恢复数据,跳过配置
220
+ ./backup-scripts/restore.sh --skip-config backup.tar.gz
221
+
222
+ # 手动恢复特定数据
223
+ tar -xzf backup.tar.gz '*/opencode_data/*' -C /
224
+ ```
225
+
226
+ ### 回滚到之前版本
227
+ ```bash
228
+ # 查看可用备份
229
+ ls -la /var/backups/ocngx/
230
+
231
+ # 恢复到指定版本
232
+ ./backup-scripts/restore.sh /var/backups/ocngx/ocngx_backup_20240101_120000.tar.gz
233
+ ```
234
+
235
+ ## 监控和维护
236
+
237
+ ### 健康检查
238
+ ```bash
239
+ # 检查服务状态
240
+ curl http://localhost:7860/health
241
+
242
+ # 检查 OpenCode 状态
243
+ curl http://localhost:3000/global/health
244
+
245
+ # 检查备份状态
246
+ ./backup-scripts/backup.sh --dry-run
247
+ ```
248
+
249
+ ### 日志监控
250
+ ```bash
251
+ # Nginx 日志
252
+ tail -f logs/nginx/access.log
253
+ tail -f logs/nginx/error.log
254
+
255
+ # OpenCode 日志
256
+ tail -f ~/.opencode/logs/opencode.log
257
+
258
+ # 备份日志
259
+ tail -f logs/backup.log
260
+ ```
261
+
262
+ ### 磁盘空间管理
263
+ ```bash
264
+ # 查看备份占用空间
265
+ du -sh /var/backups/ocngx/
266
+
267
+ # 清理旧备份
268
+ find /var/backups/ocngx -name "*.tar.gz" -mtime +30 -delete
269
+
270
+ # 查看系统磁盘使用
271
+ df -h
272
+ ```
273
+
274
+ ## 安全考虑
275
+
276
+ ### 敏感数据处理
277
+ - 备份中不包含密码明文
278
+ - 环境变量中的敏感信息会被过滤
279
+ - 建议使用外部密钥管理服务
280
+
281
+ ### 访问控制
282
+ ```bash
283
+ # 设置备份文件权限
284
+ chmod 600 /var/backups/ocngx/*.tar.gz
285
+
286
+ # 限制恢复脚本执行权限
287
+ chmod 700 backup-scripts/restore.sh
288
+ ```
289
+
290
+ ### 网络安全
291
+ - 备份传输使用加密通道
292
+ - S3 上传使用服务器端加密
293
+ - 定期轮换访问密钥
294
+
295
+ ## 故障排除
296
+
297
+ ### 常见问题
298
+
299
+ #### 1. 备份失败
300
+ ```bash
301
+ # 检查磁盘空间
302
+ df -h
303
+
304
+ # 检查文件权限
305
+ ls -la /var/backups/
306
+
307
+ # 查看详细错误
308
+ ./backup-scripts/backup.sh --debug
309
+ ```
310
+
311
+ #### 2. 恢复失败
312
+ ```bash
313
+ # 验证备份文件完整性
314
+ sha256sum -c backup.tar.gz.sha256
315
+
316
+ # 检查目标环境
317
+ ./backup-scripts/restore.sh --dry-run backup.tar.gz
318
+
319
+ # 强制恢复
320
+ ./backup-scripts/restore.sh --force backup.tar.gz
321
+ ```
322
+
323
+ #### 3. 服务启动失败
324
+ ```bash
325
+ # 检查配置文件
326
+ docker-compose config
327
+
328
+ # 查看容器日志
329
+ docker-compose logs ocngx
330
+
331
+ # 重新构建容器
332
+ docker-compose up -d --build
333
+ ```
334
+
335
+ ## 最佳实践
336
+
337
+ ### 1. 定期测试恢复
338
+ ```bash
339
+ # 每月测试一次恢复流程
340
+ ./backup-scripts/backup.sh
341
+ ./backup-scripts/restore.sh --test /var/backups/ocngx/latest.tar.gz
342
+ ```
343
+
344
+ ### 2. 版本控制
345
+ ```bash
346
+ # 记录每次重要变更
347
+ git tag -a "v1.0.0-backup" -m "Backup before migration"
348
+ ```
349
+
350
+ ### 3. 文档更新
351
+ - 记录每次迁移过程
352
+ - 更新配置变更
353
+ - 维护故障处理文档
354
+
355
+ ## 技术支持
356
+
357
+ 如遇到问题,请提供以下信息:
358
+ 1. 错误日志
359
+ 2. 系统环境信息
360
+ 3. 备份文件元数据
361
+ 4. 具体操作步骤
362
+
363
+ 联系支持:创建 Issue 并附上相关日志文件。
backup-scripts/backup.sh ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # OCNGX 完整备份脚本
4
+ # 支持多环境部署的数据备份和恢复
5
+
6
+ set -euo pipefail
7
+
8
+ # 配置变量
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ BACKUP_DIR="${BACKUP_DIR:-/var/backups/ocngx}"
11
+ BACKUP_DATE=$(date '+%Y%m%d_%H%M%S')
12
+ BACKUP_ID="ocngx_backup_${BACKUP_DATE}"
13
+ TEMP_BACKUP_DIR="/tmp/${BACKUP_ID}"
14
+
15
+ # 环境检测
16
+ detect_environment() {
17
+ echo "🔍 检测运行环境..."
18
+
19
+ if [[ -f /.dockerenv ]]; then
20
+ ENVIRONMENT="docker"
21
+ echo " 🐳 Docker 容器环境"
22
+ elif [[ -d /app && -f /app/opencode.json ]]; then
23
+ ENVIRONMENT="huggingface"
24
+ echo " 🤗 HuggingFace Spaces 环境"
25
+ else
26
+ ENVIRONMENT="local"
27
+ echo " 💻 本地环境"
28
+ fi
29
+
30
+ # 检测 OpenCode 安装位置
31
+ if command -v opencode &> /dev/null; then
32
+ OPENCODE_CMD="opencode"
33
+ OPENCODE_DATA_DIR="${HOME}/.opencode"
34
+ elif npm list -g opencode-ai &> /dev/null; then
35
+ OPENCODE_CMD="opencode"
36
+ OPENCODE_DATA_DIR="${HOME}/.opencode"
37
+ else
38
+ echo " ❌ 未找到 OpenCode 安装"
39
+ exit 1
40
+ fi
41
+
42
+ echo " 📂 OpenCode 数据目录: ${OPENCODE_DATA_DIR}"
43
+ }
44
+
45
+ # 创建备份目录
46
+ prepare_backup() {
47
+ echo "📁 准备备份环境..."
48
+
49
+ mkdir -p "$BACKUP_DIR"
50
+ mkdir -p "$TEMP_BACKUP_DIR"
51
+
52
+ # 创建备份元数据
53
+ cat > "${TEMP_BACKUP_DIR}/backup_metadata.json" << EOF
54
+ {
55
+ "backup_id": "${BACKUP_ID}",
56
+ "backup_date": "$(date -Iseconds)",
57
+ "environment": "${ENVIRONMENT}",
58
+ "hostname": "$(hostname)",
59
+ "opencode_version": "$(opencode --version 2>/dev/null || echo 'unknown')",
60
+ "system_info": {
61
+ "os": "$(uname -s)",
62
+ "arch": "$(uname -m)",
63
+ "kernel": "$(uname -r)"
64
+ },
65
+ "disk_usage": {
66
+ "total": "$(df -h / | awk 'NR==2 {print $2}')",
67
+ "used": "$(df -h / | awk 'NR==2 {print $3}')",
68
+ "available": "$(df -h / | awk 'NR==2 {print $4}')"
69
+ }
70
+ }
71
+ EOF
72
+ }
73
+
74
+ # 备份配置文件
75
+ backup_configs() {
76
+ echo "⚙️ 备份配置文件..."
77
+
78
+ local config_dir="${TEMP_BACKUP_DIR}/configs"
79
+ mkdir -p "$config_dir"
80
+
81
+ # 备份项目配置文件
82
+ [[ -f "${SCRIPT_DIR}/opencode.json" ]] && cp "${SCRIPT_DIR}/opencode.json" "$config_dir/"
83
+ [[ -f "${SCRIPT_DIR}/AGENT.md" ]] && cp "${SCRIPT_DIR}/AGENT.md" "$config_dir/"
84
+ [[ -f "${SCRIPT_DIR}/docker-start.sh" ]] && cp "${SCRIPT_DIR}/docker-start.sh" "$config_dir/"
85
+ [[ -f "${SCRIPT_DIR}/Dockerfile" ]] && cp "${SCRIPT_DIR}/Dockerfile" "$config_dir/"
86
+
87
+ # 备份 Nginx 配置
88
+ if [[ -d "${SCRIPT_DIR}/nginx" ]]; then
89
+ cp -r "${SCRIPT_DIR}/nginx" "$config_dir/"
90
+ fi
91
+
92
+ # 备份 Cron 配置
93
+ if [[ -d "${SCRIPT_DIR}/cron-jobs" ]]; then
94
+ cp -r "${SCRIPT_DIR}/cron-jobs" "$config_dir/"
95
+ fi
96
+
97
+ # 备份环境变量(排除敏感信息)
98
+ env | grep -E '^(OPENCODE_|PUBLIC_|BASE_|GATEWAY_)' > "$config_dir/environment.txt" 2>/dev/null || true
99
+
100
+ echo " ✅ 配置文件备份完成"
101
+ }
102
+
103
+ # 备份 OpenCode 数据
104
+ backup_opencode_data() {
105
+ echo "🤖 备份 OpenCode 数据..."
106
+
107
+ if [[ -d "$OPENCODE_DATA_DIR" ]]; then
108
+ local data_dir="${TEMP_BACKUP_DIR}/opencode_data"
109
+ mkdir -p "$data_dir"
110
+
111
+ # 备份核心数据目录
112
+ for subdir in "projects" "sessions" "config" "cache" "logs"; do
113
+ if [[ -d "${OPENCODE_DATA_DIR}/${subdir}" ]]; then
114
+ cp -r "${OPENCODE_DATA_DIR}/${subdir}" "$data_dir/"
115
+ echo " 📦 已备份: $subdir"
116
+ fi
117
+ done
118
+
119
+ # 备份全局配置文件
120
+ [[ -f "${OPENCODE_DATA_DIR}/config.json" ]] && cp "${OPENCODE_DATA_DIR}/config.json" "$data_dir/"
121
+ [[ -f "${OPENCODE_DATA_DIR}/preferences.json" ]] && cp "${OPENCODE_DATA_DIR}/preferences.json" "$data_dir/"
122
+
123
+ echo " ✅ OpenCode 数据备份完成"
124
+ else
125
+ echo " ⚠️ OpenCode 数据目录不存在,跳过"
126
+ fi
127
+ }
128
+
129
+ # 备份运行时状态
130
+ backup_runtime_state() {
131
+ echo "🔄 备份运行时状态..."
132
+
133
+ local runtime_dir="${TEMP_BACKUP_DIR}/runtime"
134
+ mkdir -p "$runtime_dir"
135
+
136
+ # 备份当前运行的进程信息
137
+ ps aux | grep -E "(opencode|nginx|openresty)" > "$runtime_dir/running_processes.txt" 2>/dev/null || true
138
+
139
+ # 备份网络连接状态
140
+ netstat -tlnp 2>/dev/null | grep -E ":(3000|7860)" > "$runtime_dir/network_state.txt" || true
141
+
142
+ # 备份最近的日志
143
+ if [[ -d "/var/log" ]]; then
144
+ find /var/log -name "*nginx*" -o -name "*opencode*" -mtime -7 -exec cp {} "$runtime_dir/" \; 2>/dev/null || true
145
+ fi
146
+
147
+ echo " ✅ 运行时状态备份完成"
148
+ }
149
+
150
+ # 创建备份归档
151
+ create_archive() {
152
+ echo "🗜️ 创建备份归档..."
153
+
154
+ local archive_file="${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
155
+
156
+ # 创建压缩归档
157
+ tar -czf "$archive_file" -C "/tmp" "$BACKUP_ID"
158
+
159
+ # 验证归档
160
+ if [[ -f "$archive_file" ]]; then
161
+ local archive_size=$(du -h "$archive_file" | cut -f1)
162
+ echo " ✅ 备份归档创建成功: ${archive_file} (${archive_size})"
163
+
164
+ # 生成校验和
165
+ sha256sum "$archive_file" > "${archive_file}.sha256"
166
+ echo " 🔐 校验和已生成: ${archive_file}.sha256"
167
+ else
168
+ echo " ❌ 备份归档创建失败"
169
+ exit 1
170
+ fi
171
+ }
172
+
173
+ # 清理旧备份
174
+ cleanup_old_backups() {
175
+ echo "🧹 清理旧备份..."
176
+
177
+ cd "$BACKUP_DIR"
178
+
179
+ # 保留最近 10 个备份
180
+ local backup_count=$(ls -1 ocngx_backup_*.tar.gz 2>/dev/null | wc -l)
181
+ if [[ $backup_count -gt 10 ]]; then
182
+ echo " 🗑️ 删除超过 10 个的旧备份..."
183
+ ls -t ocngx_backup_*.tar.gz 2>/dev/null | tail -n +11 | while read -r old_backup; do
184
+ echo " 删除: $old_backup"
185
+ rm -f "$old_backup" "${old_backup}.sha256"
186
+ done
187
+ fi
188
+
189
+ # 显示备份状态
190
+ local remaining_count=$(ls -1 ocngx_backup_*.tar.gz 2>/dev/null | wc -l)
191
+ local total_size=$(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1)
192
+ echo " 📊 当前备份状态: $remaining_count 个文件, 总大小: $total_size"
193
+ }
194
+
195
+ # 上传到外部存储(可选)
196
+ upload_to_external() {
197
+ if [[ -n "${BACKUP_S3_BUCKET:-}" ]]; then
198
+ echo "☁️ 上传备份到 S3..."
199
+
200
+ local archive_file="${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
201
+
202
+ if command -v aws &> /dev/null; then
203
+ aws s3 cp "$archive_file" "s3://${BACKUP_S3_BUCKET}/ocngx-backups/" --storage-class STANDARD_IA
204
+ aws s3 cp "${archive_file}.sha256" "s3://${BACKUP_S3_BUCKET}/ocngx-backups/"
205
+ echo " ✅ S3 上传完成"
206
+ else
207
+ echo " ⚠️ AWS CLI 未安装,跳过 S3 上传"
208
+ fi
209
+ fi
210
+ }
211
+
212
+ # 主函数
213
+ main() {
214
+ echo "🚀 OCNGX 完整备份开始..."
215
+ echo "📅 备份时间: $(date '+%Y-%m-%d %H:%M:%S')"
216
+ echo "🆔 备份ID: ${BACKUP_ID}"
217
+ echo ""
218
+
219
+ detect_environment
220
+ prepare_backup
221
+ backup_configs
222
+ backup_opencode_data
223
+ backup_runtime_state
224
+ create_archive
225
+ cleanup_old_backups
226
+ upload_to_external
227
+
228
+ # 清理临时目录
229
+ rm -rf "$TEMP_BACKUP_DIR"
230
+
231
+ echo ""
232
+ echo "✅ 备份完成!"
233
+ echo "📁 备份位置: ${BACKUP_DIR}/${BACKUP_ID}.tar.gz"
234
+ echo "🔍 验证命令: tar -tzf ${BACKUP_DIR}/${BACKUP_ID}.tar.gz | head -10"
235
+ }
236
+
237
+ # 执行主函数
238
+ main "$@"
backup-scripts/restore.sh ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # OCNGX 恢复脚本
4
+ # 用于在不同的 HuggingFace Space 上恢复 OCNGX 系统
5
+
6
+ set -euo pipefail
7
+
8
+ # 配置变量
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
11
+
12
+ # 参数解析
13
+ BACKUP_FILE=""
14
+ FORCE_RESTORE=false
15
+ SKIP_CONFIG=false
16
+
17
+ usage() {
18
+ echo "用法: $0 [选项] <备份文件>"
19
+ echo ""
20
+ echo "选项:"
21
+ echo " -f, --force 强制恢复,覆盖现有配置"
22
+ echo " -s, --skip-config 跳过配置文件恢复"
23
+ echo " -h, --help 显示帮助信息"
24
+ echo ""
25
+ echo "示例:"
26
+ echo " $0 /path/to/ocngx_backup_20240101_120000.tar.gz"
27
+ echo " $0 --force ocngx_backup_20240101_120000.tar.gz"
28
+ exit 1
29
+ }
30
+
31
+ # 解析命令行参数
32
+ while [[ $# -gt 0 ]]; do
33
+ case $1 in
34
+ -f|--force)
35
+ FORCE_RESTORE=true
36
+ shift
37
+ ;;
38
+ -s|--skip-config)
39
+ SKIP_CONFIG=true
40
+ shift
41
+ ;;
42
+ -h|--help)
43
+ usage
44
+ ;;
45
+ -*)
46
+ echo "未知选项: $1"
47
+ usage
48
+ ;;
49
+ *)
50
+ if [[ -z "$BACKUP_FILE" ]]; then
51
+ BACKUP_FILE="$1"
52
+ else
53
+ echo "错误: 只能指定一个备份文件"
54
+ usage
55
+ fi
56
+ shift
57
+ ;;
58
+ esac
59
+ done
60
+
61
+ # 检查备份文件
62
+ if [[ -z "$BACKUP_FILE" ]]; then
63
+ echo "❌ 错误: 必须指定备份文件"
64
+ usage
65
+ fi
66
+
67
+ if [[ ! -f "$BACKUP_FILE" ]]; then
68
+ echo "❌ 错误: 备份文件不存在: $BACKUP_FILE"
69
+ exit 1
70
+ fi
71
+
72
+ # 验证备份文件完整性
73
+ verify_backup() {
74
+ echo "🔍 验证备份文件..."
75
+
76
+ # 检查文件格式
77
+ if [[ ! "$BACKUP_FILE" =~ \.tar\.gz$ ]]; then
78
+ echo "❌ 错误: 备份文件格式不正确,应为 .tar.gz 文件"
79
+ exit 1
80
+ fi
81
+
82
+ # 验证校验和(如果存在)
83
+ local checksum_file="${BACKUP_FILE}.sha256"
84
+ if [[ -f "$checksum_file" ]]; then
85
+ echo "🔐 验证校验和..."
86
+ if sha256sum -c "$checksum_file" >/dev/null 2>&1; then
87
+ echo " ✅ 校验和验证通过"
88
+ else
89
+ echo " ❌ 校验和验证失败,备份文件可能已损坏"
90
+ exit 1
91
+ fi
92
+ else
93
+ echo " ⚠️ 未找到校验和文件,跳过验证"
94
+ fi
95
+
96
+ # 检查归档内容
97
+ echo "📦 检查归档内容..."
98
+ if ! tar -tzf "$BACKUP_FILE" >/dev/null 2>&1; then
99
+ echo "❌ 错误: 备份文件无法正常解压"
100
+ exit 1
101
+ fi
102
+
103
+ echo " ✅ 备份文件验证通过"
104
+ }
105
+
106
+ # 检查环境
107
+ check_environment() {
108
+ echo "🔍 检查恢复环境..."
109
+
110
+ # 检查是否为 HuggingFace Spaces 环境
111
+ if [[ -d "/app" && -f "/app/opencode.json" ]]; then
112
+ echo " 🤗 HuggingFace Spaces 环境检测到"
113
+ RESTORE_TARGET="/app"
114
+ else
115
+ echo " 💻 本地环境,恢复到项目目录"
116
+ RESTORE_TARGET="$PROJECT_DIR"
117
+ fi
118
+
119
+ echo " 📂 恢复目标: $RESTORE_TARGET"
120
+
121
+ # 检查磁盘空间
122
+ local backup_size=$(du -h "$BACKUP_FILE" | cut -f1)
123
+ local available_space=$(df -h "$RESTORE_TARGET" | awk 'NR==2 {print $4}')
124
+ echo " 💾 备份大小: $backup_size, 可用空间: $available_space"
125
+ }
126
+
127
+ # 预检查
128
+ pre_restore_checks() {
129
+ echo "⚠️ 执行恢复前检查..."
130
+
131
+ # 检查关键文件是否存在
132
+ local critical_files=("$RESTORE_TARGET/opencode.json" "$RESTORE_TARGET/docker-start.sh")
133
+ for file in "${critical_files[@]}"; do
134
+ if [[ -f "$file" ]] && [[ "$FORCE_RESTORE" != true ]]; then
135
+ echo " ⚠️ 发现现有配置: $file"
136
+ echo " 使用 --force 选项强制覆盖"
137
+ echo " 或使用 --skip-config 选项跳过配置恢复"
138
+ exit 1
139
+ fi
140
+ done
141
+
142
+ # 检查 OpenCode 是否运行
143
+ if pgrep -f "opencode" >/dev/null 2>&1; then
144
+ echo " ⚠️ 检测到 OpenCode 正在运行"
145
+ echo " 建议停止服务后再执行恢复"
146
+ read -p "是否继续? (y/N): " -n 1 -r
147
+ echo
148
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
149
+ echo "恢复已取消"
150
+ exit 0
151
+ fi
152
+ fi
153
+
154
+ echo " ✅ 预检查完成"
155
+ }
156
+
157
+ # 创建临时恢复目录
158
+ prepare_restore() {
159
+ echo "📁 准备恢复环境..."
160
+
161
+ TEMP_RESTORE_DIR="/tmp/ocngx_restore_$(date +%s)"
162
+ mkdir -p "$TEMP_RESTORE_DIR"
163
+
164
+ echo " 📂 临时目录: $TEMP_RESTORE_DIR"
165
+
166
+ # 解压备份文件
167
+ echo "📦 解压备份文件..."
168
+ tar -xzf "$BACKUP_FILE" -C "$TEMP_RESTORE_DIR"
169
+
170
+ # 查找备份根目录
171
+ BACKUP_ROOT=$(find "$TEMP_RESTORE_DIR" -name "ocngx_backup_*" -type d | head -1)
172
+ if [[ -z "$BACKUP_ROOT" ]]; then
173
+ echo "❌ 错误: 无法找到备份根目录"
174
+ exit 1
175
+ fi
176
+
177
+ echo " ✅ 备份解压完成: $BACKUP_ROOT"
178
+ }
179
+
180
+ # 显示备份信息
181
+ show_backup_info() {
182
+ echo "📋 备份信息:"
183
+
184
+ local metadata_file="$BACKUP_ROOT/backup_metadata.json"
185
+ if [[ -f "$metadata_file" ]]; then
186
+ echo " 🆔 备份ID: $(jq -r '.backup_id' "$metadata_file" 2>/dev/null || echo "未知")"
187
+ echo " 📅 备份时间: $(jq -r '.backup_date' "$metadata_file" 2>/dev/null || echo "未知")"
188
+ echo " 🌍 环境: $(jq -r '.environment' "$metadata_file" 2>/dev/null || echo "未知")"
189
+ echo " 🖥️ 主机名: $(jq -r '.hostname' "$metadata_file" 2>/dev/null || echo "未知")"
190
+ echo " 🤖 OpenCode版本: $(jq -r '.opencode_version' "$metadata_file" 2>/dev/null || echo "未知")"
191
+ else
192
+ echo " ⚠️ 未找到备份元数据"
193
+ fi
194
+ echo ""
195
+ }
196
+
197
+ # 恢复配置文件
198
+ restore_configs() {
199
+ if [[ "$SKIP_CONFIG" == true ]]; then
200
+ echo "⏭️ 跳过配置文件恢复"
201
+ return
202
+ fi
203
+
204
+ echo "⚙️ 恢复配置文件..."
205
+
206
+ local config_source="$BACKUP_ROOT/configs"
207
+ if [[ ! -d "$config_source" ]]; then
208
+ echo " ⚠️ 未找到配置文件备份"
209
+ return
210
+ fi
211
+
212
+ # 备份现有配置
213
+ if [[ "$FORCE_RESTORE" == true ]] && [[ -f "$RESTORE_TARGET/opencode.json" ]]; then
214
+ echo " 💾 备份现有配置..."
215
+ mkdir -p "$RESTORE_TARGET/.backup_$(date +%s)"
216
+ cp -r "$RESTORE_TARGET"/opencode.json "$RESTORE_TARGET"/docker-start.sh "$RESTORE_TARGET/.backup_$(date +%s)/" 2>/dev/null || true
217
+ fi
218
+
219
+ # 恢复核心配置文件
220
+ local files_to_restore=("opencode.json" "AGENT.md" "docker-start.sh" "Dockerfile")
221
+ for file in "${files_to_restore[@]}"; do
222
+ if [[ -f "$config_source/$file" ]]; then
223
+ cp "$config_source/$file" "$RESTORE_TARGET/"
224
+ echo " ✅ 已恢复: $file"
225
+ fi
226
+ done
227
+
228
+ # 恢复 Nginx 配置
229
+ if [[ -d "$config_source/nginx" ]]; then
230
+ cp -r "$config_source/nginx"/* "$RESTORE_TARGET/nginx/" 2>/dev/null || true
231
+ echo " ✅ 已恢复: Nginx 配置"
232
+ fi
233
+
234
+ # 恢复 Cron 配置
235
+ if [[ -d "$config_source/cron-jobs" ]]; then
236
+ cp -r "$config_source/cron-jobs"/* "$RESTORE_TARGET/cron-jobs/" 2>/dev/null || true
237
+ echo " ✅ 已恢复: Cron 配置"
238
+ fi
239
+
240
+ echo " ✅ 配置文件恢复完成"
241
+ }
242
+
243
+ # 恢复 OpenCode 数据
244
+ restore_opencode_data() {
245
+ echo "🤖 恢复 OpenCode 数据..."
246
+
247
+ local data_source="$BACKUP_ROOT/opencode_data"
248
+ if [[ ! -d "$data_source" ]]; then
249
+ echo " ⚠️ 未找到 OpenCode 数据备份"
250
+ return
251
+ fi
252
+
253
+ # 确定 OpenCode 数据目录
254
+ local opencode_data_dir="${HOME}/.opencode"
255
+ mkdir -p "$opencode_data_dir"
256
+
257
+ # 恢复数据目录
258
+ for subdir in "projects" "sessions" "config" "cache" "logs"; do
259
+ if [[ -d "$data_source/$subdir" ]]; then
260
+ cp -r "$data_source/$subdir" "$opencode_data_dir/"
261
+ echo " 📦 已恢复: $subdir"
262
+ fi
263
+ done
264
+
265
+ # 恢复配置文件
266
+ local config_files=("config.json" "preferences.json")
267
+ for file in "${config_files[@]}"; do
268
+ if [[ -f "$data_source/$file" ]]; then
269
+ cp "$data_source/$file" "$opencode_data_dir/"
270
+ echo " ⚙️ 已恢复: $file"
271
+ fi
272
+ done
273
+
274
+ # 设置正确的权限
275
+ chmod -R 755 "$opencode_data_dir" 2>/dev/null || true
276
+
277
+ echo " ✅ OpenCode 数据恢复完成"
278
+ }
279
+
280
+ # 更新环境配置
281
+ update_environment_config() {
282
+ echo "🔄 更新环境配置..."
283
+
284
+ # 检测当前 HuggingFace Space URL
285
+ if [[ -n "${SPACE_ID:-}" ]]; then
286
+ local space_url="https://${SPACE_ID}.hf.space"
287
+ echo " 🌐 检测到 HuggingFace Space URL: $space_url"
288
+
289
+ # 更新 docker-start.sh 中的 URL 配置
290
+ local docker_start_file="$RESTORE_TARGET/docker-start.sh"
291
+ if [[ -f "$docker_start_file" ]]; then
292
+ sed -i.bak "s|https://[^.]*\.hf\.space|$space_url|g" "$docker_start_file"
293
+ echo " ✅ 已更新 Space URL 配置"
294
+ fi
295
+ fi
296
+
297
+ # 设置正确的文件权限
298
+ chmod +x "$RESTORE_TARGET/docker-start.sh" 2>/dev/null || true
299
+ chmod +x "$RESTORE_TARGET/cron-jobs"/*.sh 2>/dev/null || true
300
+
301
+ echo " ✅ 环境配置更新完成"
302
+ }
303
+
304
+ # 验证恢复结果
305
+ verify_restore() {
306
+ echo "🔍 验证恢复结果..."
307
+
308
+ local errors=0
309
+
310
+ # 检查关键文件
311
+ local critical_files=(
312
+ "$RESTORE_TARGET/opencode.json"
313
+ "$RESTORE_TARGET/docker-start.sh"
314
+ "$RESTORE_TARGET/Dockerfile"
315
+ "$RESTORE_TARGET/nginx/conf.d/default.conf"
316
+ )
317
+
318
+ for file in "${critical_files[@]}"; do
319
+ if [[ -f "$file" ]]; then
320
+ echo " ✅ 存在: $file"
321
+ else
322
+ echo " ❌ 缺失: $file"
323
+ ((errors++))
324
+ fi
325
+ done
326
+
327
+ # 检查数据目录
328
+ local opencode_data_dir="${HOME}/.opencode"
329
+ if [[ -d "$opencode_data_dir" ]]; then
330
+ echo " ✅ OpenCode 数据目录存在"
331
+ else
332
+ echo " ⚠️ OpenCode 数据目录不存在"
333
+ fi
334
+
335
+ if [[ $errors -eq 0 ]]; then
336
+ echo " ✅ 恢复验证通过"
337
+ else
338
+ echo " ❌ 恢复验证失败,发现 $errors 个错误"
339
+ return 1
340
+ fi
341
+ }
342
+
343
+ # 清理临时文件
344
+ cleanup() {
345
+ echo "🧹 清理临时文件..."
346
+ rm -rf "$TEMP_RESTORE_DIR"
347
+ echo " ✅ 清理完成"
348
+ }
349
+
350
+ # 显示恢复后说明
351
+ show_post_restore_info() {
352
+ echo ""
353
+ echo "🎉 恢复完成!"
354
+ echo ""
355
+ echo "📋 后续步骤:"
356
+ echo "1. 🔄 重启服务以应用新配置:"
357
+ echo " docker-compose down && docker-compose up -d"
358
+ echo ""
359
+ echo "2. 🔍 验证服务状态:"
360
+ echo " curl http://localhost:7860/health"
361
+ echo ""
362
+ echo "3. 📖 检查 OpenCode 状态:"
363
+ echo " curl http://localhost:3000/global/health"
364
+ echo ""
365
+ echo "4. 🌐 访问 Web 界面:"
366
+ echo " http://localhost:7860"
367
+ echo ""
368
+ if [[ "$SKIP_CONFIG" == true ]]; then
369
+ echo "⚠️ 注意: 配置文件恢复已跳过,请手动检查配置"
370
+ fi
371
+ if [[ "$FORCE_RESTORE" == true ]]; then
372
+ echo "💾 原始配置已备份到 .backup_*/ 目录"
373
+ fi
374
+ }
375
+
376
+ # 主函数
377
+ main() {
378
+ echo "🚀 OCNGX 恢复开始..."
379
+ echo "📂 备份文件: $BACKUP_FILE"
380
+ echo "📅 恢复时间: $(date '+%Y-%m-%d %H:%M:%S')"
381
+ echo ""
382
+
383
+ verify_backup
384
+ check_environment
385
+ pre_restore_checks
386
+ prepare_restore
387
+ show_backup_info
388
+ restore_configs
389
+ restore_opencode_data
390
+ update_environment_config
391
+
392
+ if verify_restore; then
393
+ cleanup
394
+ show_post_restore_info
395
+ echo ""
396
+ echo "✅ 恢复成功完成!"
397
+ else
398
+ echo ""
399
+ echo "❌ 恢复过程中发现问题,请检查上述错误信息"
400
+ cleanup
401
+ exit 1
402
+ fi
403
+ }
404
+
405
+ # 错误处理
406
+ trap 'echo "❌ 恢复过程中发生错误"; cleanup; exit 1' ERR
407
+
408
+ # 执行主函数
409
+ main "$@"
deploy-config.sh ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OCNGX 部署配置文件
2
+ # 用于不同环境的部署配置
3
+
4
+ # ============================================================================
5
+ # 基础配置
6
+ # ============================================================================
7
+
8
+ # 应用基本信息
9
+ APP_NAME="ocngx"
10
+ APP_VERSION="1.0.0"
11
+
12
+ # 端口配置
13
+ NGINX_PORT=7860
14
+ OPENCODE_PORT=3000
15
+
16
+ # 认证配置
17
+ ADMIN_USERNAME=admin
18
+ ADMIN_PASSWORD=admin123
19
+
20
+ # ============================================================================
21
+ # 环境检测与配置
22
+ # ============================================================================
23
+
24
+ detect_deployment_environment() {
25
+ if [[ -n "${SPACE_ID:-}" ]]; then
26
+ echo "huggingface"
27
+ elif [[ -f /.dockerenv ]]; then
28
+ echo "docker"
29
+ else
30
+ echo "local"
31
+ fi
32
+ }
33
+
34
+ # 获取当前环境的 URL
35
+ get_base_url() {
36
+ local env=$(detect_deployment_environment)
37
+ case $env in
38
+ "huggingface")
39
+ echo "https://${SPACE_ID}.hf.space"
40
+ ;;
41
+ "docker")
42
+ echo "http://localhost:${NGINX_PORT}"
43
+ ;;
44
+ "local")
45
+ echo "http://localhost:${NGINX_PORT}"
46
+ ;;
47
+ *)
48
+ echo "http://localhost:${NGINX_PORT}"
49
+ ;;
50
+ esac
51
+ }
52
+
53
+ # ============================================================================
54
+ # HuggingFace Spaces 环境配置
55
+ # ============================================================================
56
+
57
+ generate_hf_space_config() {
58
+ cat > .space_config.yaml << EOF
59
+ title: OCNGX - OpenCode with Nginx
60
+ emoji: 🤖
61
+ colorFrom: green
62
+ colorTo: blue
63
+ sdk: docker
64
+ pinned: false
65
+ hardware: cpu-basic
66
+ secrets:
67
+ - ADMIN_USERNAME
68
+ - ADMIN_PASSWORD
69
+ - BACKUP_S3_BUCKET
70
+ - AWS_ACCESS_KEY_ID
71
+ - AWS_SECRET_ACCESS_KEY
72
+ EOF
73
+ }
74
+
75
+ # ============================================================================
76
+ # 环境变量生成
77
+ # ============================================================================
78
+
79
+ generate_env_file() {
80
+ local env=$(detect_deployment_environment)
81
+ local base_url=$(get_base_url)
82
+
83
+ cat > .env << EOF
84
+ # OCNGX 环境配置
85
+ # 生成时间: $(date)
86
+ # 环境: $env
87
+
88
+ # 基础配置
89
+ GATEWAY_HOST=127.0.0.1
90
+ GATEWAY_PORT=$OPENCODE_PORT
91
+ NGINX_PORT=$NGINX_PORT
92
+
93
+ # 认证配置
94
+ USERNAME=$ADMIN_USERNAME
95
+ PASSWORD=$ADMIN_PASSWORD
96
+
97
+ # URL 配置
98
+ OPENCODE_PUBLIC_URL=$base_url
99
+ PUBLIC_URL=$base_url
100
+ BASE_URL=$base_url
101
+
102
+ # 环境标识
103
+ DEPLOYMENT_ENV=$env
104
+ SPACE_ID=${SPACE_ID:-}
105
+
106
+ # 备份配置
107
+ BACKUP_DIR=/var/backups/ocngx
108
+ BACKUP_S3_BUCKET=${BACKUP_S3_BUCKET:-}
109
+
110
+ # 日志配置
111
+ LOG_LEVEL=${LOG_LEVEL:-info}
112
+
113
+ # HuggingFace 特定配置
114
+ EOF
115
+
116
+ if [[ "$env" == "huggingface" ]]; then
117
+ cat >> .env << EOF
118
+ # HuggingFace Spaces 特定配置
119
+ HF_SPACE_ID=${SPACE_ID}
120
+ HF_HOME=/data
121
+ TRANSFORMERS_CACHE=/data/.cache
122
+ EOF
123
+ fi
124
+ }
125
+
126
+ # ============================================================================
127
+ # Nginx 配置生成
128
+ # ============================================================================
129
+
130
+ generate_nginx_config() {
131
+ local env=$(detect_deployment_environment)
132
+ local base_url=$(get_base_url)
133
+
134
+ # 更新 default.conf 中的代理配置
135
+ if [[ -f nginx/conf.d/default.conf ]]; then
136
+ cp nginx/conf.d/default.conf nginx/conf.d/default.conf.bak
137
+
138
+ # 根据环境调整配置
139
+ sed -i "s|https://[^.]*\.hf\.space|$base_url|g" nginx/conf.d/default.conf
140
+ sed -i "s|listen 7860;|listen $NGINX_PORT;|g" nginx/conf.d/default.conf
141
+ fi
142
+ }
143
+
144
+ # ============================================================================
145
+ # 启动脚本生成
146
+ # ============================================================================
147
+
148
+ generate_startup_script() {
149
+ local env=$(detect_deployment_environment)
150
+ local base_url=$(get_base_url)
151
+
152
+ # 更新 docker-start.sh 中的 URL 配置
153
+ if [[ -f docker-start.sh ]]; then
154
+ cp docker-start.sh docker-start.sh.bak
155
+ sed -i "s|https://[^.]*\.hf\.space|$base_url|g" docker-start.sh
156
+ sed -i "s|export GATEWAY_PORT=3000|export GATEWAY_PORT=$OPENCODE_PORT|g" docker-start.sh
157
+ fi
158
+ }
159
+
160
+ # ============================================================================
161
+ # 备份配置
162
+ # ============================================================================
163
+
164
+ setup_backup_configuration() {
165
+ # 创建备份目录
166
+ mkdir -p backups
167
+ mkdir -p logs
168
+
169
+ # 设置备份脚本权限
170
+ chmod +x backup-scripts/*.sh
171
+
172
+ # 创建备份配置
173
+ cat > backup-config.json << EOF
174
+ {
175
+ "backup_schedule": "0 2 * * *",
176
+ "retention_days": 7,
177
+ "compression": "gzip",
178
+ "exclude_patterns": [
179
+ "*.tmp",
180
+ "*.log",
181
+ "cache/*",
182
+ "node_modules/*"
183
+ ],
184
+ "include_configs": true,
185
+ "include_data": true,
186
+ "include_logs": false
187
+ }
188
+ EOF
189
+ }
190
+
191
+ # ============================================================================
192
+ # 主配置函数
193
+ # ============================================================================
194
+
195
+ configure_for_deployment() {
196
+ echo "🚀 配置 OCNGX 部署环境..."
197
+
198
+ local env=$(detect_deployment_environment)
199
+ echo "📍 检测到环境: $env"
200
+
201
+ echo "⚙️ 生成环境配置..."
202
+ generate_env_file
203
+
204
+ echo "🌐 生成 Nginx 配置..."
205
+ generate_nginx_config
206
+
207
+ echo "🔧 生成启动脚本..."
208
+ generate_startup_script
209
+
210
+ echo "💾 设置备份配置..."
211
+ setup_backup_configuration
212
+
213
+ if [[ "$env" == "huggingface" ]]; then
214
+ echo "🤗 生成 HuggingFace Space 配置..."
215
+ generate_hf_space_config
216
+ fi
217
+
218
+ echo "✅ 部署配置完成!"
219
+ }
220
+
221
+ # ============================================================================
222
+ # 验证配置
223
+ # ============================================================================
224
+
225
+ verify_configuration() {
226
+ echo "🔍 验证配置文件..."
227
+
228
+ local errors=0
229
+
230
+ # 检查必需文件
231
+ local required_files=(
232
+ "Dockerfile"
233
+ "docker-start.sh"
234
+ "opencode.json"
235
+ "nginx/conf.d/default.conf"
236
+ "backup-scripts/backup.sh"
237
+ "backup-scripts/restore.sh"
238
+ )
239
+
240
+ for file in "${required_files[@]}"; do
241
+ if [[ -f "$file" ]]; then
242
+ echo " ✅ $file"
243
+ else
244
+ echo " ❌ $file"
245
+ ((errors++))
246
+ fi
247
+ done
248
+
249
+ # 检查环境变量
250
+ if [[ -f ".env" ]]; then
251
+ echo " ✅ .env 配置文件"
252
+ else
253
+ echo " ❌ .env 配置文件"
254
+ ((errors++))
255
+ fi
256
+
257
+ # 检查目录结构
258
+ local required_dirs=("nginx" "backup-scripts" "cron-jobs")
259
+ for dir in "${required_dirs[@]}"; do
260
+ if [[ -d "$dir" ]]; then
261
+ echo " ✅ $dir/ 目录"
262
+ else
263
+ echo " ❌ $dir/ 目录"
264
+ ((errors++))
265
+ fi
266
+ done
267
+
268
+ if [[ $errors -eq 0 ]]; then
269
+ echo "✅ 配置验证通过"
270
+ return 0
271
+ else
272
+ echo "❌ 配置验证失败,发现 $errors 个问题"
273
+ return 1
274
+ fi
275
+ }
276
+
277
+ # ============================================================================
278
+ # 导出函数(供其他脚本使用)
279
+ # ============================================================================
280
+
281
+ export -f detect_deployment_environment
282
+ export -f get_base_url
283
+ export -f generate_env_file
284
+ export -f generate_nginx_config
285
+ export -f generate_startup_script
286
+ export -f setup_backup_configuration
287
+ export -f configure_for_deployment
288
+ export -f verify_configuration
docker-compose.yml ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ ocngx:
5
+ build: .
6
+ container_name: ocngx
7
+ ports:
8
+ - "7860:7860"
9
+ environment:
10
+ # 基本配置
11
+ - GATEWAY_HOST=127.0.0.1
12
+ - GATEWAY_PORT=3000
13
+ - USERNAME=admin
14
+ - PASSWORD=admin123
15
+
16
+ # HuggingFace Spaces 自动检测的环境变量
17
+ - SPACE_ID=${SPACE_ID:-}
18
+ - OPENCODE_PUBLIC_URL=${OPENCODE_PUBLIC_URL:-http://localhost:7860}
19
+ - PUBLIC_URL=${PUBLIC_URL:-http://localhost:7860}
20
+ - BASE_URL=${BASE_URL:-http://localhost:7860}
21
+
22
+ # 备份配置(可选)
23
+ - BACKUP_S3_BUCKET=${BACKUP_S3_BUCKET:-}
24
+ - BACKUP_DIR=${BACKUP_DIR:-/var/backups/ocngx}
25
+
26
+ # 日志级别
27
+ - LOG_LEVEL=${LOG_LEVEL:-info}
28
+
29
+ volumes:
30
+ # 持久化数据目录
31
+ - opencode_data:/root/.opencode
32
+ - backup_data:/var/backups/ocngx
33
+
34
+ # 日志目录
35
+ - ./logs:/var/log/nginx
36
+
37
+ # 时区同步
38
+ - /etc/localtime:/etc/localtime:ro
39
+
40
+ restart: unless-stopped
41
+
42
+ healthcheck:
43
+ test: ["CMD", "curl", "-f", "http://localhost:7860/health"]
44
+ interval: 30s
45
+ timeout: 10s
46
+ retries: 3
47
+ start_period: 40s
48
+
49
+ networks:
50
+ - ocngx_network
51
+
52
+ # 可选:备份服务(用于自动备份到外部存储)
53
+ backup-scheduler:
54
+ image: alpine:latest
55
+ container_name: ocngx-backup
56
+ depends_on:
57
+ - ocngx
58
+ volumes:
59
+ - backup_data:/backups:ro
60
+ - ./backup-scripts:/scripts:ro
61
+ environment:
62
+ - BACKUP_S3_BUCKET=${BACKUP_S3_BUCKET:-}
63
+ - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
64
+ - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
65
+ - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1}
66
+ command: >
67
+ sh -c "
68
+ apk add --no-cache curl tar jq &&
69
+ echo '0 2 * * * /scripts/backup.sh' > /etc/crontabs/root &&
70
+ crond -f -l 2
71
+ "
72
+ restart: unless-stopped
73
+ networks:
74
+ - ocngx_network
75
+
76
+ volumes:
77
+ opencode_data:
78
+ driver: local
79
+ backup_data:
80
+ driver: local
81
+
82
+ networks:
83
+ ocngx_network:
84
+ driver: bridge
migrate.sh ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # OCNGX 迁移工具
4
+ # 用于在不同 HuggingFace Spaces 之间快速迁移系统
5
+
6
+ set -euo pipefail
7
+
8
+ # 配置变量
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ MIGRATION_LOG="/tmp/ocngx_migration_$(date +%s).log"
11
+
12
+ # 颜色输出
13
+ RED='\033[0;31m'
14
+ GREEN='\033[0;32m'
15
+ YELLOW='\033[1;33m'
16
+ BLUE='\033[0;34m'
17
+ NC='\033[0m' # No Color
18
+
19
+ log() {
20
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$MIGRATION_LOG"
21
+ }
22
+
23
+ log_info() {
24
+ echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$MIGRATION_LOG"
25
+ }
26
+
27
+ log_success() {
28
+ echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$MIGRATION_LOG"
29
+ }
30
+
31
+ log_warning() {
32
+ echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$MIGRATION_LOG"
33
+ }
34
+
35
+ log_error() {
36
+ echo -e "${RED}[ERROR]${NC} $1" | tee -a "$MIGRATION_LOG"
37
+ }
38
+
39
+ # 显示帮助信息
40
+ usage() {
41
+ echo "OCNGX 迁移工具 - 在不同 HuggingFace Spaces 之间迁移系统"
42
+ echo ""
43
+ echo "用法: $0 [选项] <源Space> <目标Space>"
44
+ echo ""
45
+ echo "参数:"
46
+ echo " <源Space> 源 HuggingFace Space 名称"
47
+ echo " <目标Space> 目标 HuggingFace Space 名称"
48
+ echo ""
49
+ echo "选项:"
50
+ echo " -b, --backup-only 仅创建备份,不执行迁移"
51
+ echo " -r, --restore-only 仅恢复备份,不创建新备份"
52
+ echo " -k, --keep-backup 保留本地备份文件"
53
+ echo " -f, --force 强制覆盖目标配置"
54
+ echo " -d, --dry-run 模拟运行,不执行实际操作"
55
+ echo " -h, --help 显示帮助信息"
56
+ echo ""
57
+ echo "示例:"
58
+ echo " $0 source-space target-space"
59
+ echo " $0 --backup-only source-space"
60
+ echo " $0 --restore-only --force target-space backup.tar.gz"
61
+ echo ""
62
+ exit 1
63
+ }
64
+
65
+ # 解析命令行参数
66
+ SOURCE_SPACE=""
67
+ TARGET_SPACE=""
68
+ BACKUP_ONLY=false
69
+ RESTORE_ONLY=false
70
+ KEEP_BACKUP=false
71
+ FORCE_MODE=false
72
+ DRY_RUN=false
73
+ BACKUP_FILE=""
74
+
75
+ while [[ $# -gt 0 ]]; do
76
+ case $1 in
77
+ -b|--backup-only)
78
+ BACKUP_ONLY=true
79
+ shift
80
+ ;;
81
+ -r|--restore-only)
82
+ RESTORE_ONLY=true
83
+ shift
84
+ ;;
85
+ -k|--keep-backup)
86
+ KEEP_BACKUP=true
87
+ shift
88
+ ;;
89
+ -f|--force)
90
+ FORCE_MODE=true
91
+ shift
92
+ ;;
93
+ -d|--dry-run)
94
+ DRY_RUN=true
95
+ shift
96
+ ;;
97
+ -h|--help)
98
+ usage
99
+ ;;
100
+ -*)
101
+ log_error "未知选项: $1"
102
+ usage
103
+ ;;
104
+ *)
105
+ if [[ -z "$SOURCE_SPACE" ]]; then
106
+ SOURCE_SPACE="$1"
107
+ elif [[ -z "$TARGET_SPACE" ]]; then
108
+ TARGET_SPACE="$1"
109
+ elif [[ -z "$BACKUP_FILE" ]]; then
110
+ BACKUP_FILE="$1"
111
+ else
112
+ log_error "参数过多"
113
+ usage
114
+ fi
115
+ shift
116
+ ;;
117
+ esac
118
+ done
119
+
120
+ # 验证参数
121
+ validate_parameters() {
122
+ log_info "验证参数..."
123
+
124
+ if [[ "$RESTORE_ONLY" == true ]]; then
125
+ if [[ -z "$TARGET_SPACE" && -z "$BACKUP_FILE" ]]; then
126
+ log_error "恢复模式需要指定目标Space或备份文件"
127
+ usage
128
+ fi
129
+ else
130
+ if [[ -z "$SOURCE_SPACE" || -z "$TARGET_SPACE" ]]; then
131
+ log_error "必须指定源Space和目标Space"
132
+ usage
133
+ fi
134
+ fi
135
+
136
+ log_success "参数验证通过"
137
+ }
138
+
139
+ # 检测当前环境
140
+ detect_current_environment() {
141
+ log_info "检测当前环境..."
142
+
143
+ if [[ -n "${SPACE_ID:-}" ]]; then
144
+ CURRENT_ENV="huggingface"
145
+ CURRENT_SPACE="$SPACE_ID"
146
+ log_info "当前环境: HuggingFace Space ($CURRENT_SPACE)"
147
+ elif [[ -f /.dockerenv ]]; then
148
+ CURRENT_ENV="docker"
149
+ CURRENT_SPACE="docker-container"
150
+ log_info "当前环境: Docker 容器"
151
+ else
152
+ CURRENT_ENV="local"
153
+ CURRENT_SPACE="local-machine"
154
+ log_info "当前环境: 本地机器"
155
+ fi
156
+ }
157
+
158
+ # 检查网络连接
159
+ check_network_connectivity() {
160
+ log_info "检查网络连接..."
161
+
162
+ # 检查 HuggingFace 连通性
163
+ if curl -s --connect-timeout 10 https://huggingface.co >/dev/null 2>&1; then
164
+ log_success "HuggingFace 连接正常"
165
+ else
166
+ log_error "无法连接到 HuggingFace"
167
+ exit 1
168
+ fi
169
+ }
170
+
171
+ # 创建源Space备份
172
+ create_source_backup() {
173
+ log_info "在源Space创建备份: $SOURCE_SPACE"
174
+
175
+ if [[ "$DRY_RUN" == true ]]; then
176
+ log_warning "[DRY-RUN] 模拟在 $SOURCE_SPACE 创建备份"
177
+ return
178
+ fi
179
+
180
+ # 这里需要SSH或API方式连接到源Space
181
+ # 实际实现取决于具体的访问方式
182
+
183
+ local source_url="https://$SOURCE_SPACE.hf.space"
184
+ log_info "源Space URL: $source_url"
185
+
186
+ # 方案1: 通过SSH访问(如果支持)
187
+ # ssh user@$SOURCE_SPACE.hf.space "./backup-scripts/backup.sh"
188
+
189
+ # 方案2: 通过API触发备份
190
+ # curl -X POST "$source_url/api/backup"
191
+
192
+ # 方案3: 通过HuggingFace CLI
193
+ # huggingface-cli space create-backup $SOURCE_SPACE
194
+
195
+ # 临时方案: 假设可以SSH访问
196
+ log_warning "需要手动在源Space执行: ./backup-scripts/backup.sh"
197
+ log_info "等待用户确认备份已完成..."
198
+ read -p "确认备份已完成? (y/N): " -n 1 -r
199
+ echo
200
+
201
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
202
+ log_success "源Space备份已确认"
203
+ else
204
+ log_error "备份未完成,终止迁移"
205
+ exit 1
206
+ fi
207
+ }
208
+
209
+ # 下载备份文件
210
+ download_backup() {
211
+ log_info "下载备份文件..."
212
+
213
+ if [[ "$DRY_RUN" == true ]]; then
214
+ log_warning "[DRY-RUN] 模拟下载备份文件"
215
+ return
216
+ fi
217
+
218
+ # 这里需要实现从源Space下载备份的逻辑
219
+ # 可能的实现方式:
220
+
221
+ # 1. 通过SCP下载
222
+ # scp user@$SOURCE_SPACE.hf.space:/var/backups/ocngx/latest.tar.gz ./
223
+
224
+ # 2. 通过HTTP下载
225
+ # wget "$source_url/download/backup/latest.tar.gz"
226
+
227
+ # 3. 通过HuggingFace API下载
228
+ # huggingface-cli space download-backup $SOURCE_SPACE
229
+
230
+ # 临时方案: 假设用户已手动下载
231
+ log_warning "需要手动从源Space下载备份文件到当前目录"
232
+ log_info "备份文件通常位于: /var/backups/ocngx/ocngx_backup_*.tar.gz"
233
+
234
+ read -p "请输入备份文件路径: " backup_path
235
+
236
+ if [[ -f "$backup_path" ]]; then
237
+ BACKUP_FILE="$backup_path"
238
+ log_success "备份文件已找到: $BACKUP_FILE"
239
+ else
240
+ log_error "备份文件不存在: $backup_path"
241
+ exit 1
242
+ fi
243
+ }
244
+
245
+ # 准备目标Space
246
+ prepare_target_space() {
247
+ log_info "准备目标Space: $TARGET_SPACE"
248
+
249
+ if [[ "$DRY_RUN" == true ]]; then
250
+ log_warning "[DRY-RUN] 模拟准备目标Space"
251
+ return
252
+ fi
253
+
254
+ # 1. 检查目标Space是否存在
255
+ local target_url="https://$TARGET_SPACE.hf.space"
256
+ log_info "目标Space URL: $target_url"
257
+
258
+ # 2. 部署基础代码到目标Space
259
+ log_info "部署基础代码到目标Space..."
260
+
261
+ # 这里需要实现部署逻辑
262
+ # 可能的实现方式:
263
+
264
+ # 1. 通过Git部署
265
+ # git clone <项目仓库> target-deploy
266
+ # cd target-deploy
267
+ # git remote set-url origin git@hf.co:spaces/$TARGET_SPACE
268
+ # git push origin main
269
+
270
+ # 2. 通过HuggingFace CLI部署
271
+ # huggingface-cli space create $TARGET_SPACE --space-type docker
272
+
273
+ # 临时方案: 假设用户已手动部署
274
+ log_warning "需要手动将项目代码部署到目标Space: $TARGET_SPACE"
275
+ log_info "步骤:"
276
+ log_info "1. 创建新的Space: https://huggingface.co/new-space"
277
+ log_info "2. 克隆当前项目代码"
278
+ log_info "3. 推送到新Space"
279
+
280
+ read -p "确认目标Space已准备完成? (y/N): " -n 1 -r
281
+ echo
282
+
283
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
284
+ log_success "目标Space准备已确认"
285
+ else
286
+ log_error "目标Space未准备完成,终止迁移"
287
+ exit 1
288
+ fi
289
+ }
290
+
291
+ # 配置目标环境
292
+ configure_target_environment() {
293
+ log_info "配置目标环境..."
294
+
295
+ if [[ "$DRY_RUN" == true ]]; then
296
+ log_warning "[DRY-RUN] 模拟配置目标环境"
297
+ return
298
+ fi
299
+
300
+ # 这里需要在目标Space上执行环境配置
301
+ # 类似于 prepare_target_space 的逻辑
302
+
303
+ log_warning "需要手动在目标Space配置环境:"
304
+ log_info "1. SSH连接到目标Space"
305
+ log_info "2. 设置环境变量: export SPACE_ID=$TARGET_SPACE"
306
+ log_info "3. 运行: source deploy-config.sh && configure_for_deployment"
307
+
308
+ read -p "确认环境配置已完成? (y/N): " -n 1 -r
309
+ echo
310
+
311
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
312
+ log_success "目标环境配置已确认"
313
+ else
314
+ log_error "环境配置未完成,终止迁移"
315
+ exit 1
316
+ fi
317
+ }
318
+
319
+ # 恢复数据到目标Space
320
+ restore_to_target() {
321
+ log_info "恢复数据到目标Space..."
322
+
323
+ if [[ "$DRY_RUN" == true ]]; then
324
+ log_warning "[DRY-RUN] 模拟恢复数据到目标Space"
325
+ return
326
+ fi
327
+
328
+ # 上传备份文件到目标Space
329
+ log_info "上传备份文件到目标Space..."
330
+
331
+ # 实现上传逻辑
332
+ # scp backup.tar.gz user@$TARGET_SPACE.hf.space:/tmp/
333
+
334
+ # 在目标Space执行恢复
335
+ log_info "在目标Space执行数据恢复..."
336
+
337
+ # 实现恢复逻辑
338
+ # ssh user@$TARGET_SPACE.hf.space "./backup-scripts/restore.sh --force /tmp/backup.tar.gz"
339
+
340
+ log_warning "需要手动在目标Space执行:"
341
+ log_info "1. 上传备份文件: scp $BACKUP_FILE user@$TARGET_SPACE.hf.space:/tmp/"
342
+ log_info "2. SSH连接到目标Space"
343
+ log_info "3. 运行: ./backup-scripts/restore.sh --force /tmp/$(basename $BACKUP_FILE)"
344
+
345
+ read -p "确认数据恢复已完成? (y/N): " -n 1 -r
346
+ echo
347
+
348
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
349
+ log_success "数据恢复已确认"
350
+ else
351
+ log_error "数据恢复未完成,终止迁移"
352
+ exit 1
353
+ fi
354
+ }
355
+
356
+ # 验证迁移结果
357
+ verify_migration() {
358
+ log_info "验证迁移结果..."
359
+
360
+ local target_url="https://$TARGET_SPACE.hf.space"
361
+
362
+ # 检查目标Space健康状态
363
+ if curl -s --connect-timeout 10 "$target_url/health" >/dev/null 2>&1; then
364
+ log_success "目标Space健康检查通过"
365
+ else
366
+ log_warning "目标Space健康检查失败,请手动验证"
367
+ fi
368
+
369
+ # 检查OpenCode状态
370
+ if curl -s --connect-timeout 10 "$target_url/global/health" >/dev/null 2>&1; then
371
+ log_success "OpenCode服务状态正常"
372
+ else
373
+ log_warning "OpenCode服务状态异常,请手动检查"
374
+ fi
375
+ }
376
+
377
+ # 清理临时文件
378
+ cleanup() {
379
+ log_info "清理临时文件..."
380
+
381
+ if [[ "$KEEP_BACKUP" != true ]] && [[ -n "$BACKUP_FILE" ]] && [[ -f "$BACKUP_FILE" ]]; then
382
+ if [[ "$DRY_RUN" != true ]]; then
383
+ rm -f "$BACKUP_FILE"
384
+ log_success "已清理备份文件: $BACKUP_FILE"
385
+ fi
386
+ fi
387
+
388
+ # 清理日志文件
389
+ # rm -f "$MIGRATION_LOG"
390
+ }
391
+
392
+ # 生成迁移报告
393
+ generate_report() {
394
+ log_info "生成迁移报告..."
395
+
396
+ local report_file="migration_report_$(date +%Y%m%d_%H%M%S).txt"
397
+
398
+ cat > "$report_file" << EOF
399
+ OCNGX 迁移报告
400
+ ===============
401
+
402
+ 迁移时间: $(date '+%Y-%m-%d %H:%M:%S')
403
+ 源Space: $SOURCE_SPACE
404
+ 目标Space: $TARGET_SPACE
405
+ 备份文件: $BACKUP_FILE
406
+ 当前环境: $CURRENT_ENV ($CURRENT_SPACE)
407
+
408
+ 迁移参数:
409
+ - 强制模式: $FORCE_MODE
410
+ - 仅备份: $BACKUP_ONLY
411
+ - 仅恢复: $RESTORE_ONLY
412
+ - 保留备份: $KEEP_BACKUP
413
+ - 模拟运行: $DRY_RUN
414
+
415
+ 迁移日志:
416
+ $(cat "$MIGRATION_LOG")
417
+
418
+ 后续步骤:
419
+ 1. 访问目标Space: https://$TARGET_SPACE.hf.space
420
+ 2. 验证所有功能正常
421
+ 3. 更新DNS或代理配置(如需要)
422
+ 4. 通知用户迁移完成
423
+
424
+ 注意事项:
425
+ - 请保留此报告用于记录
426
+ - 建议在迁移后24小时内监控系统状态
427
+ - 如有问题,请检查迁移日志
428
+ EOF
429
+
430
+ log_success "迁移报告已生成: $report_file"
431
+ }
432
+
433
+ # 主迁移流程
434
+ execute_migration() {
435
+ log_info "开始迁移流程..."
436
+ log_info "源Space: $SOURCE_SPACE -> 目标Space: $TARGET_SPACE"
437
+
438
+ if [[ "$BACKUP_ONLY" == true ]]; then
439
+ create_source_backup
440
+ download_backup
441
+ log_success "备份完成"
442
+ return
443
+ fi
444
+
445
+ if [[ "$RESTORE_ONLY" == true ]]; then
446
+ if [[ -z "$TARGET_SPACE" ]]; then
447
+ # 本地恢复模式
448
+ log_info "本地恢复模式"
449
+ if [[ -z "$BACKUP_FILE" ]]; then
450
+ log_error "本地恢复需要指定备份文件"
451
+ exit 1
452
+ fi
453
+ ./backup-scripts/restore.sh ${FORCE_MODE:+--force} "$BACKUP_FILE"
454
+ else
455
+ prepare_target_space
456
+ configure_target_environment
457
+ restore_to_target
458
+ fi
459
+ verify_migration
460
+ log_success "恢复完成"
461
+ return
462
+ fi
463
+
464
+ # 完整迁移流程
465
+ create_source_backup
466
+ download_backup
467
+ prepare_target_space
468
+ configure_target_environment
469
+ restore_to_target
470
+ verify_migration
471
+
472
+ log_success "迁移完成!"
473
+ }
474
+
475
+ # 错误处理
476
+ handle_error() {
477
+ log_error "迁移过程中发生错误"
478
+ cleanup
479
+ exit 1
480
+ }
481
+
482
+ # 主函数
483
+ main() {
484
+ echo "🚀 OCNGX 迁移工具"
485
+ echo "=================="
486
+
487
+ validate_parameters
488
+ detect_current_environment
489
+ check_network_connectivity
490
+
491
+ trap handle_error ERR
492
+
493
+ execute_migration
494
+ generate_report
495
+ cleanup
496
+
497
+ echo ""
498
+ log_success "迁移流程执行完成!"
499
+ echo "📊 迁移日志: $MIGRATION_LOG"
500
+ echo "📋 如有疑问,请查看迁移报告"
501
+ }
502
+
503
+ # 执行主函数
504
+ main "$@"
nginx/.DS_Store CHANGED
Binary files a/nginx/.DS_Store and b/nginx/.DS_Store differ