784 lines
24 KiB
Bash
Executable File
784 lines
24 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "${script_dir}/.." && pwd)"
|
|
source "${script_dir}/lib-nextcloud-apps-path.sh"
|
|
default_qortal_repo_url="https://github.com/Qortal/qortal.git"
|
|
default_external_auth_repo_url="https://gitea.qortal.link/crowetic/Qortal-External-Auth.git"
|
|
|
|
occ_path="/var/www/nextcloud/occ"
|
|
broker_dir="/opt/qortal-broker"
|
|
apache_site_dir="/etc/apache2/sites-available"
|
|
apache_site_name="qortalbroker.conf"
|
|
dry_run="0"
|
|
external_auth_mode="bundled"
|
|
qortal_mode="bundled"
|
|
|
|
nextcloud_url=""
|
|
nextcloud_root=""
|
|
nextcloud_custom_apps_path=""
|
|
broker_host=""
|
|
broker_url=""
|
|
external_auth_url=""
|
|
external_auth_app_id=""
|
|
external_auth_app_secret=""
|
|
qortal_node_url=""
|
|
qortal_node_api_key=""
|
|
service_user="nuqloud_oidc_service"
|
|
service_password=""
|
|
broker_db_password=""
|
|
oidc_client_id="nextcloud-local"
|
|
oidc_client_secret=""
|
|
broker_internal_api_token=""
|
|
external_auth_context=""
|
|
qortal_node_context=""
|
|
external_auth_port="3191"
|
|
broker_port="3000"
|
|
qortal_node_gateway_host_port="12390"
|
|
qortal_node_api_host_port="12391"
|
|
qortal_node_p2p_host_port="12392"
|
|
qortal_node_qdn_host_port="12394"
|
|
external_auth_context_explicit="0"
|
|
qortal_node_context_explicit="0"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./scripts/bootstrap-host-nextcloud-hybrid.sh [options]
|
|
|
|
Prepare sidecar files and rollout notes for an existing host-installed
|
|
Nextcloud instance with Docker sidecars.
|
|
|
|
Options:
|
|
--occ-path <path> Host Nextcloud occ path (default: /var/www/nextcloud/occ)
|
|
--broker-dir <path> Output/runtime dir for broker sidecars (default: /opt/qortal-broker)
|
|
--nextcloud-url <url> Explicit Nextcloud public URL (auto-detected from occ when omitted)
|
|
--broker-host <host> Broker public hostname (default: qortalbroker.<base-domain>)
|
|
--broker-url <url> Broker public URL (default: https://<broker-host>)
|
|
--external-auth bundled|existing External Auth source (default: bundled)
|
|
--external-auth-url <url> External Auth base URL (required for existing mode)
|
|
--external-auth-app-id <id> External Auth app ID (required for existing mode)
|
|
--external-auth-app-secret <sec> External Auth app secret (required for existing mode)
|
|
--with-qortal bundled|skip Qortal node source (default: bundled)
|
|
--qortal-node-url <url> Qortal node base URL (default: bundled node URL)
|
|
--qortal-node-api-key <key> Qortal node API key (optional; auto-generated later when bundled)
|
|
--service-user <user> Nextcloud service auth user (default: nuqloud_oidc_service)
|
|
--service-password <pass> Nextcloud service auth password (auto-generated when omitted)
|
|
--broker-db-password <pass> Broker DB password (auto-generated when omitted)
|
|
--oidc-client-id <id> OIDC client ID (default: nextcloud-local)
|
|
--oidc-client-secret <sec> OIDC client secret (auto-generated when omitted)
|
|
--broker-internal-api-token <tok> Broker internal API token (auto-generated when omitted)
|
|
--external-auth-context <path> Qortal External Auth repo path (default: ../Qortal-External-Auth)
|
|
--qortal-node-context <path> Qortal node repo path (default: ../qortal)
|
|
--nextcloud-custom-apps-path <path> Host Nextcloud app path (default: detected existing apps path, else <occ parent>/apps)
|
|
--broker-port <port> Broker bind port (default: 3000)
|
|
--external-auth-port <port> External Auth bind port (default: 3191)
|
|
--apache-site-dir <path> Apache vhost dir (default: /etc/apache2/sites-available)
|
|
--apache-site-name <file> Apache vhost filename (default: qortalbroker.conf)
|
|
--repo-root <path> Repository root (default: auto)
|
|
--dry-run Print intended actions without writing files
|
|
-h, --help Show this help
|
|
USAGE
|
|
}
|
|
|
|
gen_secret() {
|
|
local generated=""
|
|
while [[ "${#generated}" -lt 32 ]]; do
|
|
generated+="$(
|
|
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom 2>/dev/null | dd bs=64 count=1 status=none 2>/dev/null || true
|
|
)"
|
|
done
|
|
printf '%.32s' "${generated}"
|
|
}
|
|
|
|
trim() {
|
|
printf '%s' "${1:-}" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
|
|
}
|
|
|
|
run_cmd() {
|
|
if [[ "${dry_run}" == "1" ]]; then
|
|
printf '[dry-run] '
|
|
printf '%q ' "$@"
|
|
printf '\n'
|
|
return 0
|
|
fi
|
|
"$@"
|
|
}
|
|
|
|
occ_cmd() {
|
|
if [[ ! -f "${occ_path}" ]]; then
|
|
echo "OCC path not found: ${occ_path}" >&2
|
|
return 1
|
|
fi
|
|
if [[ -z "$(command -v php || true)" ]]; then
|
|
echo "php is required to query occ." >&2
|
|
return 1
|
|
fi
|
|
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
|
|
if command -v sudo >/dev/null 2>&1; then
|
|
sudo -u www-data php "${occ_path}" "$@"
|
|
return $?
|
|
fi
|
|
if command -v runuser >/dev/null 2>&1; then
|
|
runuser -u www-data -- php "${occ_path}" "$@"
|
|
return $?
|
|
fi
|
|
fi
|
|
php "${occ_path}" "$@"
|
|
}
|
|
|
|
derive_nextcloud_url() {
|
|
local detected=""
|
|
detected="$(occ_cmd config:system:get overwrite.cli.url 2>/dev/null | tr -d '\r' || true)"
|
|
if [[ -z "${detected}" ]]; then
|
|
detected="$(occ_cmd config:system:get trusted_domains 0 2>/dev/null | tr -d '\r' || true)"
|
|
if [[ -n "${detected}" && "${detected}" != http* ]]; then
|
|
detected="https://${detected}"
|
|
fi
|
|
fi
|
|
printf '%s' "${detected}"
|
|
}
|
|
|
|
infer_base_domain() {
|
|
local host="${1:-}"
|
|
if [[ "${host}" == *.* ]]; then
|
|
printf '%s' "${host#*.}"
|
|
else
|
|
printf '%s' "${host}"
|
|
fi
|
|
}
|
|
|
|
resolve_context_path() {
|
|
local value="${1:-}"
|
|
if [[ -z "${value}" ]]; then
|
|
printf '%s' ""
|
|
return 0
|
|
fi
|
|
if [[ "${value}" == /* ]]; then
|
|
printf '%s' "${value}"
|
|
else
|
|
printf '%s' "$(cd "${repo_root}" 2>/dev/null && pwd)/${value}"
|
|
fi
|
|
}
|
|
|
|
ensure_context_repo() {
|
|
local label="$1"
|
|
local target_path="$2"
|
|
local repo_url="$3"
|
|
|
|
if [[ -d "${target_path}/.git" ]]; then
|
|
echo "Using existing ${label} repo at ${target_path}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -e "${target_path}" ]]; then
|
|
echo "${label} context exists but is not a git repo: ${target_path}" >&2
|
|
echo "Remove it or pass a valid repo path explicitly." >&2
|
|
return 1
|
|
fi
|
|
|
|
if [[ "${dry_run}" != "1" ]] && ! command -v git >/dev/null 2>&1; then
|
|
echo "git is required to clone ${label} into ${target_path}" >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "Cloning ${label} repo into ${target_path}"
|
|
run_cmd mkdir -p "$(dirname "${target_path}")"
|
|
run_cmd git clone --depth 1 "${repo_url}" "${target_path}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--occ-path)
|
|
occ_path="${2:-}"
|
|
shift 2
|
|
;;
|
|
--occ-path=*)
|
|
occ_path="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-dir)
|
|
broker_dir="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-dir=*)
|
|
broker_dir="${1#*=}"
|
|
shift
|
|
;;
|
|
--nextcloud-url)
|
|
nextcloud_url="${2:-}"
|
|
shift 2
|
|
;;
|
|
--nextcloud-url=*)
|
|
nextcloud_url="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-host)
|
|
broker_host="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-host=*)
|
|
broker_host="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-url)
|
|
broker_url="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-url=*)
|
|
broker_url="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth-url)
|
|
external_auth_url="${2:-}"
|
|
shift 2
|
|
;;
|
|
--external-auth-url=*)
|
|
external_auth_url="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth)
|
|
external_auth_mode="${2:-}"
|
|
shift 2
|
|
;;
|
|
--external-auth=*)
|
|
external_auth_mode="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth-app-id)
|
|
external_auth_app_id="${2:-}"
|
|
shift 2
|
|
;;
|
|
--external-auth-app-id=*)
|
|
external_auth_app_id="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth-app-secret)
|
|
external_auth_app_secret="${2:-}"
|
|
shift 2
|
|
;;
|
|
--external-auth-app-secret=*)
|
|
external_auth_app_secret="${1#*=}"
|
|
shift
|
|
;;
|
|
--with-qortal)
|
|
qortal_mode="${2:-}"
|
|
shift 2
|
|
;;
|
|
--with-qortal=*)
|
|
qortal_mode="${1#*=}"
|
|
shift
|
|
;;
|
|
--qortal-node-url)
|
|
qortal_node_url="${2:-}"
|
|
shift 2
|
|
;;
|
|
--qortal-node-url=*)
|
|
qortal_node_url="${1#*=}"
|
|
shift
|
|
;;
|
|
--qortal-node-api-key)
|
|
qortal_node_api_key="${2:-}"
|
|
shift 2
|
|
;;
|
|
--qortal-node-api-key=*)
|
|
qortal_node_api_key="${1#*=}"
|
|
shift
|
|
;;
|
|
--service-user)
|
|
service_user="${2:-}"
|
|
shift 2
|
|
;;
|
|
--service-user=*)
|
|
service_user="${1#*=}"
|
|
shift
|
|
;;
|
|
--service-password)
|
|
service_password="${2:-}"
|
|
shift 2
|
|
;;
|
|
--service-password=*)
|
|
service_password="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-db-password)
|
|
broker_db_password="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-db-password=*)
|
|
broker_db_password="${1#*=}"
|
|
shift
|
|
;;
|
|
--oidc-client-id)
|
|
oidc_client_id="${2:-}"
|
|
shift 2
|
|
;;
|
|
--oidc-client-id=*)
|
|
oidc_client_id="${1#*=}"
|
|
shift
|
|
;;
|
|
--oidc-client-secret)
|
|
oidc_client_secret="${2:-}"
|
|
shift 2
|
|
;;
|
|
--oidc-client-secret=*)
|
|
oidc_client_secret="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-internal-api-token)
|
|
broker_internal_api_token="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-internal-api-token=*)
|
|
broker_internal_api_token="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth-context)
|
|
external_auth_context="${2:-}"
|
|
external_auth_context_explicit="1"
|
|
shift 2
|
|
;;
|
|
--external-auth-context=*)
|
|
external_auth_context="${1#*=}"
|
|
external_auth_context_explicit="1"
|
|
shift
|
|
;;
|
|
--qortal-node-context)
|
|
qortal_node_context="${2:-}"
|
|
qortal_node_context_explicit="1"
|
|
shift 2
|
|
;;
|
|
--qortal-node-context=*)
|
|
qortal_node_context="${1#*=}"
|
|
qortal_node_context_explicit="1"
|
|
shift
|
|
;;
|
|
--nextcloud-custom-apps-path)
|
|
nextcloud_custom_apps_path="${2:-}"
|
|
shift 2
|
|
;;
|
|
--nextcloud-custom-apps-path=*)
|
|
nextcloud_custom_apps_path="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-port)
|
|
broker_port="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-port=*)
|
|
broker_port="${1#*=}"
|
|
shift
|
|
;;
|
|
--external-auth-port)
|
|
external_auth_port="${2:-}"
|
|
shift 2
|
|
;;
|
|
--external-auth-port=*)
|
|
external_auth_port="${1#*=}"
|
|
shift
|
|
;;
|
|
--apache-site-dir)
|
|
apache_site_dir="${2:-}"
|
|
shift 2
|
|
;;
|
|
--apache-site-dir=*)
|
|
apache_site_dir="${1#*=}"
|
|
shift
|
|
;;
|
|
--apache-site-name)
|
|
apache_site_name="${2:-}"
|
|
shift 2
|
|
;;
|
|
--apache-site-name=*)
|
|
apache_site_name="${1#*=}"
|
|
shift
|
|
;;
|
|
--repo-root)
|
|
repo_root="${2:-}"
|
|
shift 2
|
|
;;
|
|
--repo-root=*)
|
|
repo_root="${1#*=}"
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
dry_run="1"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo "Preparing host-nextcloud-hybrid bootstrap..."
|
|
echo " repo_root: ${repo_root}"
|
|
echo " occ_path: ${occ_path}"
|
|
echo " dry_run: ${dry_run}"
|
|
|
|
if [[ "${repo_root}" != /* ]]; then
|
|
repo_root="$(cd "${repo_root}" 2>/dev/null && pwd)"
|
|
fi
|
|
|
|
external_auth_context="${external_auth_context:-../Qortal-External-Auth}"
|
|
qortal_node_context="${qortal_node_context:-../qortal}"
|
|
external_auth_context="$(resolve_context_path "${external_auth_context}")"
|
|
qortal_node_context="$(resolve_context_path "${qortal_node_context}")"
|
|
nextcloud_root="$(cd "$(dirname "${occ_path}")" 2>/dev/null && pwd || true)"
|
|
if [[ -z "${nextcloud_root}" ]]; then
|
|
echo "Unable to derive Nextcloud root from occ path: ${occ_path}" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "${nextcloud_custom_apps_path}" ]]; then
|
|
nextcloud_custom_apps_path="$(nextcloud_select_apps_path "${nextcloud_root}")"
|
|
else
|
|
if [[ "${nextcloud_custom_apps_path}" != /* ]]; then
|
|
nextcloud_custom_apps_path="$(resolve_context_path "${nextcloud_custom_apps_path}")"
|
|
fi
|
|
selected_nextcloud_apps_path="$(nextcloud_select_apps_path "${nextcloud_root}" "${nextcloud_custom_apps_path}")"
|
|
if [[ -n "${selected_nextcloud_apps_path}" ]]; then
|
|
if [[ "${selected_nextcloud_apps_path}" != "${nextcloud_custom_apps_path}" ]]; then
|
|
echo "Requested Nextcloud app path is not configured; using ${selected_nextcloud_apps_path} instead."
|
|
fi
|
|
nextcloud_custom_apps_path="${selected_nextcloud_apps_path}"
|
|
fi
|
|
fi
|
|
if [[ -z "${nextcloud_custom_apps_path}" ]]; then
|
|
echo "Unable to determine a usable Nextcloud app path from config.php or ${nextcloud_root}/apps." >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p "${nextcloud_custom_apps_path}"
|
|
|
|
if [[ "${external_auth_mode}" != "bundled" && "${external_auth_mode}" != "existing" ]]; then
|
|
echo "Invalid --external-auth value: ${external_auth_mode}" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${qortal_mode}" != "bundled" && "${qortal_mode}" != "skip" ]]; then
|
|
echo "Invalid --with-qortal value: ${qortal_mode}" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${external_auth_mode}" == "bundled" && "${qortal_mode}" != "bundled" ]]; then
|
|
echo "Bundled external_auth currently requires bundled qortal_node." >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${qortal_mode}" == "skip" && -z "${qortal_node_url}" ]]; then
|
|
echo "--qortal-node-url is required when using --with-qortal skip." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${nextcloud_url}" ]]; then
|
|
nextcloud_url="$(derive_nextcloud_url)"
|
|
fi
|
|
nextcloud_url="$(trim "${nextcloud_url}")"
|
|
if [[ -z "${nextcloud_url}" ]]; then
|
|
echo "Could not detect Nextcloud public URL from occ. Pass --nextcloud-url." >&2
|
|
exit 1
|
|
fi
|
|
|
|
nextcloud_host="$(printf '%s' "${nextcloud_url}" | sed -E 's#^[a-z]+://##; s#/.*$##')"
|
|
base_domain="$(infer_base_domain "${nextcloud_host}")"
|
|
|
|
if [[ -z "${broker_host}" ]]; then
|
|
broker_host="qortalbroker.${base_domain}"
|
|
fi
|
|
if [[ -z "${broker_url}" ]]; then
|
|
broker_url="https://${broker_host}"
|
|
fi
|
|
|
|
if [[ "${external_auth_mode}" == "bundled" ]]; then
|
|
external_auth_url="http://external_auth:3191"
|
|
else
|
|
external_auth_url="$(trim "${external_auth_url}")"
|
|
if [[ -z "${external_auth_url}" ]]; then
|
|
echo "--external-auth-url is required when using --external-auth existing" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "${external_auth_app_id}" || -z "${external_auth_app_secret}" ]]; then
|
|
echo "--external-auth-app-id and --external-auth-app-secret are required for external auth existing mode." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
if [[ -z "${qortal_node_url}" && "${qortal_mode}" == "bundled" ]]; then
|
|
qortal_node_url="http://qortal_node:12391"
|
|
fi
|
|
|
|
host_external_auth_url="${external_auth_url}"
|
|
if [[ "${external_auth_mode}" == "bundled" ]]; then
|
|
host_external_auth_url="http://127.0.0.1:${external_auth_port}"
|
|
fi
|
|
|
|
host_qortal_node_url="${qortal_node_url}"
|
|
if [[ "${qortal_mode}" == "bundled" ]]; then
|
|
host_qortal_node_url="http://127.0.0.1:${qortal_node_api_host_port}"
|
|
fi
|
|
|
|
if [[ -z "${service_password}" ]]; then
|
|
service_password="$(gen_secret)"
|
|
fi
|
|
if [[ -z "${broker_db_password}" ]]; then
|
|
broker_db_password="$(gen_secret)"
|
|
fi
|
|
if [[ -z "${oidc_client_secret}" ]]; then
|
|
oidc_client_secret="$(gen_secret)"
|
|
fi
|
|
if [[ -z "${broker_internal_api_token}" ]]; then
|
|
broker_internal_api_token="$(gen_secret)"
|
|
fi
|
|
|
|
compose_src="${repo_root}/deploy/docker-compose.host-hybrid.yml"
|
|
compose_dst="${broker_dir}/docker-compose.yml"
|
|
env_dst="${broker_dir}/.env"
|
|
apache_site_path="${apache_site_dir%/}/${apache_site_name}"
|
|
apache_preview_path="${broker_dir}/${apache_site_name}"
|
|
notes_path="${broker_dir}/NEXT-STEPS.txt"
|
|
compose_profiles_list=()
|
|
if [[ "${external_auth_mode}" == "bundled" ]]; then
|
|
compose_profiles_list+=("external-auth")
|
|
fi
|
|
if [[ "${qortal_mode}" == "bundled" ]]; then
|
|
compose_profiles_list+=("qortal")
|
|
fi
|
|
compose_profiles="$(IFS=,; printf '%s' "${compose_profiles_list[*]}")"
|
|
|
|
if [[ ! -f "${compose_src}" ]]; then
|
|
echo "Missing compose source file: ${compose_src}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${external_auth_mode}" == "bundled" ]]; then
|
|
if [[ ! -d "${external_auth_context}/.git" ]]; then
|
|
if [[ "${external_auth_context_explicit}" == "1" ]]; then
|
|
echo "External Auth context not found or is not a git repo: ${external_auth_context}" >&2
|
|
echo "Pass a valid --external-auth-context or omit it to auto-clone the default repo." >&2
|
|
exit 1
|
|
fi
|
|
ensure_context_repo "Qortal External Auth" "${external_auth_context}" "${default_external_auth_repo_url}"
|
|
else
|
|
echo "Using existing Qortal External Auth repo at ${external_auth_context}"
|
|
fi
|
|
fi
|
|
|
|
if [[ "${qortal_mode}" == "bundled" ]]; then
|
|
if [[ ! -d "${qortal_node_context}/.git" ]]; then
|
|
if [[ "${qortal_node_context_explicit}" == "1" ]]; then
|
|
echo "Qortal node context not found or is not a git repo: ${qortal_node_context}" >&2
|
|
echo "Pass a valid --qortal-node-context or omit it to auto-clone the default repo." >&2
|
|
exit 1
|
|
fi
|
|
ensure_context_repo "Qortal Core" "${qortal_node_context}" "${default_qortal_repo_url}"
|
|
else
|
|
echo "Using existing Qortal Core repo at ${qortal_node_context}"
|
|
fi
|
|
fi
|
|
|
|
env_content=$(cat <<EOF
|
|
BROKER_BUILD_CONTEXT=${repo_root}/services/qortal-oidc-broker
|
|
BROKER_PORT=${broker_port}
|
|
BROKER_DB_NAME=qortal_broker
|
|
BROKER_DB_USER=qortal_broker
|
|
BROKER_DB_PASSWORD=${broker_db_password}
|
|
BROKER_DATABASE_URL=postgresql://qortal_broker:${broker_db_password}@broker_db:5432/qortal_broker
|
|
COMPOSE_PROFILES=${compose_profiles}
|
|
COMPOSE_PROJECT_NAME=nuqloud-broker
|
|
|
|
NEXTCLOUD_BASE_URL=${nextcloud_url}
|
|
NEXTCLOUD_PUBLIC_URL=${nextcloud_url}
|
|
NEXTCLOUD_ROOT=${nextcloud_root}
|
|
NEXTCLOUD_CUSTOM_APPS_PATH=${nextcloud_custom_apps_path}
|
|
HOST_NEXTCLOUD_EXTERNAL_AUTH_URL=${host_external_auth_url}
|
|
HOST_NEXTCLOUD_QORTAL_NODE_URL=${host_qortal_node_url}
|
|
NEXTCLOUD_SERVICE_USER=${service_user}
|
|
NEXTCLOUD_SERVICE_PASSWORD=${service_password}
|
|
|
|
QORTAL_EXTERNAL_AUTH_BASE_URL=${external_auth_url}
|
|
QORTAL_EXTERNAL_AUTH_APP_ID=${external_auth_app_id}
|
|
QORTAL_EXTERNAL_AUTH_APP_SECRET=${external_auth_app_secret}
|
|
BROKER_INTERNAL_API_TOKEN=${broker_internal_api_token}
|
|
BROKER_CORS_ALLOWED_ORIGINS=${nextcloud_url}
|
|
BROKER_REQUEST_BODY_LIMIT=64mb
|
|
|
|
OIDC_ISSUER=${broker_url}
|
|
OIDC_CLIENT_ID=${oidc_client_id}
|
|
OIDC_CLIENT_SECRET=${oidc_client_secret}
|
|
OIDC_REDIRECT_URI_ALLOWLIST=${nextcloud_url}/apps/user_oidc/code
|
|
OIDC_POLICY_MODE=auto_provision
|
|
OIDC_AUTO_PROVISION_GUARD=invite_or_allowlist
|
|
OIDC_REQUIRE_EMAIL_FOR_NEW_ACCOUNT=true
|
|
OIDC_BRANDING_MODE=powered
|
|
OIDC_INVITE_TTL_SECONDS=604800
|
|
OIDC_AUTH_REQUEST_TTL_SECONDS=600
|
|
OIDC_AUTH_CODE_TTL_SECONDS=120
|
|
OIDC_ACCESS_TOKEN_TTL_SECONDS=600
|
|
OIDC_ID_TOKEN_TTL_SECONDS=600
|
|
OIDC_PRIVATE_KEY_PEM=
|
|
OIDC_PRIVATE_KEY_PEM_B64=
|
|
OIDC_KEY_ID=
|
|
|
|
EXTERNAL_AUTH_CONTEXT=${external_auth_context}
|
|
EXTERNAL_AUTH_PORT=${external_auth_port}
|
|
QORTAL_NODE_CONTEXT=${qortal_node_context}
|
|
QORTAL_NODE_URL=${qortal_node_url}
|
|
QORTAL_NODE_API_KEY=${qortal_node_api_key}
|
|
QORTAL_NODE_GATEWAY_HOST_PORT=${qortal_node_gateway_host_port}
|
|
QORTAL_NODE_API_HOST_PORT=${qortal_node_api_host_port}
|
|
QORTAL_NODE_P2P_HOST_PORT=${qortal_node_p2p_host_port}
|
|
QORTAL_NODE_QDN_HOST_PORT=${qortal_node_qdn_host_port}
|
|
QORTAL_AUTH_NODE_URL=${qortal_node_url}
|
|
QORTAL_AUTH_NODE_API_KEY=${qortal_node_api_key}
|
|
QORTAL_AUTH_NODE_API_KEY_MODE=paths
|
|
QORTAL_AUTH_NODE_API_KEY_PATHS=/
|
|
|
|
HOST_NEXTCLOUD_OCC_PATH=${occ_path}
|
|
HOST_NEXTCLOUD_ROOT=${nextcloud_root}
|
|
HOST_NEXTCLOUD_CUSTOM_APPS_PATH=${nextcloud_custom_apps_path}
|
|
HOST_NEXTCLOUD_BROKER_DIR=${broker_dir}
|
|
HOST_NEXTCLOUD_BROKER_HOST=${broker_host}
|
|
HOST_NEXTCLOUD_BROKER_URL=${broker_url}
|
|
HOST_NEXTCLOUD_APACHE_SITE_DIR=${apache_site_dir}
|
|
HOST_NEXTCLOUD_APACHE_SITE_NAME=${apache_site_name}
|
|
HOST_NEXTCLOUD_APACHE_SITE_PATH=${apache_site_path}
|
|
HOST_NEXTCLOUD_EXTERNAL_AUTH_MODE=${external_auth_mode}
|
|
HOST_NEXTCLOUD_EXTERNAL_AUTH_CONTEXT=${external_auth_context}
|
|
HOST_NEXTCLOUD_QORTAL_MODE=${qortal_mode}
|
|
HOST_NEXTCLOUD_QORTAL_NODE_CONTEXT=${qortal_node_context}
|
|
HOST_NEXTCLOUD_EXTERNAL_AUTH_PORT=${external_auth_port}
|
|
HOST_NEXTCLOUD_QORTAL_NODE_API_HOST_PORT=${qortal_node_api_host_port}
|
|
EOF
|
|
)
|
|
|
|
apache_content=$(cat <<EOF
|
|
<VirtualHost *:80>
|
|
ServerName ${broker_host}
|
|
ProxyPreserveHost On
|
|
ProxyPass / http://127.0.0.1:${broker_port}/
|
|
ProxyPassReverse / http://127.0.0.1:${broker_port}/
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
ErrorLog \${APACHE_LOG_DIR}/qortalbroker_error.log
|
|
CustomLog \${APACHE_LOG_DIR}/qortalbroker_access.log combined
|
|
</VirtualHost>
|
|
EOF
|
|
)
|
|
|
|
notes_content=$(cat <<EOF
|
|
Host Nextcloud Hybrid Bootstrap Notes
|
|
===================================
|
|
|
|
This is the low-level bootstrap artifact for host-hybrid adoption.
|
|
Use ./scripts/install-host-nextcloud-hybrid.sh for the full install/update flow.
|
|
|
|
Detected:
|
|
occ_path=${occ_path}
|
|
nextcloud_url=${nextcloud_url}
|
|
broker_url=${broker_url}
|
|
external_auth_mode=${external_auth_mode}
|
|
external_auth_url=${external_auth_url}
|
|
qortal_mode=${qortal_mode}
|
|
broker_dir=${broker_dir}
|
|
nextcloud_custom_apps_path=${nextcloud_custom_apps_path}
|
|
qortal_node_url=${qortal_node_url}
|
|
host_external_auth_url=${host_external_auth_url}
|
|
host_qortal_node_url=${host_qortal_node_url}
|
|
|
|
Files written:
|
|
${compose_dst}
|
|
${env_dst}
|
|
${apache_preview_path}
|
|
|
|
1. Review the generated env file.
|
|
|
|
2. Start the sidecars:
|
|
docker compose -f ${compose_dst} --env-file ${env_dst} up -d
|
|
|
|
3. If using bundled External Auth, register a broker app after the service is healthy:
|
|
curl -sS -X POST http://127.0.0.1:${external_auth_port}/apps/register \\
|
|
-H "Content-Type: application/json" \\
|
|
-d '{"name":"qortal-nextcloud-integration"}'
|
|
|
|
Then set the returned values in ${env_dst}:
|
|
- QORTAL_EXTERNAL_AUTH_APP_ID
|
|
- QORTAL_EXTERNAL_AUTH_APP_SECRET
|
|
|
|
And restart broker:
|
|
docker compose -f ${compose_dst} --env-file ${env_dst} up -d --force-recreate broker
|
|
|
|
4. Install Apache proxy site:
|
|
sudo a2enmod proxy proxy_http headers ssl
|
|
sudo cp ${apache_preview_path} ${apache_site_path}
|
|
sudo a2ensite ${apache_site_name}
|
|
sudo systemctl reload apache2
|
|
|
|
5. Ensure the dedicated Nextcloud service user exists:
|
|
export NC_SERVICE_PASSWORD='${service_password}'
|
|
sudo --preserve-env=NC_SERVICE_PASSWORD -u www-data php ${occ_path} user:add --password-from-env --display-name='${service_user}' ${service_user}
|
|
|
|
6. Copy/enable NuQloud apps as needed:
|
|
sudo -u www-data php ${occ_path} app:enable qortal_integration
|
|
sudo -u www-data php ${occ_path} app:enable --force user_oidc
|
|
|
|
7. Configure OIDC provider:
|
|
sudo -u www-data php ${occ_path} user_oidc:provider qortal -c ${oidc_client_id} -s '${oidc_client_secret}' -d ${broker_url}/.well-known/openid-configuration --scope='openid profile email' --mapping-uid=sub --mapping-display-name=name --mapping-email=email
|
|
|
|
8. Optional NuQloud login label:
|
|
sudo -u www-data php ${occ_path} config:system:set user_oidc login_label --value='Create Account / Login'
|
|
|
|
9. Save broker internal token in app config:
|
|
sudo -u www-data php ${occ_path} config:app:set qortal_integration broker_internal_api_token --value='${broker_internal_api_token}'
|
|
|
|
10. Health checks:
|
|
curl -sS ${broker_url}/.well-known/openid-configuration
|
|
curl -sS ${host_external_auth_url}/health
|
|
sudo -u www-data php ${occ_path} app:list | rg 'qortal_integration|user_oidc'
|
|
EOF
|
|
)
|
|
|
|
echo "Bootstrapping host-nextcloud-hybrid sidecar files..."
|
|
echo " occ_path: ${occ_path}"
|
|
echo " nextcloud_url: ${nextcloud_url}"
|
|
echo " broker_url: ${broker_url}"
|
|
echo " external_auth_mode: ${external_auth_mode}"
|
|
echo " qortal_mode: ${qortal_mode}"
|
|
echo " broker_dir: ${broker_dir}"
|
|
echo " nextcloud_custom_apps_path: ${nextcloud_custom_apps_path}"
|
|
echo " qortal_node_url: ${qortal_node_url}"
|
|
echo
|
|
|
|
run_cmd mkdir -p "${broker_dir}"
|
|
run_cmd cp "${compose_src}" "${compose_dst}"
|
|
if [[ "${dry_run}" == "1" ]]; then
|
|
echo "[dry-run] write ${env_dst}"
|
|
else
|
|
printf '%s\n' "${env_content}" > "${env_dst}"
|
|
fi
|
|
if [[ "${dry_run}" == "1" ]]; then
|
|
echo "[dry-run] write ${apache_preview_path}"
|
|
else
|
|
printf '%s\n' "${apache_content}" > "${apache_preview_path}"
|
|
fi
|
|
if [[ "${dry_run}" == "1" ]]; then
|
|
echo "[dry-run] write ${notes_path}"
|
|
else
|
|
printf '%s\n' "${notes_content}" > "${notes_path}"
|
|
fi
|
|
|
|
if [[ "${external_auth_mode}" == "bundled" ]]; then
|
|
echo "Bundled external_auth mode selected."
|
|
echo "Start the sidecars first, then register an app against http://127.0.0.1:${external_auth_port}/apps/register."
|
|
echo
|
|
elif [[ -z "${external_auth_app_id}" || -z "${external_auth_app_secret}" ]]; then
|
|
echo "Warning: External Auth app credentials are empty in generated .env."
|
|
echo "Fill QORTAL_EXTERNAL_AUTH_APP_ID / QORTAL_EXTERNAL_AUTH_APP_SECRET before starting broker."
|
|
echo
|
|
fi
|
|
|
|
echo "Bootstrap artifacts ready."
|
|
echo " compose: ${compose_dst}"
|
|
echo " env: ${env_dst}"
|
|
echo " apache: ${apache_preview_path}"
|
|
echo " notes: ${notes_path}"
|