#!/usr/bin/env bash set -euo pipefail if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then echo "Run this installer as root (sudo)." exit 1 fi script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd "${script_dir}/.." && pwd)" default_nc_path="/var/www/nextcloud" read -r -p "Nextcloud path [${default_nc_path}]: " nc_path nc_path="${nc_path:-$default_nc_path}" if [[ ! -f "${nc_path}/occ" ]]; then echo "Could not find occ at ${nc_path}/occ. Please check the Nextcloud path." exit 1 fi occ() { if command -v sudo >/dev/null 2>&1; then sudo -u www-data php "${nc_path}/occ" "$@" else runuser -u www-data -- php "${nc_path}/occ" "$@" fi } occ_app_get() { local app_id="${1:-}" local key="${2:-}" local value="" if [[ -z "${app_id}" || -z "${key}" ]]; then return 1 fi value="$(occ config:app:get "${app_id}" "${key}" 2>/dev/null | tr -d '\r' || true)" if [[ "${value}" == "false" ]]; then value="" fi echo "${value}" } normalize_bool_01() { local raw="${1:-}" case "$(echo "${raw}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" in 1|true|yes|y|on) echo "1" ;; 0|false|no|n|off) echo "0" ;; *) echo "" ;; esac } gen_secret() { tr -dc 'a-zA-Z0-9' /dev/null 2>&1 || status_local=1 java_path="$(occ_app_get "libresign" "java_path")" fi if [[ "${auto_install}" == "1" && -z "${jsignpdf_path}" ]]; then occ libresign:install --jsignpdf >/dev/null 2>&1 || status_local=1 jsignpdf_path="$(occ_app_get "libresign" "jsignpdf_jar_path")" fi if [[ "${auto_install}" == "1" && -z "${pdftk_path}" ]]; then occ libresign:install --pdftk >/dev/null 2>&1 || status_local=1 pdftk_path="$(occ_app_get "libresign" "pdftk_path")" fi if [[ "${auto_setup}" == "1" && -z "${root_cert}" ]]; then engine="$(echo "${LIBRESIGN_CERT_ENGINE:-openssl}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" if [[ "${engine}" != "cfssl" ]]; then engine="openssl" fi cn="$(libresign_default_cn)" org="${LIBRESIGN_CERT_O:-${NUQLOUD_BILLING_COMPANY_NAME:-NuQloud}}" country="${LIBRESIGN_CERT_C:-}" state="${LIBRESIGN_CERT_ST:-}" locality="${LIBRESIGN_CERT_L:-}" ou_raw="${LIBRESIGN_CERT_OU:-Document Signing}" occ_args=("libresign:configure:${engine}" "--cn=${cn}") if [[ -n "${org}" ]]; then occ_args+=("--o=${org}") fi if [[ -n "${country}" ]]; then occ_args+=("--c=${country}") fi if [[ -n "${state}" ]]; then occ_args+=("--st=${state}") fi if [[ -n "${locality}" ]]; then occ_args+=("--l=${locality}") fi IFS=',;' read -r -a ou_values <<< "${ou_raw}" for ou_value in "${ou_values[@]}"; do ou_value="$(echo "${ou_value}" | xargs)" if [[ -n "${ou_value}" ]]; then occ_args+=("--ou=${ou_value}") fi done occ "${occ_args[@]}" >/dev/null 2>&1 || status_local=1 root_cert="$(occ_app_get "libresign" "rootCert")" fi if [[ "${auto_install}" == "1" ]]; then if [[ -z "${java_path}" || -z "${jsignpdf_path}" || -z "${pdftk_path}" ]]; then status_local=1 fi fi if [[ "${auto_setup}" == "1" && -z "${root_cert}" ]]; then status_local=1 fi return "${status_local}" } broker_dir="/opt/qortal-broker" mkdir -p "${broker_dir}" compose_src="${repo_root}/deploy/docker-compose.broker.yml" compose_dst="${broker_dir}/docker-compose.yml" cp "${compose_src}" "${compose_dst}" cat > "${broker_dir}/.env" </dev/null 2>&1; then for app_name in "${app_dirs[@]}"; do if [[ -d "${repo_root}/nextcloud/custom_apps/${app_name}" ]]; then rsync -a "${repo_root}/nextcloud/custom_apps/${app_name}/" "${nc_path}/custom_apps/${app_name}/" fi done if [[ -d "${repo_root}/nextcloud/custom_apps/user_oidc" ]]; then rsync -a "${repo_root}/nextcloud/custom_apps/user_oidc/" "${nc_path}/custom_apps/user_oidc/" fi else for app_name in "${app_dirs[@]}"; do if [[ -d "${repo_root}/nextcloud/custom_apps/${app_name}" ]]; then rm -rf "${nc_path}/custom_apps/${app_name}" cp -R "${repo_root}/nextcloud/custom_apps/${app_name}" "${nc_path}/custom_apps/${app_name}" fi done if [[ -d "${repo_root}/nextcloud/custom_apps/user_oidc" ]]; then rm -rf "${nc_path}/custom_apps/user_oidc" cp -R "${repo_root}/nextcloud/custom_apps/user_oidc" "${nc_path}/custom_apps/user_oidc" fi fi echo "Enabling Nextcloud apps..." set +e install_libresign="$(normalize_bool_01 "${INSTALL_LIBRESIGN:-1}")" if [[ -z "${install_libresign}" ]]; then install_libresign="1" fi enable_list=(chd_admin qortal_files_bridge qortal_talk_bridge nuqloud_scrum) if [[ "${custom_pwa_enabled}" == "1" ]]; then enable_list=(custom_pwa "${enable_list[@]}") fi for app_name in "${enable_list[@]}"; do occ app:enable "${app_name}" >/dev/null 2>&1 done occ app:enable --force user_oidc >/dev/null 2>&1 enable_oidc_status=$? install_oidc_status=0 if [[ "${enable_oidc_status}" -ne 0 ]]; then occ app:install user_oidc >/dev/null 2>&1 install_oidc_status=$? occ app:enable --force user_oidc >/dev/null 2>&1 enable_oidc_status=$? fi occ app:enable qortal_integration >/dev/null 2>&1 enable_qortal_status=$? occ user_oidc:provider --help >/dev/null 2>&1 verify_oidc_status=$? libresign_enable_status=0 libresign_install_status=0 libresign_setup_status=0 if [[ "${install_libresign}" == "1" ]]; then occ app:enable --force libresign >/dev/null 2>&1 libresign_enable_status=$? if [[ "${libresign_enable_status}" -ne 0 ]]; then occ app:install libresign >/dev/null 2>&1 libresign_install_status=$? occ app:enable --force libresign >/dev/null 2>&1 libresign_enable_status=$? fi if [[ "${libresign_enable_status}" -eq 0 ]]; then configure_libresign_vm libresign_setup_status=$? fi fi occ config:app:set qortal_integration broker_internal_api_token --value="${broker_internal_token}" >/dev/null 2>&1 libresign_failure=0 if [[ "${install_libresign}" == "1" && ( "${libresign_enable_status}" -ne 0 || "${libresign_setup_status}" -ne 0 ) ]]; then libresign_failure=1 fi set -e if [[ "${enable_qortal_status}" -ne 0 || "${enable_oidc_status}" -ne 0 || "${verify_oidc_status}" -ne 0 || "${libresign_failure}" -ne 0 ]]; then echo "Warning: one or more app enable checks failed." echo "Run manually:" echo " sudo -u www-data php ${nc_path}/occ app:enable qortal_integration" echo " sudo -u www-data php ${nc_path}/occ app:install user_oidc" echo " sudo -u www-data php ${nc_path}/occ app:enable --force user_oidc" echo " sudo -u www-data php ${nc_path}/occ user_oidc:provider --help" if [[ "${install_libresign}" == "1" ]]; then echo " sudo -u www-data php ${nc_path}/occ app:install libresign" echo " sudo -u www-data php ${nc_path}/occ app:enable --force libresign" echo " sudo -u www-data php ${nc_path}/occ libresign:install --all" echo " sudo -u www-data php ${nc_path}/occ libresign:configure:openssl --cn=\"$(libresign_default_cn)\" --o=\"${LIBRESIGN_CERT_O:-${NUQLOUD_BILLING_COMPANY_NAME:-NuQloud}}\" --ou=\"${LIBRESIGN_CERT_OU:-Document Signing}\"" fi if [[ "${install_oidc_status}" -ne 0 && "${enable_oidc_status}" -ne 0 ]]; then echo " Note: app:install user_oidc fallback failed; check appstore connectivity or custom_apps path." fi if [[ "${install_libresign}" == "1" && "${libresign_install_status}" -ne 0 && "${libresign_enable_status}" -ne 0 ]]; then echo " Note: app:install libresign fallback failed; check appstore connectivity." fi fi echo "Configuring user_oidc provider..." set +e occ user_oidc:provider qortal \ -c nextcloud-local \ -s "${oidc_secret}" \ -d "${broker_url}/.well-known/openid-configuration" \ --scope="openid profile email" \ --mapping-uid=sub \ --mapping-display-name=name \ --mapping-email=email provider_config_status=$? oidc_login_label_status=0 sovereign_mode="$(occ config:app:get qortal_integration sc_mode 2>/dev/null | tr -d '\r' || true)" if [[ "${sovereign_mode}" == "full_qortal" ]]; then occ config:system:delete user_oidc login_label >/dev/null 2>&1 oidc_login_label_status=$? else occ config:system:set user_oidc login_label --value="Create Account / Login" >/dev/null 2>&1 oidc_login_label_status=$? fi set -e if [[ "${provider_config_status}" -ne 0 ]]; then echo "Warning: failed to configure user_oidc provider automatically." echo "Run manually:" echo " sudo -u www-data php ${nc_path}/occ user_oidc:provider qortal -c nextcloud-local -s '' -d ${broker_url}/.well-known/openid-configuration --scope='openid profile email' --mapping-uid=sub --mapping-display-name=name --mapping-email=email" fi if [[ "${oidc_login_label_status}" -ne 0 ]]; then echo "Warning: failed to set user_oidc login label." echo "Run manually:" echo " sudo -u www-data php ${nc_path}/occ config:system:set user_oidc login_label --value='Create Account / Login'" fi if ! command -v docker >/dev/null 2>&1; then echo "Docker is required but not installed. Install Docker and re-run docker compose manually." exit 1 fi if docker compose version >/dev/null 2>&1; then docker_compose="docker compose" elif command -v docker-compose >/dev/null 2>&1; then docker_compose="docker-compose" else echo "Docker Compose not found." exit 1 fi echo "Starting broker containers..." ${docker_compose} -f "${compose_dst}" --env-file "${broker_dir}/.env" up -d echo "Configuring Apache reverse proxy..." if ! a2enmod proxy proxy_http headers ssl >/dev/null 2>&1; then echo "Warning: failed to enable Apache modules (proxy/proxy_http/headers/ssl)." fi apache_site="/etc/apache2/sites-available/qortalbroker.conf" cat > "${apache_site}" < ServerName ${broker_host} ProxyPreserveHost On ProxyPass / http://127.0.0.1:3000/ ProxyPassReverse / http://127.0.0.1:3000/ RequestHeader set X-Forwarded-Proto "https" ErrorLog \${APACHE_LOG_DIR}/qortalbroker_error.log CustomLog \${APACHE_LOG_DIR}/qortalbroker_access.log combined EOF a2ensite qortalbroker.conf >/dev/null 2>&1 || true systemctl reload apache2 || true read -r -p "Run certbot for ${broker_host}? (y/N): " run_certbot if [[ "${run_certbot}" == "y" || "${run_certbot}" == "Y" ]]; then read -r -p "Let's Encrypt email: " le_email if command -v certbot >/dev/null 2>&1; then certbot --apache -d "${broker_host}" -m "${le_email}" --agree-tos --redirect --non-interactive else echo "certbot is not installed. Install it and rerun manually." fi fi echo "Install complete." echo "Broker URL: ${broker_url}" echo "Nextcloud URL: ${nc_url}" echo "OIDC provider configured with client_id=nextcloud-local" echo "If login fails, verify DNS for ${broker_host} and Apache proxy."