From e77a293adeeebcc64a15b30acba082bc6669a47f Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Tue, 31 Mar 2026 11:50:07 +0300 Subject: [PATCH] feat: refactor nextcloud commands into wand --- .config/wand/nextcloud.yml | 258 +++++++++++++++++ .../zsh/plugins/local/common/nextcloud.zsh | 270 ++---------------- 2 files changed, 279 insertions(+), 249 deletions(-) create mode 100644 .config/wand/nextcloud.yml diff --git a/.config/wand/nextcloud.yml b/.config/wand/nextcloud.yml new file mode 100644 index 00000000..a4110eea --- /dev/null +++ b/.config/wand/nextcloud.yml @@ -0,0 +1,258 @@ +use: + description: Set the Nextcloud dev version + cmd: | + VERSION_FILE="$HOME/.nc-dev-version" + VERSION="$1" + if [ -z "$VERSION" ]; then + VERSION="$(tr -d '\n' < "$VERSION_FILE" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + else + if [ "$VERSION" != "master" ] && [ "$VERSION" != "latest" ] && [ "$VERSION" != "dev" ]; then + VERSION="stable$VERSION" + else + VERSION="nextcloud" + fi + fi + echo "$VERSION" > "$VERSION_FILE" + echo "Set Nextcloud dev version to: $VERSION" + +version: + description: Get the current Nextcloud dev version + cmd: | + VERSION="$(tr -d '\n' < "$HOME/.nc-dev-version" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + echo "$VERSION" + +start: + description: Start the Nextcloud dev container + working_dir: ~/Dev/nextcloud-docker-dev + cmd: | + VERSION_FILE="$HOME/.nc-dev-version" + VERSION="$1" + if [ -n "$VERSION" ]; then + if [ "$VERSION" != "master" ] && [ "$VERSION" != "latest" ] && [ "$VERSION" != "dev" ]; then + VERSION="stable$VERSION" + else + VERSION="nextcloud" + fi + echo "$VERSION" > "$VERSION_FILE" + else + VERSION="$(tr -d '\n' < "$VERSION_FILE" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + fi + echo "Set Nextcloud dev version to: $VERSION" + docker compose up -d "$VERSION" + +stop: + description: Stop the Nextcloud dev container + working_dir: ~/Dev/nextcloud-docker-dev + cmd: docker compose stop + +exec: + description: Execute command in the Nextcloud container + flags: + aio: + type: bool + description: Use AIO container instead of dev + cmd: | + if [ "$WAND_FLAG_AIO" = "true" ]; then + sudo docker exec --user www-data -it nextcloud-aio-nextcloud "$@" + else + VERSION="$(tr -d '\n' < "$HOME/.nc-dev-version" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + docker exec --user www-data -it "nextcloud-${VERSION}-1" "$@" + fi + +occ: + description: Run occ command in Nextcloud container + flags: + aio: + type: bool + description: Use AIO container instead of dev + cmd: | + if [ "$WAND_FLAG_AIO" = "true" ]; then + sudo docker exec --user www-data -it nextcloud-aio-nextcloud php occ "$@" + else + VERSION="$(tr -d '\n' < "$HOME/.nc-dev-version" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + docker exec --user www-data -it "nextcloud-${VERSION}-1" php occ "$@" + fi + +logs: + description: Tail the Nextcloud log file + flags: + aio: + type: bool + description: Use AIO container instead of dev + pretty: + alias: p + type: bool + description: Pretty-print JSON logs with jq + cmd: | + if [ "$WAND_FLAG_AIO" = "true" ]; then + CONTAINER="nextcloud-aio-nextcloud" + SUDO="sudo" + else + VERSION="$(tr -d '\n' < "$HOME/.nc-dev-version" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + CONTAINER="nextcloud-${VERSION}-1" + SUDO="" + fi + + if [ "$WAND_FLAG_PRETTY" = "true" ]; then + $SUDO docker exec --user www-data "$CONTAINER" tail "$@" /var/www/html/data/nextcloud.log \ + | while IFS= read -r line; do + printf '%s\n' "$line" | jq -C -c --unbuffered . + printf '\n' + done + else + $SUDO docker exec --user www-data "$CONTAINER" tail "$@" /var/www/html/data/nextcloud.log + fi + +debug: + description: Set Nextcloud debug mode + flags: + aio: + type: bool + description: Use AIO container instead of dev + cmd: | + if [ "$WAND_FLAG_AIO" = "true" ]; then + sudo docker exec --user www-data -it nextcloud-aio-nextcloud php occ config:system:set debug --type bool --value "$1" + else + VERSION="$(tr -d '\n' < "$HOME/.nc-dev-version" 2>/dev/null)" + [ -z "$VERSION" ] && VERSION="nextcloud" + docker exec --user www-data -it "nextcloud-${VERSION}-1" php occ config:system:set debug --type bool --value "$1" + fi + +upgrade: + description: Upgrade Nextcloud AIO + flags: + beta: + type: bool + description: Upgrade via beta channel + cmd: | + AIO="sudo docker exec --user www-data -it nextcloud-aio-nextcloud" + if [ "$WAND_FLAG_BETA" = "true" ]; then + $AIO php occ config:system:set updater.release.channel --value=beta + $AIO php updater/updater.phar --no-interaction + $AIO php occ config:system:set updater.release.channel --value=stable + else + $AIO php updater/updater.phar --no-interaction + fi + +force-appupdate: + description: Force refresh Nextcloud AIO app store cache + aliases: [fau] + cmd: | + AIO="sudo docker exec --user www-data -it nextcloud-aio-nextcloud" + INSTANCE_ID=$($AIO php occ config:system:get instanceid | tr -d "\n\r") + APPSTORE_DIR="/mnt/ncdata/appdata_${INSTANCE_ID}/appstore" + APPS_JSON="${APPSTORE_DIR}/apps.json" + mv "$APPS_JSON" "${APPS_JSON}.bk" + echo "Downloading appstore apps.json from Nextcloud..." + if ! curl -L https://apps.nextcloud.com/api/v1/apps.json -o "$APPS_JSON"; then + echo "Failed to download apps.json. Restoring backup." + mv "${APPS_JSON}.bk" "$APPS_JSON" + exit 1 + fi + chown www-data:www-data "$APPS_JSON" + +backup: + description: Rsync Nextcloud data from remote server to external drive + confirm: This will sync from spider.casraf.dev to the 2T SSD. Continue? + cmd: | + DRIVE="/Volumes/2T SSD" + if [ ! -d "$DRIVE/Nextcloud/" ]; then + echo "Mount the 2T SSD first!" + exit 1 + fi + rsync -avhz --delete --delete-excluded --partial --progress -e ssh \ + --exclude 'appdata_*/' \ + --exclude '._*' \ + --exclude '.pnpm-store/' \ + spider.casraf.dev:/mnt/ncdata/ \ + "$DRIVE/Nextcloud/" + find "$DRIVE" -type f -name '._*' -exec rm -f -- {} + + +db-proxy: + description: Manage AIO database proxy + children: + start: + description: Start a local proxy to the Nextcloud AIO database container + env: + NC_CFG_PATH: /var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/config/config.php + NC_DB_CONTAINER: nextcloud-aio-database + NC_PROXY_NAME: nc-db-proxy + cmd: | + DBTYPE="" DBHOST_RAW="" DBUSER="" DBPASS="" DBNAME="" + while IFS='=' read -r k v; do + case "$k" in + dbtype) DBTYPE="$v" ;; + dbhost) DBHOST_RAW="$v" ;; + dbuser) DBUSER="$v" ;; + dbpassword) DBPASS="$v" ;; + dbname) DBNAME="$v" ;; + esac + done </,"="); print }' "$NC_CFG_PATH") + EOF + + if [ -z "$DBTYPE" ] || [ -z "$DBHOST_RAW" ] || [ -z "$DBUSER" ] || [ -z "$DBPASS" ] || [ -z "$DBNAME" ]; then + echo "Failed to read DB settings from $NC_CFG_PATH" >&2 + exit 1 + fi + + case "$DBTYPE" in + pgsql) SCHEME="postgres"; DEFAULT_PORT=5432 ;; + mysql|mariadb) SCHEME="mysql"; DEFAULT_PORT=3306 ;; + *) echo "Unknown dbtype '$DBTYPE'" >&2; exit 1 ;; + esac + + INTERNAL_HOST="$DBHOST_RAW" + case "$DBHOST_RAW" in + *:*) INTERNAL_HOST="${DBHOST_RAW%%:*}"; DBPORT="${DBHOST_RAW##*:}" ;; + *) DBPORT="$DEFAULT_PORT" ;; + esac + + if [ "$DBPORT" = "5432" ]; then LOCALPORT=55432; else LOCALPORT=53306; fi + + if ! docker inspect "$NC_DB_CONTAINER" >/dev/null 2>&1; then + echo "Container $NC_DB_CONTAINER not found." >&2 + exit 1 + fi + NET=$(docker inspect -f '{{range $k, $_ := .NetworkSettings.Networks}}{{println $k}}{{end}}' "$NC_DB_CONTAINER" | head -n1) + if [ -z "$NET" ]; then + echo "Could not determine Docker network for $NC_DB_CONTAINER." >&2 + exit 1 + fi + + docker rm -f "$NC_PROXY_NAME" >/dev/null 2>&1 || true + if ! docker run -d --rm --name "$NC_PROXY_NAME" --network "$NET" \ + -p "127.0.0.1:${LOCALPORT}:${DBPORT}" \ + alpine/socat \ + "tcp-listen:${DBPORT},fork,reuseaddr" "tcp:${INTERNAL_HOST}:${DBPORT}" >/dev/null; then + echo "Failed to start proxy container." >&2 + exit 1 + fi + + if [ "$SCHEME" = "postgres" ]; then + URI="postgres://${DBUSER}:${DBPASS}@127.0.0.1:${LOCALPORT}/${DBNAME}?sslmode=disable" + else + URI="mysql://${DBUSER}:${DBPASS}@127.0.0.1:${LOCALPORT}/${DBNAME}" + fi + echo "Proxy up: 127.0.0.1:${LOCALPORT} -> ${INTERNAL_HOST}:${DBPORT} (network: ${NET})" + echo "LazySQL connection URI:" + echo " ${URI}" + echo + echo "When done, run: nxc db-proxy stop" + + stop: + description: Stop the Nextcloud AIO database proxy + env: + NC_PROXY_NAME: nc-db-proxy + cmd: | + if docker rm -f "$NC_PROXY_NAME" >/dev/null 2>&1; then + echo "Proxy stopped." + else + echo "No proxy running." + fi diff --git a/.local/share/zsh/plugins/local/common/nextcloud.zsh b/.local/share/zsh/plugins/local/common/nextcloud.zsh index 13a19959..79b5e464 100755 --- a/.local/share/zsh/plugins/local/common/nextcloud.zsh +++ b/.local/share/zsh/plugins/local/common/nextcloud.zsh @@ -1,254 +1,26 @@ #!/usr/bin/env zsh -NC_VERSION_FILE="$HOME/.nc-dev-version" -NC_DEV_DIR="$HOME/Dev/nextcloud-docker-dev" +alias nxc="wand --wand-file \$HOME/.config/wand/nextcloud.yml" -# set or reset the Nextcloud dev version -nc-dev-use() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-dev-use [version]" - echo "Set or reset the Nextcloud dev version" - return 0 - fi - local version="$1" - if [[ -z "$version" ]]; then - version="$(tr -d '\n' < $NC_VERSION_FILE)" - [ -z "$version" ] && version="nextcloud" - else - if [[ "$version" != "master" && "$version" != "latest" && "$version" != "dev" ]]; then - version="stable$version" - else - version="nextcloud" - fi - fi - echo "$version" > $NC_VERSION_FILE - echo "Set Nextcloud dev version to: $version" -} +# dev +alias nc-dev-use="nxc use" +alias nc-dev-start="nxc start" +alias nc-dev-stop="nxc stop" +alias nc-dev="nxc exec --" +alias nc-dev-occ="nxc occ --" +alias nc-dev-logs="nxc logs --" +alias nc-dev-pretty-logs="nxc logs --pretty --" +alias nc-dev-debug="nxc debug --" -# get the current Nextcloud dev version -nc-dev-get-version() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-dev-get-version" - echo "Get the current Nextcloud dev version" - return 0 - fi - local version - version="$(tr -d '\n' < $NC_VERSION_FILE)" - if [[ -z "$version" ]]; then - version="nextcloud" - fi - echo "$version" -} +# aio +alias nc-aio="nxc exec --aio --" +alias nc-aio-occ="nxc occ --aio --" +alias nc-aio-debug="nxc debug --aio --" +alias nc-aio-upgrade="nxc upgrade" +alias nc-aio-upgrade-beta="nxc upgrade --beta" +alias nc-aio-force-appupdate="nxc force-appupdate" -# start the Nextcloud dev container for a given version -nc-dev-start() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-dev-start [version]" - echo "Start the Nextcloud dev container for a given version" - return 0 - fi - local version="$1" - nc-dev-use "$version" - version="$(nc-dev-get-version)" - pushd $HOME/Dev/nextcloud-docker-dev - docker compose up -d $version - popd -} - -# stop the Nextcloud dev container -alias nc-dev-stop="pushd \$NC_DEV_DIR && docker compose stop; popd" - -# Nextcloud AIO aliases -alias nc-aio="sudo docker exec --user www-data -it nextcloud-aio-nextcloud" -alias nc-aio-occ="nc-aio php occ" -alias nc-aio-debug="nc-aio-occ config:system:set debug --type bool --value" -alias nc-aio-upgrade="nc-aio php updater/updater.phar --no-interaction" -alias nc-aio-upgrade-beta="nc-aio-occ config:system:set updater.release.channel --value=beta && nc-aio-upgrade; nc-aio-occ config:system:set updater.release.channel --value=stable" - -# Nextcloud dev aliases -alias nc-dev="docker exec --user www-data -it nextcloud-\$(nc-dev-get-version)-1" -alias nc-dev-occ="nc-dev php occ" - -# tail the Nextcloud dev log file -nc-dev-logs() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-dev-logs [args...]" - echo "Tail the Nextcloud dev log file" - return 0 - fi - docker exec --user www-data nextcloud-$(nc-dev-get-version)-1 tail $@ /var/www/html/data/nextcloud.log -} - -# tail and pretty-print the Nextcloud dev log as JSON -nc-dev-pretty-logs() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-dev-pretty-logs [args...]" - echo "Tail and pretty-print the Nextcloud dev log as JSON" - return 0 - fi - # Forward all args (e.g., -f) to nc-dev-logs - nc-dev-logs "$@" | while IFS= read -r line; do - printf '%s\n' "$line" | jq -C -c --unbuffered . - printf '\n' - done -} - -# rsync Nextcloud data from remote server to external drive -nc-backup() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-backup" - echo "Rsync Nextcloud data from remote server to external drive" - return 0 - fi - drive="/Volumes/2T SSD" - if [ ! -d "$drive/Nextcloud/" ]; then - echo "Mount the 2T SSD first!" - exit 1 - fi - - rsync -avhz --delete --delete-excluded --partial --progress -e ssh \ - --exclude 'appdata_*/' \ - --exclude '._*' \ - --exclude '.pnpm-store/' \ - spider.casraf.dev:/mnt/ncdata/ \ - "$drive/Nextcloud/" - find "$drive" -type f -name '._*' -exec rm -f -- {} + -} - -# --- CONFIG (edit if your paths/names differ) --- -NC_CFG_PATH="/var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/config/config.php" -NC_DB_CONTAINER="nextcloud-aio-database" -NC_PROXY_NAME="nc-db-proxy" - -# Parse Nextcloud's config.php using awk (works even if it doesn't `return $CONFIG`) -_nc_read_cfg_via_awk() { - # prints lines: dbtype=pgsql, dbhost=..., dbuser=..., dbpassword=..., dbname=... - sudo awk ' - /dbtype|dbname|dbuser|dbpassword|dbhost/ { - gsub(/[ ,'\'';]/,""); # remove spaces, commas, single quotes, semicolons - gsub(/=>/,"="); # turn "=>" into "=" - print # e.g. dbtype=pgsql - } - ' "$NC_CFG_PATH" -} - -# start a local proxy to the Nextcloud AIO database container -nc-enable-db-proxy() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-enable-db-proxy" - echo "Start a local proxy to the Nextcloud AIO database container" - return 0 - fi - # 1) Read values from config.php using the proven awk filter - local DBTYPE="" DBHOST_RAW="" DBUSER="" DBPASS="" DBNAME="" - while IFS='=' read -r k v; do - case "$k" in - dbtype) DBTYPE="$v" ;; - dbhost) DBHOST_RAW="$v" ;; - dbuser) DBUSER="$v" ;; - dbpassword) DBPASS="$v" ;; - dbname) DBNAME="$v" ;; - esac - done < <(_nc_read_cfg_via_awk) - - if [[ -z "$DBTYPE" || -z "$DBHOST_RAW" || -z "$DBUSER" || -z "$DBPASS" || -z "$DBNAME" ]]; then - echo "Failed to read DB settings from $NC_CFG_PATH" >&2 - return 1 - fi - - # 2) Determine scheme and default port - local SCHEME DBPORT DEFAULT_PORT - case "$DBTYPE" in - pgsql) - SCHEME="postgres" - DEFAULT_PORT=5432 - ;; - mysql | mariadb) - SCHEME="mysql" - DEFAULT_PORT=3306 - ;; - *) - echo "Unknown dbtype '$DBTYPE' (expected pgsql/mysql/mariadb)" >&2 - return 1 - ;; - esac - - # 3) Split dbhost into host[:port] - local INTERNAL_HOST="$DBHOST_RAW" - if [[ "$DBHOST_RAW" == *:* ]]; then - INTERNAL_HOST="${DBHOST_RAW%%:*}" - DBPORT="${DBHOST_RAW##*:}" - else - DBPORT="$DEFAULT_PORT" - fi - - # 4) Choose a localhost port (avoid collisions) - local LOCALPORT - if [[ "$DBPORT" == "5432" ]]; then - LOCALPORT=55432 - else - LOCALPORT=53306 - fi - - # 5) Find the Docker network of the DB container - if ! docker inspect "$NC_DB_CONTAINER" >/dev/null 2>&1; then - echo "Container $NC_DB_CONTAINER not found." >&2 - return 1 - fi - local NET - NET=$(docker inspect -f '{{range $k, $_ := .NetworkSettings.Networks}}{{println $k}}{{end}}' "$NC_DB_CONTAINER" | head -n1) - if [[ -z "$NET" ]]; then - echo "Could not determine Docker network for $NC_DB_CONTAINER." >&2 - return 1 - fi - - # 6) Start localhost-only proxy: 127.0.0.1:$LOCALPORT -> INTERNAL_HOST:$DBPORT (inside Docker net) - docker rm -f "$NC_PROXY_NAME" >/dev/null 2>&1 || true - if ! docker run -d --rm --name "$NC_PROXY_NAME" --network "$NET" \ - -p 127.0.0.1:${LOCALPORT}:${DBPORT} \ - alpine/socat \ - tcp-listen:${DBPORT},fork,reuseaddr tcp:${INTERNAL_HOST}:${DBPORT} >/dev/null; then - echo "Failed to start proxy container." >&2 - return 1 - fi - - if [[ "$SCHEME" == "postgres" ]]; then - URI="postgres://${DBUSER}:${DBPASS}@127.0.0.1:${LOCALPORT}/${DBNAME}?sslmode=disable" - else - URI="mysql://${DBUSER}:${DBPASS}@127.0.0.1:${LOCALPORT}/${DBNAME}" - fi - echo "Proxy up: 127.0.0.1:${LOCALPORT} → ${INTERNAL_HOST}:${DBPORT} (network: ${NET})" - echo "LazySQL connection URI:" - echo " ${URI}" - echo - echo "When done, run: nc-disable-db-proxy" -} - -# stop the Nextcloud AIO database proxy -nc-disable-db-proxy() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - echo "Usage: nc-disable-db-proxy" - echo "Stop the Nextcloud AIO database proxy" - return 0 - fi - if docker rm -f "$NC_PROXY_NAME" >/dev/null 2>&1; then - echo "Proxy stopped." - else - echo "No proxy running." - fi -} - -nc-aio-force-appupdate() { - INSTANCE_ID=$(nc-aio-occ config:system:get instanceid | tr -d "\n" | tr -d "\r") - APPSTORE_DIR="/mnt/ncdata/appdata_${INSTANCE_ID}/appstore" - APPS_JSON="${APPSTORE_DIR}/apps.json" - mv "$APPS_JSON" "${APPS_JSON}.bk" - echo "Downloading appstore apps.json from Nextcloud..." - curl -L https://apps.nextcloud.com/api/v1/apps.json -o $APPS_JSON - if [ $? -ne 0 ]; then - echo "Failed to download apps.json. Restoring backup." - mv "${APPS_JSON}.bk" "$APPS_JSON" - return 1 - fi - chown www-data:www-data $APPS_JSON -} +# shared +alias nc-backup="nxc backup" +alias nc-enable-db-proxy="nxc db-proxy start" +alias nc-disable-db-proxy="nxc db-proxy stop"