| |
| FROM node:22-slim |
|
|
| |
| RUN apt-get update && apt-get install -y |
| git python3 python3-pip ca-certificates procps tzdata \ |
| build-essential curl \ |
| |
| chromium fonts-noto-cjk-extra fonts-noto-color-emoji \ |
| && rm -rf /var/lib/apt/lists/* |
|
|
| |
| RUN pip3 install |
|
|
| |
| RUN npm install -g openclaw@latest @marvae24/weibo-cli |
| |
|
|
| |
| ENV TZ=Asia/Shanghai \ |
| PORT=7860 \ |
| HOME=/root \ |
| OPENCLAW_TRUST_LOCAL_WS=1 \ |
| OPENCLAW_SECURITY_STRICT=false \ |
| NODE_TLS_REJECT_UNAUTHORIZED=0 \ |
| OPENCLAW_TRUST_PROXY=true \ |
| NODE_ENV=production \ |
| OPENCLAW_SKIP_CANVAS_HOST=1 |
|
|
| |
| RUN echo 'import os, sys, tarfile, time\n\ |
| from huggingface_hub import HfApi, hf_hub_download\n\ |
| from datetime import datetime, timedelta\n\ |
| api = HfApi()\n\ |
| repo_id = os.getenv("HF_DATASET")\n\ |
| token = os.getenv("HF_TOKEN")\n\ |
| base_dir = "/root"\n\ |
| \n\ |
| def restore():\n\ |
| if not repo_id or not token: return\n\ |
| try:\n\ |
| files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\ |
| now = datetime.now()\n\ |
| for i in range(5):\n\ |
| day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\ |
| name = "backup_" + day + ".tar.gz"\n\ |
| if name in files:\n\ |
| path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)\n\ |
| with tarfile.open(path, "r:gz") as tar: tar.extractall(path=base_dir)\n\ |
| print("--- [Sync] ✅ 恢复成功: " + day + " ---")\n\ |
| return True\n\ |
| except Exception as e: print("--- [Sync] ❌ 恢复失败: " + str(e))\n\ |
| \n\ |
| def backup():\n\ |
| if not repo_id or not token: return\n\ |
| try:\n\ |
| try:\n\ |
| files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\ |
| cutoff = datetime.now() - timedelta(days=14)\n\ |
| for f in files:\n\ |
| if f.startswith("backup_") and f.endswith(".tar.gz"):\n\ |
| try:\n\ |
| if datetime.strptime(f[7:17], "%Y-%m-%d") < cutoff:\n\ |
| api.delete_file(path_in_repo=f, repo_id=repo_id, repo_type="dataset", token=token)\n\ |
| print("--- [Sync] 🗑️ 已清理过期备份: " + f + " ---")\n\ |
| except: pass\n\ |
| except Exception as e: print("--- [Sync] ⚠️ 清理检查失败: " + str(e))\n\ |
| \n\ |
| target_dir = "/root/.openclaw"\n\ |
| if not os.path.exists(target_dir): return\n\ |
| day_str = datetime.now().strftime("%Y-%m-%d")\n\ |
| name = "backup_" + day_str + ".tar.gz"\n\ |
| \n\ |
| def exclude_modules(tarinfo):\n\ |
| if "node_modules" in tarinfo.name: return None\n\ |
| return tarinfo\n\ |
| \n\ |
| with tarfile.open(name, "w:gz") as tar: tar.add(target_dir, arcname=".openclaw", filter=exclude_modules)\n\ |
| api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\ |
| if os.path.exists(name): os.remove(name)\n\ |
| print("--- [Sync] ✨ 备份完成 (已跳过依赖): " + name + " ---")\n\ |
| except Exception as e: print("--- [Sync] ❌ 备份失败: " + str(e))\n\ |
| \n\ |
| def update_env():\n\ |
| import json\n\ |
| path = "/root/.openclaw/openclaw.json"\n\ |
| if not os.path.exists(path): return\n\ |
| try:\n\ |
| with open(path, "r") as f: c = json.load(f)\n\ |
| b = os.getenv("OPENAI_API_BASE", "").replace("/chat/completions", "").replace("/v1/", "/v1")\n\ |
| k = os.getenv("OPENAI_API_KEY", "")\n\ |
| p = os.getenv("OPENCLAW_GATEWAY_PASSWORD", "")\n\ |
| if "models" in c and "providers" in c["models"] and "custom_provider" in c["models"]["providers"]:\n\ |
| if b: c["models"]["providers"]["custom_provider"]["baseUrl"] = b\n\ |
| if k: c["models"]["providers"]["custom_provider"]["apiKey"] = k\n\ |
| if "gateway" in c and "auth" in c["gateway"]:\n\ |
| if p: c["gateway"]["auth"]["token"] = p\n\ |
| with open(path, "w") as f: json.dump(c, f, indent=2)\n\ |
| print("--- [Sync] 🔄 环境变量已热更新至配置文件 ---")\n\ |
| except Exception as e: print("--- [Sync] ⚠️ 配置文件热更新失败: " + str(e))\n\ |
| \n\ |
| if __name__ == "__main__":\n\ |
| if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\ |
| elif len(sys.argv) > 1 and sys.argv[1] == "update": update_env()\n\ |
| else: restore()' > /usr/local/bin/sync.py |
|
|
| |
| RUN echo "#!/bin/bash\n\ |
| set -e\n\ |
| mkdir -p /root/.openclaw\n\ |
| ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime\n\ |
| \n\ |
| python3 /usr/local/bin/sync.py restore\n\ |
| \n\ |
| if ls /root/.openclaw/extensions/*/package.json 1> /dev/null 2>&1; then\n\ |
| for pkg in /root/.openclaw/extensions/*/package.json; do\n\ |
| ext_dir=\$(dirname \"\$pkg\")\n\ |
| echo \"--- [System] 📦 正在重建插件依赖: \$(basename \"\$ext_dir\") ---\"\n\ |
| npm install --prefix \"\$ext_dir\" --production --no-audit --no-fund\n\ |
| done\n\ |
| fi\n\ |
| \n\ |
| find /root/.openclaw -name \"*.lock\" -delete\n\ |
| chmod 700 /root/.openclaw\n\ |
| \n\ |
| if [ ! -f /root/.openclaw/openclaw.json ]; then\n\ |
| echo \"--- [System] 📝 初次运行,生成默认配置... ---\"\n\ |
| CLEAN_BASE=\$(echo \"\$OPENAI_API_BASE\" | sed \"s|/chat/completions||g\" | sed \"s|/v1/|/v1|g\")\n\ |
| cat > /root/.openclaw/openclaw.json <<EOF\n\ |
| {\n\ |
| \"models\": { \ |
| \"providers\": { \ |
| \"custom_provider\": { \ |
| \"baseUrl\": \"\$CLEAN_BASE\", \ |
| \"apiKey\": \"\$OPENAI_API_KEY\", \ |
| \"api\": \"openai-completions\", \ |
| \"models\": [{ \"id\": \"\$MODEL\", \"name\": \"MyModel\", \"contextWindow\": 256000 }] \ |
| } \ |
| } \ |
| },\n\ |
| \"agents\": { \ |
| \"defaults\": { \ |
| \"model\": { \ |
| \"primary\": \"custom_provider/\$MODEL\" \ |
| }, \ |
| \"imageModel\": \"custom_provider/\$MODEL\" \ |
| } \ |
| },\n\ |
| \"gateway\": {\n\ |
| \"mode\": \"local\", \"port\": 7860, \"bind\": \"custom\", \"customBindHost\": \"0.0.0.0\",\n\ |
| \"trustedProxies\": [\"10.0.0.0/8\"],\n\ |
| \"auth\": { \"mode\": \"token\", \"token\": \"\$OPENCLAW_GATEWAY_PASSWORD\" },\n\ |
| \"controlUi\": { \"enabled\": true, \"allowInsecureAuth\": true, \"dangerouslyDisableDeviceAuth\": true, \"dangerouslyAllowHostHeaderOriginFallback\": true, \"allowedOrigins\": [\"https://rickyai365-mypet.hf.space\"] },\n\ |
| \"tools\": { \"deny\": [\"gateway\"] }\n\ |
| }\n\ |
| }\n\ |
| EOF\n\ |
| else\n\ |
| python3 /usr/local/bin/sync.py update\n\ |
| fi\n\ |
| \n\ |
| (while true; do sleep 3600; python3 /usr/local/bin/sync.py backup; done) &\n\ |
| \n\ |
| echo \"--- [System] 🚀 正在启动 OpenClaw Gateway... ---\"\n\ |
| export NODE_ENV=production\n\ |
| export OPENCLAW_TRUST_PROXY=true\n\ |
| \n\ |
| openclaw doctor --fix\n\ |
| exec openclaw gateway run --port 7860\n\ |
| " > /usr/local/bin/start-openclaw && chmod +x /usr/local/bin/start-openclaw |
|
|
| EXPOSE 7860 |
| CMD ["/usr/local/bin/start-openclaw"] |