224 lines
7.8 KiB
Shell
Executable file
224 lines
7.8 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
if [[ ${EUID} -ne 0 ]]; then
|
|
echo "run as root" >&2
|
|
exit 1
|
|
fi
|
|
|
|
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
install_root=/opt/guanghu/lake-lamp-authz
|
|
state_root=/var/lib/guanghu/architecture-provision
|
|
secret_root=/etc/guanghu/secrets/lake-lamp
|
|
authorization_env=$secret_root/authorization.env
|
|
provider_registry=/etc/guanghu/secrets/hololake-ai-providers.json
|
|
knowledge_repo=/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1/data/repositories/bingshuo/hololake-knowledge-base.git
|
|
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
|
backup_root=$state_root/manual-backups/lake-lamp-authz-$stamp
|
|
|
|
required_source_files=(
|
|
server.js
|
|
workorder-manager.js
|
|
map-gate.js
|
|
smtp-mailer.js
|
|
action-client.js
|
|
architecture-provision-broker.js
|
|
deployment-event.js
|
|
deployment-event-worker.js
|
|
deployment-source-policy.js
|
|
guanghu-router.js
|
|
repo-push-broker.js
|
|
hololake-session.js
|
|
hololake-capabilities.js
|
|
)
|
|
|
|
for file in "${required_source_files[@]}" lake-lamp-authz.service lake-lamp-architecture-provision.service lake-lamp-deployment-event-worker.service; do
|
|
[[ -f "$script_dir/$file" ]] || {
|
|
echo "missing required release file: $file" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
install -d -m 0700 "$backup_root"
|
|
[[ ! -e "$install_root" ]] || cp -a "$install_root" "$backup_root/install-root"
|
|
for existing in \
|
|
/etc/systemd/system/lake-lamp-authz.service \
|
|
/etc/systemd/system/lake-lamp-architecture-provision.service \
|
|
/etc/systemd/system/lake-lamp-deployment-event-worker.service \
|
|
"$authorization_env" \
|
|
"$provider_registry"; do
|
|
if [[ -e "$existing" ]]; then
|
|
destination=$backup_root/existing${existing}
|
|
install -d -m 0700 "$(dirname "$destination")"
|
|
cp -a "$existing" "$destination"
|
|
fi
|
|
done
|
|
|
|
rollback() {
|
|
set +e
|
|
if [[ -d "$backup_root/install-root" ]]; then
|
|
rm -rf -- "$install_root"
|
|
cp -a "$backup_root/install-root" "$install_root"
|
|
fi
|
|
for existing in \
|
|
/etc/systemd/system/lake-lamp-authz.service \
|
|
/etc/systemd/system/lake-lamp-architecture-provision.service \
|
|
/etc/systemd/system/lake-lamp-deployment-event-worker.service \
|
|
"$authorization_env" \
|
|
"$provider_registry"; do
|
|
saved=$backup_root/existing${existing}
|
|
if [[ -e "$saved" ]]; then
|
|
install -d -m 0755 "$(dirname "$existing")"
|
|
cp -a "$saved" "$existing"
|
|
elif [[ "$existing" = "$provider_registry" ]]; then
|
|
rm -f -- "$existing"
|
|
fi
|
|
done
|
|
systemctl daemon-reload
|
|
systemctl restart lake-lamp-authz.service
|
|
systemctl restart lake-lamp-architecture-provision.service
|
|
systemctl restart lake-lamp-deployment-event-worker.service
|
|
}
|
|
trap 'rc=$?; if [[ $rc -ne 0 ]]; then rollback; fi; exit "$rc"' EXIT
|
|
|
|
install -d -m 0755 "$install_root"
|
|
for file in "${required_source_files[@]}"; do
|
|
install -m 0644 "$script_dir/$file" "$install_root/$file"
|
|
done
|
|
install -m 0644 "$script_dir/lake-lamp-authz.service" /etc/systemd/system/lake-lamp-authz.service
|
|
install -m 0644 "$script_dir/lake-lamp-architecture-provision.service" /etc/systemd/system/lake-lamp-architecture-provision.service
|
|
install -m 0644 "$script_dir/lake-lamp-deployment-event-worker.service" /etc/systemd/system/lake-lamp-deployment-event-worker.service
|
|
install -d -m 0700 "$state_root"
|
|
install -d -m 0750 /var/lib/guanghu/deployment-events
|
|
install -d -m 0700 /var/lib/guanghu/deployment-events/receipts
|
|
install -d -m 0755 /opt/guanghu/architecture-releases
|
|
install -d -m 0755 /etc/guanghu/lake-lamp
|
|
install -d -m 0750 "$secret_root"
|
|
if [[ ! -e /etc/guanghu/lake-lamp/deployment-repositories.json ]]; then
|
|
install -m 0644 "$script_dir/deployment-repositories.example.json" /etc/guanghu/lake-lamp/deployment-repositories.json
|
|
fi
|
|
|
|
[[ -f "$authorization_env" ]] || {
|
|
echo "private authorization environment is missing" >&2
|
|
exit 1
|
|
}
|
|
|
|
append_setting() {
|
|
local key=$1
|
|
local value=$2
|
|
if ! grep -q "^${key}=" "$authorization_env"; then
|
|
printf '%s=%s\n' "$key" "$value" >>"$authorization_env"
|
|
fi
|
|
}
|
|
|
|
if ! grep -q '^HOLOLAKE_SESSION_PEPPER=' "$authorization_env"; then
|
|
append_setting HOLOLAKE_SESSION_PEPPER "$(/usr/bin/openssl rand -hex 32)"
|
|
fi
|
|
append_setting HOLOLAKE_SESSION_STATE_FILE /var/lib/guanghu/lake-lamp-authz/hololake-sessions.json
|
|
append_setting HOLOLAKE_OTP_TTL 600
|
|
append_setting HOLOLAKE_ACCOUNT_SESSION_TTL 86400
|
|
append_setting HOLOLAKE_OTP_REQUEST_LIMIT 6
|
|
append_setting HOLOLAKE_KNOWLEDGE_REPOSITORY_PATH "$knowledge_repo"
|
|
append_setting HOLOLAKE_KNOWLEDGE_MAX_ARCHIVE_BYTES 134217728
|
|
append_setting HOLOLAKE_AI_PROVIDERS_FILE "$provider_registry"
|
|
chmod 0600 "$authorization_env"
|
|
|
|
if [[ ! -f "$provider_registry" ]]; then
|
|
/usr/bin/python3 - "$provider_registry" <<'PY'
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
|
|
destination = pathlib.Path(sys.argv[1])
|
|
values = {}
|
|
roots = (
|
|
pathlib.Path("/etc/guanghu/secrets"),
|
|
pathlib.Path("/etc/guanghu/persona-secrets"),
|
|
)
|
|
for root in roots:
|
|
if not root.exists():
|
|
continue
|
|
for file in root.rglob("*.env"):
|
|
try:
|
|
for raw in file.read_text().splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
values.setdefault(key.strip(), value.strip().strip("'\""))
|
|
except (OSError, UnicodeError):
|
|
continue
|
|
|
|
providers = {}
|
|
if values.get("DEEPSEEK_API_KEY"):
|
|
providers["deepseek"] = {
|
|
"name": "DeepSeek",
|
|
"base_url": "https://api.deepseek.com",
|
|
"api_key": values["DEEPSEEK_API_KEY"],
|
|
"models": ["deepseek-chat", "deepseek-reasoner"],
|
|
}
|
|
elif values.get("OPENAI_API_KEY"):
|
|
providers["openai"] = {
|
|
"name": "OpenAI",
|
|
"base_url": "https://api.openai.com/v1",
|
|
"api_key": values["OPENAI_API_KEY"],
|
|
"models": ["gpt-4.1-mini"],
|
|
}
|
|
elif values.get("DASHSCOPE_API_KEY"):
|
|
providers["qwen"] = {
|
|
"name": "Qwen",
|
|
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
"api_key": values["DASHSCOPE_API_KEY"],
|
|
"models": ["qwen-plus"],
|
|
}
|
|
|
|
if providers:
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
temporary = destination.with_suffix(".tmp")
|
|
temporary.write_text(json.dumps({
|
|
"schema": "guanghu.hololake-ai-providers/v1",
|
|
"providers": providers,
|
|
}, ensure_ascii=False, indent=2) + "\n")
|
|
temporary.chmod(0o640)
|
|
temporary.replace(destination)
|
|
PY
|
|
fi
|
|
|
|
if [[ -f "$provider_registry" ]]; then
|
|
chown root:guanghu-authz "$provider_registry"
|
|
chmod 0640 "$provider_registry"
|
|
fi
|
|
|
|
[[ -d "$knowledge_repo" ]] || {
|
|
echo "registered HoloLake knowledge repository is missing" >&2
|
|
exit 1
|
|
}
|
|
command -v setfacl >/dev/null 2>&1 || {
|
|
echo "setfacl is required for private knowledge repository access" >&2
|
|
exit 1
|
|
}
|
|
setfacl -R -m u:guanghu-authz:rX "$knowledge_repo"
|
|
setfacl -R -d -m u:guanghu-authz:rX "$knowledge_repo"
|
|
runuser -u guanghu-authz -- git --git-dir="$knowledge_repo" rev-parse --verify refs/heads/main >/dev/null
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable --now lake-lamp-architecture-provision.service
|
|
systemctl restart lake-lamp-authz.service
|
|
systemctl enable --now lake-lamp-deployment-event-worker.service
|
|
systemctl is-active --quiet lake-lamp-architecture-provision.service
|
|
systemctl is-active --quiet lake-lamp-authz.service
|
|
systemctl is-active --quiet lake-lamp-deployment-event-worker.service
|
|
health=$(/usr/bin/curl -fsS --max-time 10 http://127.0.0.1:3921/health)
|
|
/usr/bin/node -e '
|
|
const health = JSON.parse(process.argv[1]);
|
|
if (!health.ok || health.service !== "lake-lamp-authz") process.exit(1);
|
|
if (!health.hololake_mobile || !health.hololake_mobile.email_session || !health.hololake_mobile.knowledge_snapshot) process.exit(1);
|
|
' "$health"
|
|
if [[ -f "$provider_registry" ]]; then
|
|
/usr/bin/node -e '
|
|
const health = JSON.parse(process.argv[1]);
|
|
if (!health.hololake_mobile || !health.hololake_mobile.ai_gateway) process.exit(1);
|
|
' "$health"
|
|
fi
|
|
trap - EXIT
|
|
printf 'HOLOLAKE_MOBILE_CAPABILITIES_INSTALLED backup=%s\n' "$backup_root"
|