improvements to configure

This commit is contained in:
2026-10-29 06:36:51 -06:00
parent 1bd73df94f
commit 7209f7424c
7 changed files with 348 additions and 22 deletions
+3 -3
View File
@@ -216,9 +216,9 @@ configure
It covers: It covers:
- **Your Federated Computer Core.** Give it your Superpanel's address (e.g. https://superpanel.example.com), your Core email, name and password, and it adds Superpanel to your applications, adds your Nextcloud (calendar, contacts and files) and email to GNOME's Online Accounts, and mounts your Nextcloud files at /mnt/example.com ("example.com" in the Files sidebar), and points Element at your Core's Matrix server. For Nextcloud and the files it creates a Nextcloud app password, which you can revoke in Nextcloud under Settings > Security. - **Your Federated Computer Core.** Give it your Superpanel's address (e.g. https://superpanel.example.com), your Core email, name and password, and it adds Superpanel to your applications, adds your Nextcloud (calendar, contacts and files) and email to GNOME's Online Accounts, and mounts your Nextcloud files at /mnt/example.com ("example.com" in the Files sidebar), and points Element at your Core's Matrix server. For Nextcloud and the files it creates a Nextcloud app password, which you can revoke in Nextcloud under Settings > Security. You can add more Cores: run `configure core` again and choose "Add a Core". Each gets its own Superpanel, Nextcloud, email and files; Element, Tailscale and backups use your main Core (the first one, unless you choose another). "Remove a Core" takes it off this computer and revokes its app password, and leaves everything on the Core itself as it is.
- **Your Core's Tailscale network.** Joins this computer to it through headscale.example.com, so you can reach your other machines on it. Set up your Core first. You sign in through a link it shows you; if this computer is on another Tailscale network, it moves it to your Core's. - **Your Core's Tailscale network.** Joins this computer to it through headscale.example.com, so you can reach your other machines on it. Set up your Core first. You sign in through a link it shows you; if this computer is on another Tailscale network, it moves it to your Core's. To leave your Core's network, run `configure tailscale` again.
- **Daily backups of your home folder** to your Nextcloud files, in a .backup-{machine name} folder, with restic. Set up your Core first. The backup is encrypted with a passphrase that `configure` shows you once: save it somewhere other than this computer, such as Bitwarden. It keeps 7 daily, 4 weekly and 6 monthly backups, and leaves out caches and things that can be reinstalled. To leave out more, list paths in ~/.config/coredesktop/backup-exclude, one per line. restic reaches Nextcloud directly through rclone with its own Nextcloud app password, so the backup doesn't depend on the /mnt mount. See "Restoring from a backup" below. - **Daily backups of your home folder** to your Nextcloud files, in a .backup-{machine name} folder, with restic. Set up your Core first. The backup is encrypted with a passphrase that `configure` shows you once: save it somewhere other than this computer, such as Bitwarden. It keeps 7 daily, 4 weekly and 6 monthly backups, and leaves out caches and things that can be reinstalled. To leave out more, list paths in ~/.config/coredesktop/backup-exclude, one per line. restic reaches Nextcloud directly through rclone with its own Nextcloud app password, so the backup doesn't depend on the /mnt mount. See "Restoring from a backup" below. To turn backups off, run `configure backup` again; it also offers to delete the backups from your Nextcloud files (they go to Nextcloud's trash bin).
- Signing in to Claude Code, opencode and Hermes Agent and choosing their model providers. - Signing in to Claude Code, opencode and Hermes Agent and choosing their model providers.
To set up one thing, name it, e.g. `configure hermes`; `configure --list` shows the names. To set up one thing, name it, e.g. `configure hermes`; `configure --list` shows the names.
+196 -9
View File
@@ -3,11 +3,30 @@
TITLE="Federated Computer Core" TITLE="Federated Computer Core"
DESCRIPTION="Superpanel, Nextcloud, email, files and Element" DESCRIPTION="Superpanel, Nextcloud, email, files and Element"
# What you entered last time, so running this again only does what's missing # Each Core is in cores/<domain>.conf, with what you entered last time, so
# running this again only does what's missing. core.conf is a copy of the main
# Core's: Element, Tailscale and Backup use that one.
CORE_CONF="$HOME/.config/coredesktop/core.conf" CORE_CONF="$HOME/.config/coredesktop/core.conf"
CORES_DIR="$HOME/.config/coredesktop/cores"
TAILSCALE_CONF="$HOME/.config/coredesktop/tailscale.conf"
BACKUP_CONF="$HOME/.config/coredesktop/backup.conf"
GOA_ACCOUNT="$COREDESKTOP/configure.d/lib/goa-account" GOA_ACCOUNT="$COREDESKTOP/configure.d/lib/goa-account"
source "$COREDESKTOP/configure.d/lib/nextcloud.sh" source "$COREDESKTOP/configure.d/lib/nextcloud.sh"
[ -f "$CORE_CONF" ] && source "$CORE_CONF" [ -f "$CORE_CONF" ] && source "$CORE_CONF"
MAIN_DOMAIN="$CORE_DOMAIN"
core_domains() {
local file
for file in "$CORES_DIR"/*.conf; do
[ -f "$file" ] && basename "$file" .conf
done
}
load_core() {
CORE_DOMAIN="" CORE_EMAIL="" CORE_NAME=""
[ -n "$1" ] && source "$CORES_DIR/$1.conf"
core_names
}
# CORE_DOMAIN, CORE_EMAIL and CORE_NAME come from core.conf; the rest follow # CORE_DOMAIN, CORE_EMAIL and CORE_NAME come from core.conf; the rest follow
core_names() { core_names() {
@@ -48,19 +67,37 @@ element_done() {
"$ELEMENT_CONFIG" &> /dev/null "$ELEMENT_CONFIG" &> /dev/null
} }
is_configured() { # Element only follows the main Core
[ -n "$CORE_DOMAIN" ] || return 1 core_done() {
core_names superpanel_done && nextcloud_done && email_done && files_done \
superpanel_done && nextcloud_done && email_done && files_done && element_done && { [ "$CORE_DOMAIN" != "$MAIN_DOMAIN" ] || element_done; }
} }
# Done when every Core is
is_configured() {
local domain domains
domains="$(core_domains)"
[ -n "$domains" ] || return 1
for domain in $domains; do
(load_core "$domain" && core_done) || return 1
done
}
# The first Core you set up is the main one
save_conf() { save_conf() {
mkdir -p "$(dirname "$CORE_CONF")" mkdir -p "$CORES_DIR"
{ {
printf 'CORE_DOMAIN=%q\n' "$CORE_DOMAIN" printf 'CORE_DOMAIN=%q\n' "$CORE_DOMAIN"
printf 'CORE_EMAIL=%q\n' "$CORE_EMAIL" printf 'CORE_EMAIL=%q\n' "$CORE_EMAIL"
printf 'CORE_NAME=%q\n' "$CORE_NAME" printf 'CORE_NAME=%q\n' "$CORE_NAME"
} > "$CORE_CONF" } > "$CORES_DIR/$CORE_DOMAIN.conf"
if [ -z "$MAIN_DOMAIN" ] || [ "$MAIN_DOMAIN" = "$CORE_DOMAIN" ]; then
make_main "$CORE_DOMAIN"
fi
}
make_main() {
cp "$CORES_DIR/$1.conf" "$CORE_CONF" && MAIN_DOMAIN="$1"
} }
desktop_entry() { desktop_entry() {
@@ -263,7 +300,8 @@ step() {
"setup_$name" || { echo -e "\e[31m$title was not set up.\e[0m"; STATUS=1; } "setup_$name" || { echo -e "\e[31m$title was not set up.\e[0m"; STATUS=1; }
} }
run_configure() { # Set up the Core loaded with load_core, or a new one
setup_core() {
local url local url
STATUS=0 STATUS=0
@@ -304,7 +342,156 @@ run_configure() {
step nextcloud "Nextcloud in Online Accounts" step nextcloud "Nextcloud in Online Accounts"
step email "Email in Online Accounts" step email "Email in Online Accounts"
step files "Nextcloud files at $MOUNT" step files "Nextcloud files at $MOUNT"
element_installed && step element "Element with your Core's Matrix server" [ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && element_installed \
&& step element "Element with your Core's Matrix server"
unset PASSWORD APP_PASSWORD unset PASSWORD APP_PASSWORD
return $STATUS return $STATUS
} }
# Tailscale and Backup point at one Core, which can't be removed while they do
tailscale_on() {
[ "$(source "$TAILSCALE_CONF" 2> /dev/null && echo "$TAILSCALE_DOMAIN")" = "$1" ] \
&& tailscale status --json 2> /dev/null | jq -e '.BackendState == "Running"' > /dev/null
}
backup_on() {
[[ "$(source "$BACKUP_CONF" 2> /dev/null && echo "$BACKUP_WEBDAV")" == "https://nextcloud.$1/"* ]] \
&& systemctl --user is-enabled -q coredesktop-backup.timer 2> /dev/null
}
# Choose one of DOMAINS, other than $2 if given; sets PICKED
pick_core() {
local choices=() domain choice i
for domain in "${DOMAINS[@]}"; do
[ "$domain" != "$2" ] && choices+=("$domain")
done
if [ ${#choices[@]} -eq 1 ]; then
PICKED="${choices[0]}"
return 0
fi
for i in "${!choices[@]}"; do
printf ' %d) %s\n' $((i + 1)) "${choices[$i]}"
done
read -rp "$1 " choice
[[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#choices[@]} )) || return 1
PICKED="${choices[$((choice - 1))]}"
}
# Point Element at the main Core, and say what else follows it
main_changed() {
load_core "$MAIN_DOMAIN"
echo "$MAIN_DOMAIN is now your main Core."
if element_installed && ! element_done && confirm_yes "Point Element at $MATRIX?"; then
setup_element
fi
[ -f "$TAILSCALE_CONF" ] && echo "To move this computer to its Tailscale network: configure tailscale"
[ -f "$BACKUP_CONF" ] && echo "To back up to it instead: configure backup"
return 0
}
# The app password this computer uses for the Core, from davfs2's secrets
files_app_password() {
grep -s "^$MOUNT " "$HOME/.davfs2/secrets" \
| python3 -I -c 'import shlex, sys; print(shlex.split(sys.stdin.read())[2])' 2> /dev/null
}
# Undo what setup_core did. Nothing on the Core itself changes.
remove_core() {
local domain="$1" new_main="" app_password mount_re
if tailscale_on "$domain"; then
echo "This computer is on $domain's Tailscale network. Leave it first: configure tailscale"
return 1
fi
if backup_on "$domain"; then
echo "Your backups go to $domain. Turn them off first: configure backup"
return 1
fi
if [ "$domain" = "$MAIN_DOMAIN" ] && [ ${#DOMAINS[@]} -gt 1 ]; then
echo "$domain is your main Core. Which Core should be the main one instead?"
pick_core "Choose:" "$domain" || { echo "Choose a number from the list."; return 1; }
new_main="$PICKED"
fi
echo "This removes $domain's Superpanel, Nextcloud, email and files from this computer."
echo "Your files, email and everything else stay on the Core."
confirm "Remove $domain?" || return 0
load_core "$domain"
app_password="$(files_app_password)"
[ -n "$app_password" ] && nextcloud_revoke_app_password "$app_password"
mountpoint -q "$MOUNT" && { umount "$MOUNT" 2> /dev/null || sudo umount "$MOUNT"; }
mount_re="${MOUNT//./\\.}"
if grep -qs " $MOUNT davfs " /etc/fstab; then
sudo sed -i "\\# $mount_re davfs #d" /etc/fstab && sudo systemctl daemon-reload
fi
[ -d "$MOUNT" ] && sudo rmdir "$MOUNT" 2> /dev/null
sed -i "\\#^$mount_re #d" "$HOME/.davfs2/secrets" 2> /dev/null
sed -i "\\#^file://$mount_re #d" "$HOME/.config/gtk-3.0/bookmarks" 2> /dev/null
rm -f "$HOME/.config/autostart/core-$CORE_DOMAIN-files.desktop"
"$GOA_ACCOUNT" remove owncloud "$CORE_USER@nextcloud.$CORE_DOMAIN"
"$GOA_ACCOUNT" remove imap_smtp "$CORE_EMAIL"
rm -f "$SUPERPANEL_DESKTOP" "$HOME/.local/share/icons/core-$CORE_DOMAIN-superpanel.png"
# Element keeps its default server, unless that was this Core
if [ "$domain" = "$MAIN_DOMAIN" ] && [ -z "$new_main" ] && [ -f "$ELEMENT_CONFIG" ] \
&& jq -e --arg name "matrix.$domain" '.default_server_config["m.homeserver"].server_name == $name' \
"$ELEMENT_CONFIG" &> /dev/null; then
jq 'del(.default_server_config)' "$ELEMENT_CONFIG" > "$ELEMENT_CONFIG.tmp" \
&& mv -f "$ELEMENT_CONFIG.tmp" "$ELEMENT_CONFIG"
fi
rm -f "$CORES_DIR/$domain.conf"
echo "Removed $domain from this computer."
if [ -n "$new_main" ]; then
make_main "$new_main" && main_changed
elif [ "$domain" = "$MAIN_DOMAIN" ]; then
rm -f "$CORE_CONF"
fi
}
run_configure() {
local choice i state
mapfile -t DOMAINS < <(core_domains)
if [ ${#DOMAINS[@]} -eq 0 ]; then
load_core ""
setup_core
return
fi
echo "Your Cores:"
for i in "${!DOMAINS[@]}"; do
if (load_core "${DOMAINS[$i]}" && core_done); then
state="\e[32mdone\e[0m "
else
state="\e[33mto do\e[0m"
fi
printf " %d) $state %s%s\n" $((i + 1)) "${DOMAINS[$i]}" \
"$([ "${DOMAINS[$i]}" = "$MAIN_DOMAIN" ] && echo " (main)")"
done
echo
echo " Choose a number to finish or change its setup, or:"
echo " a) Add a Core"
[ ${#DOMAINS[@]} -gt 1 ] && echo " m) Make another Core the main one"
echo " r) Remove a Core"
echo " q) Leave it as it is"
echo
read -rp "Choose: " choice
case "$choice" in
a|A)
load_core ""
setup_core ;;
m|M)
[ ${#DOMAINS[@]} -gt 1 ] || return 0
pick_core "Which should be the main one?" "$MAIN_DOMAIN" || { echo "Choose a number from the list."; return 1; }
make_main "$PICKED" && main_changed ;;
r|R)
pick_core "Which Core should be removed?" || { echo "Choose a number from the list."; return 1; }
remove_core "$PICKED" ;;
*)
if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#DOMAINS[@]} )); then
load_core "${DOMAINS[$((choice - 1))]}"
setup_core
fi ;;
esac
}
+35 -2
View File
@@ -14,12 +14,45 @@ is_installed() {
# Tailscale has no status field naming its login server, so tailscale.conf # Tailscale has no status field naming its login server, so tailscale.conf
# records which Core it was set up for # records which Core it was set up for
is_configured() { joined() {
[ -n "$CORE_DOMAIN" ] && [ "$TAILSCALE_DOMAIN" = "$CORE_DOMAIN" ] \ [ -n "$TAILSCALE_DOMAIN" ] \
&& tailscale status --json 2> /dev/null | jq -e '.BackendState == "Running"' > /dev/null && tailscale status --json 2> /dev/null | jq -e '.BackendState == "Running"' > /dev/null
} }
# Done when on the main Core's network
is_configured() {
[ -n "$CORE_DOMAIN" ] && [ "$TAILSCALE_DOMAIN" = "$CORE_DOMAIN" ] && joined
}
# Log out rather than only go down, so this computer's key is expired on
# headscale and it's off the network until it joins again
leave_network() {
sudo tailscale logout || return 1
rm -f "$TAILSCALE_CONF"
echo "This computer has left your Core's Tailscale network. To join again: configure tailscale"
}
run_configure() { run_configure() {
local choice
if joined &> /dev/null; then
echo "This computer is on $TAILSCALE_DOMAIN's Tailscale network (headscale.$TAILSCALE_DOMAIN)."
echo
if [ "$TAILSCALE_DOMAIN" = "$CORE_DOMAIN" ]; then
echo " 1) Join it again"
else
echo " 1) Join your main Core's network instead (headscale.$CORE_DOMAIN)"
fi
echo " 2) Leave the network"
echo " q) Leave it as it is"
echo
read -rp "Choose: " choice
case "$choice" in
1) ;;
2) leave_network; return ;;
*) return 0 ;;
esac
fi
if [ -z "$CORE_DOMAIN" ]; then if [ -z "$CORE_DOMAIN" ]; then
echo "This joins your Core's Tailscale network, so set up your Core first: configure core" echo "This joins your Core's Tailscale network, so set up your Core first: configure core"
return 1 return 1
+77 -2
View File
@@ -13,11 +13,16 @@ is_installed() {
command -v restic > /dev/null && command -v rclone > /dev/null && command -v secret-tool > /dev/null command -v restic > /dev/null && command -v rclone > /dev/null && command -v secret-tool > /dev/null
} }
is_configured() { backups_on() {
source "$COREDESKTOP/system/backup-env.sh" > /dev/null || return 1 source "$COREDESKTOP/system/backup-env.sh" > /dev/null || return 1
[ -n "$RESTIC_PASSWORD_COMMAND" ] && systemctl --user is-enabled -q coredesktop-backup.timer [ -n "$RESTIC_PASSWORD_COMMAND" ] && systemctl --user is-enabled -q coredesktop-backup.timer
} }
# Done when backing up to the main Core
is_configured() {
backups_on && [ "$BACKUP_WEBDAV" = "https://nextcloud.$CORE_DOMAIN/remote.php/dav/files/${CORE_EMAIL%@*}" ]
}
save_secret() { save_secret() {
printf '%s' "$3" | secret-tool store --label "CoreDesktop backup $1 ($2)" \ printf '%s' "$3" | secret-tool store --label "CoreDesktop backup $1 ($2)" \
coredesktop backup item "$1" url "$2" coredesktop backup item "$1" url "$2"
@@ -59,8 +64,78 @@ new_passphrase() {
save_secret passphrase "$WEBDAV/$BACKUP_DIR" "$pass" save_secret passphrase "$WEBDAV/$BACKUP_DIR" "$pass"
} }
# Stop the daily backup and any running now, and remove its units
stop_backups() {
systemctl --user disable --now coredesktop-backup.timer 2> /dev/null
systemctl --user stop coredesktop-backup.service 2> /dev/null
rm -f "$UNITS"/coredesktop-backup.{service,timer}
systemctl --user daemon-reload
}
# Delete the backup from Nextcloud, then what's on this computer for it:
# without the backup, the passphrase and app password are no use
delete_backups() {
local app_password
nextcloud_login || { echo "Couldn't sign in to Nextcloud."; return 1; }
echo "Deleting $BACKUP_DIR from your Nextcloud files..."
rclone purge ":webdav:$BACKUP_DIR" \
|| { echo "rclone couldn't delete $BACKUP_DIR."; return 1; }
echo "Deleted. Nextcloud keeps it in its trash bin, where it still uses space, until you empty it."
app_password="$(backup_secret nextcloud "$WEBDAV")"
[ -n "$app_password" ] && nextcloud_revoke_app_password "$app_password"
secret-tool clear coredesktop backup item nextcloud url "$WEBDAV"
secret-tool clear coredesktop backup item passphrase url "$WEBDAV/$BACKUP_DIR"
rm -f "$BACKUP_CONF"
}
turn_off() {
local answer
WEBDAV="$BACKUP_WEBDAV"
CORE_USER="$BACKUP_USER"
NEXTCLOUD="${BACKUP_WEBDAV%%/remote.php/*}"
stop_backups
echo "Backups are off."
echo
echo "The backups made so far are still in $BACKUP_DIR in your Nextcloud files."
if ! confirm "Delete them? They can't be restored from after that."; then
echo "Kept them. To turn backups back on: configure backup"
return 0
fi
read -rp "To delete them, type the folder name ($BACKUP_DIR): " answer
if [ "$answer" != "$BACKUP_DIR" ]; then
echo "That's not the folder name, so the backups were kept."
return 0
fi
delete_backups
}
run_configure() { run_configure() {
local pass local pass choice
if backups_on &> /dev/null; then
if is_configured &> /dev/null; then
echo "Your home folder is backed up daily to $BACKUP_DIR in your Nextcloud files."
echo
echo " 1) Set it up again"
else
echo "Your home folder is backed up daily to $BACKUP_DIR on ${BACKUP_WEBDAV%%/remote.php/*},"
echo "which isn't your main Core."
echo
echo " 1) Back up to your main Core ($CORE_DOMAIN) instead; the backups so far stay where they are"
fi
echo " 2) Turn backups off"
echo " q) Leave it as it is"
echo
read -rp "Choose: " choice
case "$choice" in
1) ;;
2) turn_off; return ;;
*) return 0 ;;
esac
fi
if [ -z "$CORE_DOMAIN" ] || [ -z "$CORE_EMAIL" ]; then if [ -z "$CORE_DOMAIN" ] || [ -z "$CORE_EMAIL" ]; then
echo "Backups go to your Nextcloud files, so set up your Core first: configure core" echo "Backups go to your Nextcloud files, so set up your Core first: configure core"
return 1 return 1
+19 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Add an account to GNOME Online Accounts, the way Settings does. """Add or remove an account in GNOME Online Accounts, the way Settings does.
Settings adds accounts by calling AddAccount on the Online Accounts service, Settings adds accounts by calling AddAccount on the Online Accounts service,
which saves the account and keeps its password in the GNOME keyring. This which saves the account and keeps its password in the GNOME keyring. This
@@ -9,6 +9,7 @@ so its password doesn't show up in the process list.
Usage: Usage:
goa-account exists <provider> <presentation-identity> goa-account exists <provider> <presentation-identity>
goa-account add < account.json goa-account add < account.json
goa-account remove <provider> <presentation-identity>
account.json: account.json:
{"provider": "owncloud", "identity": "...", "presentation_identity": "...", {"provider": "owncloud", "identity": "...", "presentation_identity": "...",
@@ -36,16 +37,20 @@ def accounts(bus):
(objects,) = call(bus, "/org/gnome/OnlineAccounts", (objects,) = call(bus, "/org/gnome/OnlineAccounts",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects",
None, "(a{oa{sa{sv}}})") None, "(a{oa{sa{sv}}})")
for interfaces in objects.values(): for path, interfaces in objects.items():
account = interfaces.get("org.gnome.OnlineAccounts.Account") account = interfaces.get("org.gnome.OnlineAccounts.Account")
if account: if account:
yield account yield path, account
def find(bus, provider, presentation_identity):
return [path for path, account in accounts(bus)
if account.get("ProviderType") == provider
and account.get("PresentationIdentity") == presentation_identity]
def exists(bus, provider, presentation_identity): def exists(bus, provider, presentation_identity):
return any(account.get("ProviderType") == provider return bool(find(bus, provider, presentation_identity))
and account.get("PresentationIdentity") == presentation_identity
for account in accounts(bus))
def add(bus, account): def add(bus, account):
@@ -58,6 +63,11 @@ def add(bus, account):
"org.gnome.OnlineAccounts.Manager", "AddAccount", parameters, "(o)") "org.gnome.OnlineAccounts.Manager", "AddAccount", parameters, "(o)")
def remove(bus, provider, presentation_identity):
for path in find(bus, provider, presentation_identity):
call(bus, path, "org.gnome.OnlineAccounts.Account", "Remove", None, "()")
def main(): def main():
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
if len(sys.argv) == 4 and sys.argv[1] == "exists": if len(sys.argv) == 4 and sys.argv[1] == "exists":
@@ -68,6 +78,9 @@ def main():
return 0 return 0
add(bus, account) add(bus, account)
return 0 return 0
if len(sys.argv) == 4 and sys.argv[1] == "remove":
remove(bus, sys.argv[2], sys.argv[3])
return 0
print(__doc__.strip().split("\n\n")[2], file=sys.stderr) print(__doc__.strip().split("\n\n")[2], file=sys.stderr)
return 2 return 2
+6
View File
@@ -25,3 +25,9 @@ nextcloud_app_password() {
*) return 1 ;; *) return 1 ;;
esac esac
} }
# Revoke an app password, signing in with it. Uses CORE_USER and NEXTCLOUD.
nextcloud_revoke_app_password() {
auth_header "$CORE_USER" "$1" | curl -sf -o /dev/null -X DELETE -H @- \
-H 'OCS-APIRequest: true' "$NEXTCLOUD/ocs/v2.php/core/apppassword"
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# configure can set up more than one Core, each in cores/<domain>.conf;
# core.conf stays as the main Core's
CORE_CONF="$HOME/.config/coredesktop/core.conf"
CORES_DIR="$HOME/.config/coredesktop/cores"
[ -f "$CORE_CONF" ] || exit 0
CORE_DOMAIN="$(source "$CORE_CONF" && echo "$CORE_DOMAIN")"
[ -n "$CORE_DOMAIN" ] || exit 0
mkdir -p "$CORES_DIR" || exit 1
[ -f "$CORES_DIR/$CORE_DOMAIN.conf" ] || cp "$CORE_CONF" "$CORES_DIR/$CORE_DOMAIN.conf"