#!/usr/bin/env bash set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${script_dir}/lib-compose-project.sh" compose_file="docker-compose.devprod.yml" env_file=".env.devprod" restore_core_skeleton="0" check_integrity="0" purge_existing_user_skeleton="0" empty_skeleton_dir="/var/www/html/data/nuqloud-empty-skeleton" # Use the mounted Nextcloud tree so the app container can seed the runtime skeleton directly. nuqloud_skeleton_source_dir="/var/www/html/nuqloud-skeleton" usage() { cat <<'USAGE' Usage: ./scripts/configure-nextcloud-defaults.sh [options] Options: --compose-file Compose file to use (default: docker-compose.devprod.yml) --env-file Env file to use (default: .env.devprod) --restore-core-skeleton Restore signed core skeleton files from the configured Nextcloud image --purge-existing-user-skeleton Remove known Nextcloud skeleton files from existing user file trees --check-integrity Run occ integrity:check-core after configuration --empty-skeleton-dir NuQloud skeleton directory path (default: /var/www/html/data/nuqloud-empty-skeleton) If /var/www/html/nuqloud-skeleton is missing, an empty skeleton is used. -h, --help Show this help USAGE } while [[ $# -gt 0 ]]; do case "$1" in --compose-file) compose_file="$2" shift 2 ;; --env-file) env_file="$2" shift 2 ;; --restore-core-skeleton) restore_core_skeleton="1" shift ;; --purge-existing-user-skeleton) purge_existing_user_skeleton="1" shift ;; --check-integrity) check_integrity="1" shift ;; --empty-skeleton-dir) empty_skeleton_dir="$2" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done 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() { 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}") echo "Using sudo for docker commands." 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}") echo "Using sudo for docker commands." return 0 fi echo "Unable to access docker, even with sudo." exit 1 } docker() { "${DOCKER_CMD[@]}" "$@" } read_env_value() { local key="$1" local line="" line="$(grep -m1 -E "^${key}=" "${env_file}" || true)" if [[ -z "${line}" ]]; then return 1 fi echo "${line#*=}" } occ_exec() { docker compose -f "${compose_file}" --env-file "${env_file}" exec -T --user www-data app php occ "$@" } ensure_docker_access docker compose -f "${compose_file}" --env-file "${env_file}" exec -T -u root app \ sh -lc ' set -eu rm -rf "$2" mkdir -p "$2" if [ -d "$1" ]; then cp -a "$1/." "$2/" else echo "NuQloud skeleton source directory not found; using an empty skeleton: $1" >&2 fi find "$2" -name .gitkeep -type f -delete chown -R www-data:www-data "$2" ' sh "${nuqloud_skeleton_source_dir}" "${empty_skeleton_dir}" occ_exec config:system:set skeletondirectory --value="${empty_skeleton_dir}" >/dev/null configured_skeleton_dir="$(occ_exec config:system:get skeletondirectory 2>/dev/null | tr -d '\r' || true)" if [[ "${configured_skeleton_dir}" != "${empty_skeleton_dir}" ]]; then echo "Failed to verify skeletondirectory=${empty_skeleton_dir}" exit 1 fi occ_exec app:disable firstrunwizard >/dev/null 2>&1 || true if [[ "${purge_existing_user_skeleton}" == "1" ]]; then docker compose -f "${compose_file}" --env-file "${env_file}" exec -T -u root app \ sh -lc ' set -eu skeleton_source="/var/www/html/core/skeleton" data_root="/var/www/html/data" removed=0 if [ ! -d "${skeleton_source}" ]; then echo "Core skeleton source not found: ${skeleton_source}" >&2 exit 1 fi for user_files in "${data_root}"/*/files; do [ -d "${user_files}" ] || continue while IFS= read -r rel; do target="${user_files}/${rel#./}" if [ -f "${target}" ]; then rm -f "${target}" removed=$((removed + 1)) fi done </dev/null || true done </dev/null fi if [[ "${restore_core_skeleton}" == "1" ]]; then nextcloud_image="$(read_env_value "NEXTCLOUD_IMAGE" || true)" nextcloud_image="${nextcloud_image:-nextcloud:34-apache}" echo "Restoring core skeleton files from ${nextcloud_image}..." if ! docker run --rm --entrypoint sh "${nextcloud_image}" -lc ' set -eu for candidate in /usr/src/nextcloud/core/skeleton /var/www/html/core/skeleton; do if [ -d "${candidate}" ]; then cd "${candidate}" tar -cf - . exit 0 fi done echo "Unable to locate core skeleton directory in image." >&2 exit 1 ' | docker compose -f "${compose_file}" --env-file "${env_file}" exec -T -u root app sh -lc ' set -eu mkdir -p /var/www/html/core/skeleton tar -xf - -C /var/www/html/core/skeleton '; then echo "Failed to restore signed core skeleton files from ${nextcloud_image}." exit 1 fi fi if [[ "${check_integrity}" == "1" ]]; then occ_exec integrity:check-core fi echo "Configured Nextcloud defaults:" echo " firstrunwizard disabled" echo " skeletondirectory=${empty_skeleton_dir}" if [[ "${purge_existing_user_skeleton}" == "1" ]]; then echo " existing user skeleton files purged" fi if [[ "${restore_core_skeleton}" == "1" ]]; then echo " core skeleton restored from image" fi