feat: refactor nextcloud commands into wand

This commit is contained in:
2026-03-31 11:50:07 +03:00
parent d1c0a23840
commit e77a293ade
2 changed files with 279 additions and 249 deletions

258
.config/wand/nextcloud.yml Normal file
View File

@@ -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 <<EOF
$(sudo awk '/dbtype|dbname|dbuser|dbpassword|dbhost/ { gsub(/[ ,'\'';\t]/,""); gsub(/=>/,"="); 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

View File

@@ -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"