#!/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-compose-project.sh" compose_file="" env_file="" app_service="app" verify_only="0" usage() { cat <<'USAGE' Usage: ./scripts/ensure-nextcloud-office-config.sh --compose-file --env-file [options] Ensures Nextcloud Office / Collabora configuration matches env values. Options: --compose-file Docker compose file to use (required) --env-file Env file to use (required) --app-service Nextcloud app service name (default: app) --verify-only Do not attempt repairs; only verify current state -h, --help Show this help USAGE } read_kv() { local key="$1" local line line="$(grep -m1 -E "^${key}=" "${env_file}" || true)" if [[ -z "${line}" ]]; then return 1 fi echo "${line#*=}" } trim_csv() { local raw="${1:-}" local cleaned cleaned="$(echo "${raw}" | tr -d '[:space:]')" cleaned="${cleaned#,}" cleaned="${cleaned%,}" echo "${cleaned}" } profile_enabled() { local profiles="$1" local name="$2" [[ ",${profiles}," == *",${name},"* ]] } compute_public_url() { local domain="$1" local https_port="$2" if [[ -z "${domain}" ]]; then echo "" return 0 fi if [[ -n "${https_port}" && "${https_port}" != "443" ]]; then echo "https://${domain}:${https_port}" else echo "https://${domain}" fi } ensure_docker_access() { local docker_path="" docker_path="$(type -P docker || true)" if [[ -z "${docker_path}" ]]; then echo "docker is required" exit 1 fi if "${docker_path}" info >/dev/null 2>&1; then DOCKER_CMD=("${docker_path}") return 0 fi if ! command -v sudo >/dev/null 2>&1; then echo "docker is installed but this user cannot access it, and sudo is unavailable." exit 1 fi if sudo -n "${docker_path}" info >/dev/null 2>&1; then DOCKER_CMD=(sudo "${docker_path}") return 0 fi echo "docker requires elevated privileges on this machine." echo "You may be prompted for your sudo password." if sudo "${docker_path}" info >/dev/null 2>&1; then DOCKER_CMD=(sudo "${docker_path}") return 0 fi echo "Unable to access docker, even with sudo." exit 1 } docker() { "${DOCKER_CMD[@]}" "$@" } compose_cmd() { docker compose -f "${compose_file}" --env-file "${env_file}" "$@" } occ_exec() { compose_cmd exec -T --user www-data "${app_service}" php occ "$@" } occ_exec_quiet_retry() { local rc=1 local max_attempts="${OCC_RETRY_ATTEMPTS:-6}" local retry_sleep="${OCC_RETRY_SLEEP_SECONDS:-4}" local attempt if [[ $# -eq 0 ]]; then return 1 fi for attempt in $(seq 1 "${max_attempts}"); do occ_exec "$@" >/dev/null 2>&1 rc=$? if [[ "${rc}" -eq 0 ]]; then return 0 fi sleep "${retry_sleep}" done return "${rc}" } while [[ $# -gt 0 ]]; do case "$1" in --compose-file) compose_file="${2:-}" shift 2 ;; --compose-file=*) compose_file="${1#*=}" shift ;; --env-file) env_file="${2:-}" shift 2 ;; --env-file=*) env_file="${1#*=}" shift ;; --app-service) app_service="${2:-}" shift 2 ;; --app-service=*) app_service="${1#*=}" shift ;; --verify-only) verify_only="1" shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done if [[ -z "${compose_file}" || -z "${env_file}" ]]; then usage exit 1 fi if [[ "${compose_file}" != /* ]]; then compose_file="${repo_root}/${compose_file}" fi if [[ "${env_file}" != /* ]]; then env_file="${repo_root}/${env_file}" fi if [[ ! -f "${compose_file}" ]]; then echo "Missing compose file: ${compose_file}" exit 1 fi if [[ ! -f "${env_file}" ]]; then echo "Missing env file: ${env_file}" exit 1 fi export_compose_project_name_from_env "${env_file}" || true ensure_docker_access profiles="$(trim_csv "$(read_kv "COMPOSE_PROFILES" || true)")" if ! profile_enabled "${profiles}" "office"; then echo "Office profile not enabled in ${env_file}; nothing to do." exit 0 fi if ! occ_exec status >/dev/null 2>&1; then echo "Nextcloud OCC is not ready in service '${app_service}'." exit 1 fi collabora_public_url="$(read_kv "COLLABORA_PUBLIC_URL" || true)" collabora_public_url="${collabora_public_url%/}" if [[ -z "${collabora_public_url}" ]]; then collabora_domain="$(read_kv "COLLABORA_DOMAIN" || true)" public_https_port="$(read_kv "PUBLIC_HTTPS_PORT" || true)" if [[ -z "${public_https_port}" ]]; then public_https_port="$(read_kv "CADDY_HTTPS_PORT" || true)" fi collabora_public_url="$(compute_public_url "${collabora_domain}" "${public_https_port}")" fi if [[ -z "${collabora_public_url}" ]]; then echo "Missing COLLABORA_PUBLIC_URL or COLLABORA_DOMAIN while office profile is enabled." exit 1 fi status=0 office_enable_status=0 office_wopi_status=0 office_public_wopi_status=0 echo "Ensuring Nextcloud Office configuration..." echo " compose_file=${compose_file}" echo " env_file=${env_file}" echo " collabora_public_url=${collabora_public_url}" if [[ "${verify_only}" != "1" ]]; then if occ_exec_quiet_retry app:enable --force richdocuments; then office_enable_status=0 else office_enable_status=1 occ_exec_quiet_retry app:install richdocuments >/dev/null 2>&1 || true if occ_exec_quiet_retry app:enable --force richdocuments; then office_enable_status=0 else office_enable_status=1 fi fi if occ_exec_quiet_retry config:app:set richdocuments wopi_url --value="${collabora_public_url}"; then office_wopi_status=0 else office_wopi_status=1 fi if occ_exec_quiet_retry config:app:set richdocuments public_wopi_url --value="${collabora_public_url}"; then office_public_wopi_status=0 else office_public_wopi_status=1 fi fi if [[ "${office_enable_status}" -ne 0 || "${office_wopi_status}" -ne 0 || "${office_public_wopi_status}" -ne 0 ]]; then status=1 fi if ! occ_exec app:list --enabled 2>/dev/null | grep -qE '^[[:space:]]+- richdocuments:'; then echo "richdocuments app is not enabled." status=1 fi current_wopi_url="$(occ_exec config:app:get richdocuments wopi_url 2>/dev/null | tr -d '\r' || true)" current_public_wopi_url="$(occ_exec config:app:get richdocuments public_wopi_url 2>/dev/null | tr -d '\r' || true)" if [[ "${current_wopi_url%/}" != "${collabora_public_url}" ]]; then echo "richdocuments wopi_url mismatch: ${current_wopi_url:-}" status=1 fi if [[ "${current_public_wopi_url%/}" != "${collabora_public_url}" ]]; then echo "richdocuments public_wopi_url mismatch: ${current_public_wopi_url:-}" status=1 fi echo "Office verification summary:" echo " richdocuments_enabled=$([[ "${status}" -eq 0 ]] && echo yes || echo check_failed)" echo " wopi_url=${current_wopi_url:-}" echo " public_wopi_url=${current_public_wopi_url:-}" if [[ "${status}" -eq 0 ]]; then echo "Office configuration verified." else echo "Office configuration verification failed." fi exit "${status}"