erayoc commited on
Commit
a541e9c
·
verified ·
1 Parent(s): 040d7be

Create Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +505 -0
Dockerfile ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenClaw on Hugging Face Spaces
2
+
3
+ FROM node:22-slim
4
+
5
+ # 1. 基础依赖
6
+ RUN apt-get update && apt-get install -y --no-install-recommends \
7
+ git openssh-client build-essential python3 python3-pip \
8
+ g++ make ca-certificates curl \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # 2. 安装 Hugging Face Hub
12
+ RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
13
+
14
+ # 3. Git 配置
15
+ RUN update-ca-certificates && \
16
+ git config --global http.sslVerify false && \
17
+ git config --global url."https://github.com/".insteadOf ssh://git@github.com/
18
+
19
+ # 4. 安装 OpenClaw
20
+ RUN npm install -g openclaw@latest --unsafe-perm
21
+
22
+ # 5. 环境变量预设 - 所有可配置的环境变量,使用 ${VAR:-default} 格式
23
+ ENV \
24
+ # 基础配置
25
+ PORT=${PORT:-7860} \
26
+ NODE_ENV=${NODE_ENV:-production} \
27
+ HOME=${HOME:-/root} \
28
+ \
29
+ # OpenClaw 核心配置
30
+ OPENCLAW_GATEWAY_MODE=${OPENCLAW_GATEWAY_MODE:-local} \
31
+ OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN:-} \
32
+ \
33
+ # 模型配置
34
+ MODEL_PROVIDER=${MODEL_PROVIDER:-nvidia} \
35
+ MODEL_ID=${MODEL_ID:-moonshotai/kimi-k2.5} \
36
+ MODEL_CONTEXT_WINDOW=${MODEL_CONTEXT_WINDOW:-256000} \
37
+ MODEL_DISPLAY_NAME=${MODEL_DISPLAY_NAME:-"Kimi K2.5"} \
38
+ MODEL_MODE=${MODEL_MODE:-"replace"} \
39
+ \
40
+ # API 配置
41
+ API_BASE_URL=${API_BASE_URL:-https://integrate.api.nvidia.com/v1} \
42
+ API_TYPE=${API_TYPE:-openai-completions} \
43
+ OPENAI_API_KEY=${OPENAI_API_KEY:-} \
44
+ \
45
+ # 网关配置
46
+ GATEWAY_AUTH_MODE=${GATEWAY_AUTH_MODE:-token} \
47
+ GATEWAY_BIND=${GATEWAY_BIND:-lan} \
48
+ GATEWAY_TRUSTED_PROXIES=${GATEWAY_TRUSTED_PROXIES:-0.0.0.0/0,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16} \
49
+ GATEWAY_ALLOWED_ORIGINS=${GATEWAY_ALLOWED_ORIGINS:-https://control.example.com} \
50
+ \
51
+ # 控制UI配置
52
+ CONTROLUI_ALLOW_INSECURE_AUTH=${CONTROLUI_ALLOW_INSECURE_AUTH:-true} \
53
+ CONTROLUI_DANGEROUS_HOST_HEADER=${CONTROLUI_DANGEROUS_HOST_HEADER:-true} \
54
+ CONTROLUI_DANGEROUS_DISABLE_DEVICE_AUTH=${CONTROLUI_DANGEROUS_DISABLE_DEVICE_AUTH:-true} \
55
+ \
56
+ # Telegram 配置
57
+ TELEGRAM_ENABLED=${TELEGRAM_ENABLED:-false} \
58
+ TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-} \
59
+ TELEGRAM_DM_POLICY=${TELEGRAM_DM_POLICY:-allowlist} \
60
+ TELEGRAM_ALLOW_FROM=${TELEGRAM_ALLOW_FROM:-} \
61
+ TELEGRAM_PROXY_HOST=${TELEGRAM_PROXY_HOST:-} \
62
+ \
63
+ # 备份配置
64
+ BACKUP_ENABLED=${BACKUP_ENABLED:-true} \
65
+ BACKUP_INTERVAL=${BACKUP_INTERVAL:-21600} \
66
+ BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-5} \
67
+ HF_DATASET=${HF_DATASET:-} \
68
+ HF_TOKEN=${HF_TOKEN:-}
69
+
70
+ # 6. 同步脚本 - 备份和恢复整个 .openclaw 目录
71
+ RUN cat > /usr/local/bin/sync.py << 'SYNC_EOF'
72
+ #!/usr/bin/env python3
73
+ import os
74
+ import sys
75
+ import tarfile
76
+ import shutil
77
+ from huggingface_hub import HfApi, hf_hub_download
78
+ from datetime import datetime, timedelta
79
+
80
+ api = HfApi()
81
+ repo_id = os.getenv("HF_DATASET", "")
82
+ token = os.getenv("HF_TOKEN", "")
83
+ retention_days = int(os.getenv("BACKUP_RETENTION_DAYS", "5"))
84
+
85
+ OPENCLAW_DIR = "/root/.openclaw"
86
+ BACKUP_DIR = "/tmp/openclaw_backups"
87
+
88
+ def ensure_backup_dir():
89
+ """确保备份临时目录存在"""
90
+ os.makedirs(BACKUP_DIR, exist_ok=True)
91
+
92
+ def restore():
93
+ """从 Hugging Face Dataset 恢复最新的 .openclaw 目录"""
94
+ if not repo_id or not token:
95
+ print("⚠️ HF_DATASET 或 HF_TOKEN 未设置,跳过恢复")
96
+ return False
97
+
98
+ if not os.path.exists(OPENCLAW_DIR):
99
+ os.makedirs(OPENCLAW_DIR, mode=0o755, exist_ok=True)
100
+
101
+ try:
102
+ # 列出数据集中的所有文件
103
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
104
+ backup_files = [f for f in files if f.startswith("backup_") and f.endswith(".tar.gz")]
105
+
106
+ if not backup_files:
107
+ print("ℹ️ 没有找到备份文件")
108
+ return False
109
+
110
+ # 按日期排序,获取最新的备份
111
+ backup_files.sort(reverse=True)
112
+ latest_backup = backup_files[0]
113
+
114
+ print(f"📥 发现最新备份: {latest_backup}")
115
+
116
+ # 下载备份文件
117
+ ensure_backup_dir()
118
+ local_backup_path = os.path.join(BACKUP_DIR, latest_backup)
119
+
120
+ print(f"⬇️ 下载备份文件...")
121
+ downloaded_path = hf_hub_download(
122
+ repo_id=repo_id,
123
+ filename=latest_backup,
124
+ repo_type="dataset",
125
+ token=token,
126
+ local_dir=BACKUP_DIR,
127
+ local_dir_use_symlinks=False
128
+ )
129
+
130
+ # 创建临时恢复目录
131
+ restore_temp = os.path.join(BACKUP_DIR, "restore_temp")
132
+ if os.path.exists(restore_temp):
133
+ shutil.rmtree(restore_temp)
134
+ os.makedirs(restore_temp)
135
+
136
+ # 解压备份文件
137
+ print(f"📦 解压备份文件...")
138
+ with tarfile.open(downloaded_path, "r:gz") as tar:
139
+ tar.extractall(path=restore_temp)
140
+
141
+ # 检查解压后的内容
142
+ extracted_items = os.listdir(restore_temp)
143
+ print(f"解压内容: {extracted_items}")
144
+
145
+ # 情况1: 解压后直接是 .openclaw 目录
146
+ if ".openclaw" in extracted_items:
147
+ source_dir = os.path.join(restore_temp, ".openclaw")
148
+ # 情况2: 解压后是目录内容(sessions, openclaw.json 等)
149
+ elif set(extracted_items) & {"sessions", "openclaw.json", "workspace"}:
150
+ source_dir = restore_temp
151
+ else:
152
+ print(f"❌ 无法识别的备份格式: {extracted_items}")
153
+ return False
154
+
155
+ # 备份当前目录(如果需要)
156
+ if os.path.exists(OPENCLAW_DIR) and os.listdir(OPENCLAW_DIR):
157
+ backup_current = os.path.join(BACKUP_DIR, "current_before_restore")
158
+ if os.path.exists(backup_current):
159
+ shutil.rmtree(backup_current)
160
+ shutil.copytree(OPENCLAW_DIR, backup_current)
161
+ print(f"💾 已备份当前目录到: {backup_current}")
162
+
163
+ # 清空并恢复目标目录
164
+ print(f"🔄 恢复数据到 {OPENCLAW_DIR}...")
165
+ if os.path.exists(OPENCLAW_DIR):
166
+ # 删除所有内容但不删除目录本身
167
+ for item in os.listdir(OPENCLAW_DIR):
168
+ item_path = os.path.join(OPENCLAW_DIR, item)
169
+ if os.path.isfile(item_path):
170
+ os.remove(item_path)
171
+ elif os.path.isdir(item_path):
172
+ shutil.rmtree(item_path)
173
+
174
+ # 复制所有文件
175
+ for item in os.listdir(source_dir):
176
+ src = os.path.join(source_dir, item)
177
+ dst = os.path.join(OPENCLAW_DIR, item)
178
+ if os.path.isdir(src):
179
+ shutil.copytree(src, dst)
180
+ else:
181
+ shutil.copy2(src, dst)
182
+
183
+ print(f"✅ 恢复完成!")
184
+
185
+ # 清理临时文件
186
+ shutil.rmtree(restore_temp)
187
+ os.remove(downloaded_path)
188
+
189
+ return True
190
+
191
+ except Exception as e:
192
+ print(f"❌ 恢复失败: {e}")
193
+ return False
194
+
195
+ def backup():
196
+ """备份整个 .openclaw 目录到 Hugging Face Dataset"""
197
+ if not repo_id or not token:
198
+ print("⚠️ HF_DATASET 或 HF_TOKEN 未设置,跳过备份")
199
+ return
200
+
201
+ if not os.path.exists(OPENCLAW_DIR) or not os.listdir(OPENCLAW_DIR):
202
+ print("ℹ️ .openclaw 目录为空,跳过备份")
203
+ return
204
+
205
+ try:
206
+ # 生成备份文件名
207
+ today = datetime.now().strftime("%Y-%m-%d")
208
+ backup_filename = f"backup_{today}.tar.gz"
209
+ backup_path = os.path.join(BACKUP_DIR, backup_filename)
210
+
211
+ ensure_backup_dir()
212
+
213
+ print(f"📦 创建备份: {backup_filename}")
214
+
215
+ # 创建备份
216
+ with tarfile.open(backup_path, "w:gz") as tar:
217
+ tar.add(OPENCLAW_DIR, arcname=".openclaw")
218
+
219
+ # 上传到 Hugging Face
220
+ print(f"⬆️ 上传到 Hugging Face Dataset: {repo_id}")
221
+ api.upload_file(
222
+ path_or_fileobj=backup_path,
223
+ path_in_repo=backup_filename,
224
+ repo_id=repo_id,
225
+ repo_type="dataset",
226
+ token=token
227
+ )
228
+
229
+ print(f"✅ 备份完成: {backup_filename}")
230
+
231
+ # 清理旧备份(可选,保留最近 retention_days 天的备份)
232
+ try:
233
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
234
+ backup_files = [f for f in files if f.startswith("backup_") and f.endswith(".tar.gz")]
235
+ backup_files.sort()
236
+
237
+ # 如果备份文件数量超过保留天数,删除最旧的
238
+ while len(backup_files) > retention_days:
239
+ oldest = backup_files.pop(0)
240
+ print(f"🗑️ 删除旧备份: {oldest}")
241
+ api.delete_file(
242
+ path_in_repo=oldest,
243
+ repo_id=repo_id,
244
+ repo_type="dataset",
245
+ token=token
246
+ )
247
+ except Exception as e:
248
+ print(f"⚠️ 清理旧备份失败: {e}")
249
+
250
+ # 删除本地临时文件
251
+ os.remove(backup_path)
252
+
253
+ except Exception as e:
254
+ print(f"❌ 备份失败: {e}")
255
+
256
+ def list_backups():
257
+ """列出所有可用的备份"""
258
+ if not repo_id or not token:
259
+ print("⚠️ HF_DATASET 或 HF_TOKEN 未设置")
260
+ return
261
+
262
+ try:
263
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
264
+ backup_files = [f for f in files if f.startswith("backup_") and f.endswith(".tar.gz")]
265
+ backup_files.sort(reverse=True)
266
+
267
+ if backup_files:
268
+ print("📋 可用的备份:")
269
+ for i, f in enumerate(backup_files, 1):
270
+ print(f" {i}. {f}")
271
+ else:
272
+ print("ℹ️ 没有找到备份文件")
273
+
274
+ except Exception as e:
275
+ print(f"❌ 列出备份��败: {e}")
276
+
277
+ if __name__ == "__main__":
278
+ if len(sys.argv) > 1:
279
+ if sys.argv[1] == "backup":
280
+ backup()
281
+ elif sys.argv[1] == "restore":
282
+ restore()
283
+ elif sys.argv[1] == "list":
284
+ list_backups()
285
+ else:
286
+ print(f"未知命令: {sys.argv[1]}")
287
+ print("可用命令: backup, restore, list")
288
+ else:
289
+ # 默认执行恢复
290
+ restore()
291
+ SYNC_EOF
292
+ RUN chmod +x /usr/local/bin/sync.py
293
+
294
+ # 7. Telegram API 替换脚本
295
+ RUN cat > /usr/local/bin/patch-telegram-api << 'PATCH_EOF'
296
+ #!/bin/bash
297
+
298
+ # 如果设置了 TELEGRAM_PROXY_HOST,则替换所有 Telegram API 地址
299
+ if [ -n "$TELEGRAM_PROXY_HOST" ]; then
300
+ echo "🔧 检测到 TELEGRAM_PROXY_HOST 设置: $TELEGRAM_PROXY_HOST"
301
+ echo "🔄 开始替换 OpenClaw 中的 Telegram API 地址..."
302
+
303
+ OPENCLAW_DIR="/usr/local/lib/node_modules/openclaw"
304
+
305
+ if [ -d "$OPENCLAW_DIR" ]; then
306
+ # 统计替换前的匹配数量
307
+ MATCH_COUNT=$(grep -r "api.telegram.org" "$OPENCLAW_DIR" 2>/dev/null | wc -l)
308
+ echo "📊 找到 $MATCH_COUNT 处需要替换的地址"
309
+
310
+ # 执行替换(使用 sed -i 进行原地替换)
311
+ # 处理 .js 文件
312
+ find "$OPENCLAW_DIR" -type f -name "*.js" -exec sed -i "s|api\\.telegram\\.org|$TELEGRAM_PROXY_HOST|g" {} +
313
+
314
+ # 再次检查是否还有未替换的(可能是其他格式)
315
+ REMAINING=$(grep -r "api.telegram.org" "$OPENCLAW_DIR" 2>/dev/null | wc -l)
316
+
317
+ if [ "$REMAINING" -eq 0 ]; then
318
+ echo "✅ Telegram API 地址替换完成!"
319
+ echo " 原始地址: api.telegram.org"
320
+ echo " 新地址: $TELEGRAM_PROXY_HOST"
321
+ else
322
+ echo "⚠️ 仍有 $REMAINING 处未替换,尝试二次替换..."
323
+ # 尝试更宽松的匹配(处理可能的转义或编码)
324
+ find "$OPENCLAW_DIR" -type f \( -name "*.js" -o -name "*.json" -o -name "*.ts" \) -exec sed -i "s|api.telegram.org|$TELEGRAM_PROXY_HOST|g" {} +
325
+
326
+ FINAL_REMAINING=$(grep -r "api.telegram.org" "$OPENCLAW_DIR" 2>/dev/null | wc -l)
327
+ echo "📊 最终剩余未替换: $FINAL_REMAINING 处"
328
+ fi
329
+
330
+ # 验证替换结果(显示几处示例)
331
+ echo "🔍 验证替换结果(前3处):"
332
+ grep -r "$TELEGRAM_PROXY_HOST" "$OPENCLAW_DIR" 2>/dev/null | head -3 | sed 's|.*| &|'
333
+ else
334
+ echo "❌ OpenClaw 目录不存在: $OPENCLAW_DIR"
335
+ fi
336
+ else
337
+ echo "ℹ️ 未设置 TELEGRAM_PROXY_HOST,跳过 Telegram API 替换"
338
+ fi
339
+ PATCH_EOF
340
+ RUN chmod +x /usr/local/bin/patch-telegram-api
341
+
342
+ # 8. 启动脚本
343
+ RUN cat > /usr/local/bin/start-openclaw << 'START_EOF'
344
+ #!/bin/bash
345
+ set -e
346
+
347
+ # 创建必要的目录
348
+ mkdir -p /root/.openclaw
349
+ mkdir -p /root/.openclaw/sessions
350
+ mkdir -p /root/.openclaw/workspace
351
+
352
+ echo "========================================"
353
+ echo "OpenClaw Gateway Starting..."
354
+ echo "========================================"
355
+
356
+ # 尝试恢复数据(如果配置了备份)
357
+ if [ -n "$HF_DATASET" ] && [ -n "$HF_TOKEN" ]; then
358
+ echo "🔄 尝试从 Hugging Face 恢复数据..."
359
+ python3 /usr/local/bin/sync.py restore || echo "⚠️ 恢复失败,继续启动..."
360
+ fi
361
+
362
+ # 执行 Telegram API 地址替换(如果设置了代理)
363
+ /usr/local/bin/patch-telegram-api
364
+
365
+ # 准备 API 配置
366
+ CLEAN_BASE="${API_BASE_URL}"
367
+ CLEAN_BASE=$(echo "$CLEAN_BASE" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|')
368
+
369
+ # 生成令牌(如果没设置)
370
+ if [ -z "$OPENCLAW_GATEWAY_TOKEN" ]; then
371
+ OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 16)
372
+ echo "🔑 生成的网关令牌: $OPENCLAW_GATEWAY_TOKEN"
373
+ fi
374
+
375
+ # 转换可信代理列表为JSON数组
376
+ TRUSTED_PROXIES_JSON=$(echo "$GATEWAY_TRUSTED_PROXIES" | tr ',' '\n' | awk '{ printf "\"%s\",", $0 }' | sed 's/,$//' | sed 's/^/[/' | sed 's/$/]/')
377
+
378
+ # 转换允许的源列表为JSON数组
379
+ ALLOWED_ORIGINS_JSON=$(echo "$GATEWAY_ALLOWED_ORIGINS" | tr ',' '\n' | awk '{ printf "\"%s\",", $0 }' | sed 's/,$//' | sed 's/^/[/' | sed 's/$/]/')
380
+
381
+ # 转换Telegram允许列表为JSON数组
382
+ if [ -n "$TELEGRAM_ALLOW_FROM" ]; then
383
+ # 规范化ID,添加tg:前缀(如果没有)
384
+ TELEGRAM_ALLOW_JSON=$(echo "$TELEGRAM_ALLOW_FROM" | tr ',' '\n' | while read id; do
385
+ id=$(echo "$id" | xargs) # 去除空格
386
+ if [[ "$id" =~ ^[0-9]+$ ]]; then
387
+ echo "\"tg:$id\""
388
+ elif [[ "$id" =~ ^tg: ]]; then
389
+ echo "\"$id\""
390
+ elif [[ "$id" =~ ^@ ]]; then
391
+ echo "\"$id\""
392
+ else
393
+ echo "\"$id\""
394
+ fi
395
+ done | paste -sd ',' | sed 's/^/[/' | sed 's/$/]/')
396
+ else
397
+ TELEGRAM_ALLOW_JSON="[]"
398
+ fi
399
+
400
+ # 创建 OpenClaw 配置(如果配置文件不存在)
401
+ if [ ! -f "/root/.openclaw/openclaw.json" ]; then
402
+ echo "📝 创建 OpenClaw 配置文件..."
403
+ cat > /root/.openclaw/openclaw.json <<OPENCLAW_CONFIG
404
+ {
405
+ "models": {
406
+ "mode": "${MODEL_MODE}",
407
+ "providers": {
408
+ "${MODEL_PROVIDER}": {
409
+ "baseUrl": "${CLEAN_BASE}",
410
+ "apiKey": "${OPENAI_API_KEY}",
411
+ "api": "${API_TYPE}",
412
+ "models": [{
413
+ "id": "${MODEL_ID}",
414
+ "name": "${MODEL_DISPLAY_NAME}",
415
+ "contextWindow": ${MODEL_CONTEXT_WINDOW}
416
+ }]
417
+ }
418
+ }
419
+ },
420
+ "agents": {
421
+ "defaults": {
422
+ "model": {
423
+ "primary": "${MODEL_PROVIDER}/${MODEL_ID}"
424
+ },
425
+ "workspace": "/root/.openclaw/workspace"
426
+ }
427
+ },
428
+ "gateway": {
429
+ "mode": "${OPENCLAW_GATEWAY_MODE}",
430
+ "bind": "${GATEWAY_BIND}",
431
+ "port": ${PORT},
432
+ "trustedProxies": ${TRUSTED_PROXIES_JSON},
433
+ "auth": {
434
+ "mode": "${GATEWAY_AUTH_MODE}",
435
+ "token": "${OPENCLAW_GATEWAY_TOKEN}"
436
+ },
437
+ "remote": {
438
+ "token": "${OPENCLAW_GATEWAY_TOKEN}"
439
+ },
440
+ "controlUi": {
441
+ "allowInsecureAuth": ${CONTROLUI_ALLOW_INSECURE_AUTH},
442
+ "dangerouslyAllowHostHeaderOriginFallback": ${CONTROLUI_DANGEROUS_HOST_HEADER},
443
+ "dangerouslyDisableDeviceAuth": ${CONTROLUI_DANGEROUS_DISABLE_DEVICE_AUTH},
444
+ "allowedOrigins": ${ALLOWED_ORIGINS_JSON}
445
+ }
446
+ },
447
+ "session": {
448
+ "store": "/root/.openclaw/sessions/sessions.json"
449
+ },
450
+ "channels": {
451
+ "telegram": {
452
+ "enabled": ${TELEGRAM_ENABLED},
453
+ "botToken": "${TELEGRAM_BOT_TOKEN}",
454
+ "dmPolicy": "${TELEGRAM_DM_POLICY}",
455
+ "allowFrom": ${TELEGRAM_ALLOW_JSON}
456
+ }
457
+ }
458
+ }
459
+ OPENCLAW_CONFIG
460
+ else
461
+ echo "📁 使用现有的配置文件"
462
+ fi
463
+
464
+ echo "========================================"
465
+ echo "📊 配置信息:"
466
+ echo " • 模型: ${MODEL_ID}"
467
+ echo " • Provider: ${MODEL_PROVIDER}"
468
+ echo " • 端口: ${PORT}"
469
+ echo " • 备份: ${BACKUP_ENABLED}"
470
+ echo " • Allowed Origins: ${GATEWAY_ALLOWED_ORIGINS}"
471
+ echo " • Telegram Enabled: ${TELEGRAM_ENABLED}"
472
+ if [ "${TELEGRAM_ENABLED}" = "true" ]; then
473
+ echo " • Telegram DM Policy: ${TELEGRAM_DM_POLICY}"
474
+ echo " • Telegram Allow From: ${TELEGRAM_ALLOW_FROM}"
475
+ if [ -n "$TELEGRAM_PROXY_HOST" ]; then
476
+ echo " • Telegram Proxy: ${TELEGRAM_PROXY_HOST}"
477
+ fi
478
+ fi
479
+ echo "========================================"
480
+
481
+ # 后台备份循环(如果启用)
482
+ if [ "${BACKUP_ENABLED}" = "true" ] && [ -n "$HF_DATASET" ] && [ -n "$HF_TOKEN" ]; then
483
+ echo "🔄 启动自动备份 (间隔: ${BACKUP_INTERVAL}秒)"
484
+ (
485
+ while true; do
486
+ sleep ${BACKUP_INTERVAL}
487
+ echo "⏰ 执行定时备份..."
488
+ python3 /usr/local/bin/sync.py backup || echo "⚠️ 备份失败"
489
+ done
490
+ ) &
491
+ fi
492
+
493
+ # 运行诊断并启动网关
494
+ echo "🔧 运行 OpenClaw 诊断..."
495
+ openclaw doctor --fix || true
496
+
497
+ echo "🚀 启动 OpenClaw Gateway..."
498
+ echo "========================================"
499
+ exec openclaw gateway run --port $PORT
500
+ START_EOF
501
+ RUN chmod +x /usr/local/bin/start-openclaw
502
+
503
+ EXPOSE 7860
504
+
505
+ CMD ["/usr/local/bin/start-openclaw"]