feat(storage): support configurable install root

Add a LuCI install-root input, persist the selected path in UCI,
and route install, status, backup, uninstall, and runtime scripts
through the configured storage root for new installs.

Reference: custom install root flow
This commit is contained in:
2026-03-18 13:48:07 +08:00
parent ee10bb0bd5
commit 68f24e6658
17 changed files with 739 additions and 122 deletions

View File

@@ -29,9 +29,8 @@ get_pid_by_port() {
}
# ── 路径 (OpenWrt 适配) ──
NODE_BASE="${NODE_BASE:-/opt/openclaw/node}"
OC_GLOBAL="${OC_GLOBAL:-/opt/openclaw/global}"
OC_DATA="${OC_DATA:-/opt/openclaw/data}"
. /usr/libexec/openclaw-paths.sh
oc_load_paths "$OPENCLAW_INSTALL_ROOT"
NODE_BIN="${NODE_BASE}/bin/node"
OC_STATE_DIR="${OC_DATA}/.openclaw"
CONFIG_FILE="${OC_STATE_DIR}/openclaw.json"
@@ -99,7 +98,7 @@ json_set() {
local parent_dir="$(dirname "$CONFIG_FILE")"
if ! mkdir -p "$parent_dir" 2>/dev/null; then
echo "ERROR: 无法创建配置目录 $parent_dir" >&2
echo "HINT: 请检查 /opt/openclaw/data 是否存在且有写权限" >&2
echo "HINT: 请检查 ${OC_DATA} 是否存在且有写权限" >&2
return 1
fi
@@ -2362,7 +2361,7 @@ backup_restore_menu() {
# 提取 payload 到根目录 (还原到原始绝对路径)
tar -xzf "$latest" --strip-components=3 -C / "${backup_name}/payload/posix/" 2>&1
# 修复权限
chown -R openclaw:openclaw /opt/openclaw/data/.openclaw 2>/dev/null
chown -R openclaw:openclaw "${OC_DATA}/.openclaw" 2>/dev/null
echo -e " ${GREEN}✅ 配置和数据已完整恢复!原配置已保存为 openclaw.json.pre-restore${NC}"
echo ""
prompt_with_default "是否重启服务使配置生效? (Y/n)" "Y" do_restart

View File

@@ -14,12 +14,36 @@ const fs = require('fs');
const path = require('path');
const os = require('os');
function loadInstallRoot() {
if (process.env.OPENCLAW_INSTALL_ROOT) {
return normalizeInstallRoot(process.env.OPENCLAW_INSTALL_ROOT);
}
try {
const { execSync } = require('child_process');
return normalizeInstallRoot(execSync('uci -q get openclaw.main.install_root 2>/dev/null', {
encoding: 'utf8',
timeout: 3000,
}).trim());
} catch {
return '/opt';
}
}
function normalizeInstallRoot(value) {
const cleaned = (value || '').trim();
if (!cleaned || cleaned[0] !== '/' || /\s/.test(cleaned)) return '/opt';
const normalized = cleaned.replace(/\/+$/, '');
return normalized || '/';
}
// ── 配置 (OpenWrt 适配) ──
const PORT = parseInt(process.env.OC_CONFIG_PORT || '18793', 10);
const HOST = process.env.OC_CONFIG_HOST || '0.0.0.0'; // token 认证保护,可安全绑定所有接口
const NODE_BASE = process.env.NODE_BASE || '/opt/openclaw/node';
const OC_GLOBAL = process.env.OC_GLOBAL || '/opt/openclaw/global';
const OC_DATA = process.env.OC_DATA || '/opt/openclaw/data';
const INSTALL_ROOT = loadInstallRoot();
const OC_ROOT = INSTALL_ROOT === '/' ? '/openclaw' : `${INSTALL_ROOT}/openclaw`;
const NODE_BASE = process.env.NODE_BASE || `${OC_ROOT}/node`;
const OC_GLOBAL = process.env.OC_GLOBAL || `${OC_ROOT}/global`;
const OC_DATA = process.env.OC_DATA || `${OC_ROOT}/data`;
const SCRIPT_PATH = process.env.OC_CONFIG_SCRIPT || '/usr/share/openclaw/oc-config.sh';
const SSL_CERT = '/etc/uhttpd.crt';
const SSL_KEY = '/etc/uhttpd.key';
@@ -170,6 +194,7 @@ class PtySession {
const env = {
...process.env, TERM: 'xterm-256color', COLUMNS: String(this.cols), LINES: String(this.rows),
COLORTERM: 'truecolor', LANG: 'en_US.UTF-8',
OPENCLAW_INSTALL_ROOT: INSTALL_ROOT,
NODE_BASE, OC_GLOBAL, OC_DATA,
HOME: OC_DATA,
OPENCLAW_HOME: OC_DATA,