#!/bin/bash export FPM_TUNE_VERSION=v5 export NGINX_CONF_VERSION=v8 export MY_CNF_VERSION=v6 export ENTRYPOINT_VERSION=v3 export ENTRYPOINT_WHITEBOARD_VERSION=v1 export ENTRYPOINT_TALK_VERSION=v1 export CRONTAB_VERSION=v1 export PG_BACKUP_VERSION=v2 run_occ() { # NOTE: uses $* (not $@) so this still works when called with multiple args as seperate words. su -p www-data -s /bin/sh -c "/var/www/html/occ $*" } install_apps() { install_apps="$@" if [ -z "$install_apps" ]; then install_apps=$APPS fi for app in $install_apps; do run_occ "app:install $app" done } set_app_config() { APP=$1 KEY=$2 VALUE=$3 run_occ "config:app:set $APP $KEY --value '$VALUE'" } set_system_config() { KEY=$1 VALUE=$2 run_occ "config:system:set $KEY --value '$VALUE'" } set_trusted_proxies() { trusted_proxies="$@" if [ -z "$1" ]; then trusted_proxies="$TRUSTED_PROXIES" fi set_system_config trusted_proxies "$trusted_proxies" } set_logfile_stdout() { set_system_config logfile '/dev/stdout' } customize() { if [ -z "$1" ] then echo "Usage: ... customize " exit 1 fi asset_dir=$1 for asset in $COPY_ASSETS; do source=$(echo $asset | cut -d "|" -f1) target=$(echo $asset | cut -d "|" -f2) echo copy $source to $target abra app cp $APP_NAME $asset_dir/$source $target done abra app cmd -T $APP_NAME app set_app_config theming color \"$THEMING_COLOR\" abra app cmd -T $APP_NAME app set_app_config theming slogan \"$THEMING_SLOGAN\" abra app cmd -T $APP_NAME app run_occ '"theming:config background \"/var/www/html/themes/flow_background.jpg\""' abra app cmd -T $APP_NAME app run_occ '"theming:config logo \"/var/www/html/themes/icon_left_brand.svg\""' abra app cmd -T $APP_NAME app run_occ '"theming:config logoheader \"/var/www/html/themes/icon.png\""' } install_bbb() { install_apps bbb set_app_config bbb app.navigation true set_app_config bbb api.url "$BBB_URL" set_app_config bbb api.secret "$(cat /run/secrets/bbb_secret)" } install_onlyoffice() { install_apps onlyoffice set_app_config onlyoffice DocumentServerUrl "$ONLYOFFICE_URL" set_app_config onlyoffice jwt_secret "$(cat /run/secrets/onlyoffice_jwt)" set_app_config onlyoffice customizationForcesave true } install_collabora() { install_apps richdocuments set_app_config richdocuments wopi_url "$COLLABORA_URL" # important for security reaosns # https://docs.nextcloud.com/server/latest/admin_manual/office/configuration.html#wopi-settings set_app_config richdocuments wopi_allowlist "$COLLABORA_ALLOWLIST" } install_whiteboard() { install_apps whiteboard set_app_config whiteboard collabBackendUrl "https://${DOMAIN}/whiteboard" set_app_config whiteboard jwt_secret_key "$(cat /run/secrets/whiteboard_jwt)" } install_talk() { install_apps spreed run_occ "talk:signaling:add --verify 'wss://${TALK_DOMAIN}' '$(cat /run/secrets/talk_signaling_secret)'" run_occ "talk:stun:add '${TALK_DOMAIN}:3478'" run_occ "talk:stun:add '${TALK_DOMAIN}:443'" run_occ "talk:turn:add --secret='$(cat /run/secrets/talk_turn_secret)' turn '${TALK_DOMAIN}:3478' udp,tcp" } install_fulltextsearch() { install_apps fulltextsearch install_apps fulltextsearch_elasticsearch install_apps files_fulltextsearch set_app_config fulltextsearch search_platform "OCA\\FullTextSearch_Elasticsearch\\Platform\\ElasticSearchPlatform" set_app_config fulltextsearch_elasticsearch elastic_host "http://elastic:$(cat /run/secrets/elasticsearch_password)@elasticsearch:9200/" set_app_config fulltextsearch_elasticsearch elastic_index "nextcloud" set_app_config files_fulltextsearch files_local "1" } set_default_quota() { set_app_config files default_quota "$DEFAULT_QUOTA" } set_authentik() { install_apps sociallogin AUTHENTIK_SECRET=$(cat /run/secrets/authentik_secret) AUTHENTIK_ID=$(cat /run/secrets/authentik_id) set_system_config logo_url https://$AUTHENTIK_DOMAIN set_app_config sociallogin custom_providers " { \"custom_oidc\":[ { \"name\":\"$AUTHENTIK_USER_PREFIX\", \"title\":\"authentik\", \"authorizeUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/authorize/\", \"tokenUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/token/\", \"displayNameClaim\":\"preferred_username\", \"userInfoUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/userinfo/\", \"logoutUrl\": \"https://$AUTHENTIK_DOMAIN/application/o/nextcloud/end-session/\", \"clientId\":\"$AUTHENTIK_ID\", \"clientSecret\":\"$AUTHENTIK_SECRET\", \"scope\":\"openid profile email nextcloud\", \"groupsClaim\":\"nextcloud_groups\", \"style\":\"openid\", \"defaultGroup\":\"\", \"groupMapping\": { \"admin\": \"admin\", \"authentik Admins\": \"admin\" } } ] }" set_app_config sociallogin update_profile_on_login 1 set_app_config sociallogin auto_create_groups 1 set_app_config sociallogin hide_default_login 1 run_occ 'config:system:set social_login_auto_redirect --value true' run_occ 'config:system:set allow_user_to_change_display_name --value=false' run_occ 'config:system:set lost_password_link --value=disabled' } set_user_oidc() { install_apps user_oidc USER_OIDC_SECRET=$(cat /run/secrets/user_oidc_secret) run_occ "user_oidc:provider \ --clientid=${USER_OIDC_ID} \ --clientsecret=${USER_OIDC_SECRET} \ --discoveryuri=${USER_OIDC_DISCOVERY_URI} \ --endsessionendpointuri=${USER_OIDC_END_SESSION_URI} \ --postlogouturi=https://${DOMAIN} \ --scope='openid email profile' \ ${USER_OIDC_PROVIDER}" # disable non user_oidc login if [[ ${USER_OIDC_LOGIN_ONLY:-false} = "true" ]]; then run_occ "config:app:set --value=0 user_oidc allow_multiple_user_backends" fi } disable_skeletondirectory() { run_occ "config:system:set skeletondirectory --value ''" } set_windowsfriendly_filenames() { run_occ 'config:system:set forbidden_filename_characters 0 --value=?' run_occ 'config:system:set forbidden_filename_characters 1 --value=\<' run_occ 'config:system:set forbidden_filename_characters 2 --value=\>' run_occ 'config:system:set forbidden_filename_characters 3 --value=:' run_occ 'config:system:set forbidden_filename_characters 4 --value=*' run_occ 'config:system:set forbidden_filename_characters 5 --value=\|' run_occ 'config:system:set forbidden_filename_characters 6 --value=\"' } upgrade_mariadb() { mariadb-upgrade -p`cat /run/secrets/db_root_password` } # Checks whether this instance looks ready to update to the next Nextcloud # major version. # # Usage: # abra app cmd app check_major_upgrade # abra app cmd app check_major_upgrade 33 # check readiness for a specific target # # What it checks: # - current version is exactly one major behind the target # - no pending DB upgrade from a previous, unfinished update # - whether a newer release is available on the current major # (recommended before upgradeing to the next major) # - every enabled, non-shipped app's compatibility with the target major # - for apps that don't, whether apps.nextcloud.com already has a newer # release that does # # It does NOT check every precondition, always read the release notes # from Nextcloud too. check_major_upgrade() { target_major=$1 echo "=== Nextcloud major upgrade readiness check ===" status_json=$(run_occ status --output=json 2>/dev/null) if [ -z "$status_json" ]; then echo "[FAIL] Could not read 'occ status' - is Nextcloud installed and reachable?" return 1 fi current_version=$(echo "$status_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo $d["versionstring"] ?? "";') current_major=${current_version%%.*} needs_db_upgrade=$(echo "$status_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo ($d["needsDbUpgrade"] ?? false) ? "true" : "false";') if [ -z "$current_major" ]; then echo "[FAIL] Could not determine the current Nextcloud version from 'occ status'." return 1 fi if [ -z "$target_major" ]; then target_major=$((current_major + 1)) fi echo "Current version: $current_version" echo "Target major version: $target_major" ok=true if [ "$target_major" -le "$current_major" ]; then echo "[FAIL] Target major ($target_major) is not newer than the current major ($current_major)." ok=false elif [ "$target_major" -gt "$((current_major + 1))" ]; then echo "[FAIL] Cannot skip major versions. Upgrade to $((current_major + 1)) first." ok=false fi if [ "$needs_db_upgrade" = "true" ]; then echo "[FAIL] A pending database upgrade was detected. Run 'occ upgrade' for the current version first." ok=false fi echo echo "--- occ update:check ---" update_check_output=$(run_occ "update:check" 2>&1) if [ -z "$update_check_output" ]; then echo "[WARN] 'occ update:check' produced no output, could not verify." elif echo "$update_check_output" | grep -q "Everything up to date"; then echo "[OK] Everything up to date." else available_version=$(echo "$update_check_output" | grep -oE 'Nextcloud [0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | awk '{print $2}') available_major=${available_version%%.*} if [ -z "$available_major" ]; then echo "[WARN] Could not parse 'occ update:check' output to determine the available version." elif [ "$available_major" = "$current_major" ]; then echo "[WARN] $available_version is available on the current major. Recommended to update to that before upgrading to $target_major." else echo "[OK] Already on the latest release of major $current_major (next available update is $available_version)." fi fi echo echo "--- Non-shipped app compatibility with Nextcloud $target_major ---" echo "(shipped apps are skipped, they come bundled with the docker image)" apps_json=$(run_occ "app:list --shipped=false --enabled --output=json" 2>/dev/null) if [ -z "$apps_json" ]; then echo "[WARN] 'occ app:list' returned no output, could not check non-shipped app compatibility." enabled_apps="" else apps_json_valid=$(echo "$apps_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); echo (json_last_error() === JSON_ERROR_NONE && is_array($d)) ? "1" : "0";') if [ "$apps_json_valid" != "1" ]; then echo "[WARN] Could not parse 'occ app:list' output, could not check non-shipped app compatibility." enabled_apps="" else enabled_apps=$(echo "$apps_json" | php -r '$d=json_decode(stream_get_contents(STDIN),true); foreach(array_keys($d["enabled"] ?? []) as $a) echo $a."\n";') if [ -z "$enabled_apps" ]; then echo "No non-shipped apps are enabled - nothing to check here." fi fi fi compatible_apps="" compatible_apps_fetched=0 for app in $enabled_apps; do info_file=$(find /var/www/html/apps /var/www/html/custom_apps -maxdepth 3 -type f -ipath "*/$app/appinfo/info.xml" 2>/dev/null | head -n1) if [ -z "$info_file" ]; then echo "[WARN] $app: could not locate appinfo/info.xml, skipping" continue fi max_version=$(php -r ' $x = @simplexml_load_file($argv[1]); $dep = $x ? ($x->dependencies->nextcloud ?? null) : null; echo $dep !== null ? (string)$dep["max-version"] : ""; ' "$info_file") if [ -z "$max_version" ]; then echo "[WARN] $app: no max-version declared in info.xml, assume compatible but verify manually" continue fi if [ "${max_version%%.*}" -ge "$target_major" ] 2>/dev/null; then echo "[OK] $app: installed version supports up to Nextcloud $max_version" continue fi echo "[INFO] $app: installed version only supports up to Nextcloud $max_version" if [ "$compatible_apps_fetched" != "1" ]; then compatible_apps_fetched=1 compatible_apps=$(curl -fsSL --max-time 30 "https://apps.nextcloud.com/api/v1/platform/${target_major}.0.0/apps.json" 2>/dev/null \ | php -r '$d=json_decode(stream_get_contents(STDIN),true); if(is_array($d)) foreach($d as $a) echo $a["id"]."\n";') fi if [ -z "$compatible_apps" ]; then echo "[FAIL] $app: could not reach apps.nextcloud.com to check for a newer compatible release, verify manually" ok=false elif echo "$compatible_apps" | grep -qxF "$app"; then echo "[WARN] $app: apps.nextcloud.com has a release that supports $target_major. It may not update until Nextcloud is upgraded, occ upgrade will try to update it automatically" else echo "[FAIL] $app: no apps.nextcloud.com release supports $target_major yet, it will be disabled during the upgrade" ok=false fi done echo if [ "$ok" = true ]; then echo "=== READY: no blocking issues found for upgrade to major $target_major ===" return 0 else echo "=== NOT READY: resolve the [FAIL] items above before running the upgrade ===" return 1 fi }