v2 post-migration bug fixes, lidarr_missing_art port, and Partnership SSH/FolderView3 enhancements
Bug fixes across the ecosystem after v1→v2 architecture migration and Unraid 7.2.5 upgrade:
- common.sh: fix _alias_array() phantom empty element (removed [@]:-} pattern), fix
resolve_remote_ip() with Tailscale FQDN lowercase + awk fallback, add FANART/LASTFM
key aliases in detect_hosts(), global [@]:-} sweep across 10+ scripts
- webgui_restart.sh: fix emhttp detection (pgrep emhttpd) and restart command for 7.2.5
(/usr/local/sbin/emhttp stop && start — rc.emhttp removed in 7.2.5)
- bandwidth_monitor.sh, continuous_scripts_status.sh: fix 'local' keyword outside function
- backup_verify.sh: fix resolve_remote_ip() called before detect_hosts()
- Orchestrators: fix script display duplication bug in status output (${entry##*/})
- rsync_stop.sh, git_pull_execute.sh, partnership_manager.sh, coffee_report: lowercase all
tailscale ip -4 call sites to match Tailscale's lowercase device names
- master_host1.conf: fix SSH key path (gmer4lfe_rsync_automation), add FANART/LASTFM keys
- master_host2.conf: add FANART/LASTFM API keys
New: Media/lidarr_missing_art.sh
- Full ecosystem port of standalone Lidarr artwork fetcher
- Fetches missing album art via fanart.tv + Last.fm APIs
- @tsv batch extraction: 1 jq call per API response vs N*albums (8050 albums in 24s)
- HOST guard (HOST1 only), --status, --dry-run, acquire_lock
New: Initial_run/ssh_setup.sh
- Generates {hostname}_rsync_automation ed25519 keypair (skip if exists, --force to regen)
- ssh-copy-id to remote via Tailscale IP, auto-updates master_host*.conf
- --validate mode: strike tracking (SSH_MAX_STRIKES, SSH_STRIKE_RESET_HRS),
notify at limit — Tailscale-unreachable remote does NOT count as SSH strike
New: Initial_run/partnership_onboard.sh
- Orchestrator: ssh_setup.sh then partnership_manager.sh --onboard in one command
Partnership/partnership_manager.sh: FolderView3 integration
- Derive partner folder name at runtime (strip unraid- prefix case-insensitively)
- --onboard: create {Mirror}-Failover folder with failover tier containers
- --offboard (both paths): stop + rm containers in folder, remove JSON entry
- --check: calls ssh_setup.sh --validate when IP resolves but SSH state empty
- --status: shows FolderView3 folder state and containers inline
master.conf: SSH_MAX_STRIKES, SSH_STRIKE_RESET_HRS, PARTNERSHIP_FOLDERVIEW3,
PARTNERSHIP_FOLDERVIEW3_URL added to Partnership section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
7cecda6b90
commit
5526bc1eca
@@ -260,7 +260,7 @@ reconfigure_local_webuis() {
|
||||
log "Reconfiguring local auth WebUIs → ${target_ip}..."
|
||||
|
||||
local failures=0
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
local container="${entry%%|*}"
|
||||
local port="${entry##*|}"
|
||||
@@ -331,11 +331,188 @@ remove_tailscale_device() {
|
||||
error "Failed to remove $hostname from Tailscale — remove manually"
|
||||
}
|
||||
|
||||
# ── FolderView3 integration ───────────────────────────────────────────────────────────────────
|
||||
FOLDERVIEW3_DIR="/usr/local/emhttp/plugins/folder.view3"
|
||||
FOLDERVIEW3_JSON="/boot/config/plugins/folder.view3/docker.json"
|
||||
|
||||
# Derive short partner name: strip unraid- prefix (case-insensitive) if present
|
||||
derive_partner_folder_name() {
|
||||
local hostname="$1"
|
||||
local short="${hostname,,}"
|
||||
[[ "$short" == unraid-* ]] && short="${short:7}"
|
||||
# Capitalise first char for readability: jayred365 → Jayred365-Failover
|
||||
echo "${short^}-Failover"
|
||||
}
|
||||
|
||||
folderview3_ensure_plugin() {
|
||||
if [[ -d "$FOLDERVIEW3_DIR" ]]; then
|
||||
log "FolderView3 plugin present ✅"
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "${PARTNERSHIP_FOLDERVIEW3_URL:-}" ]]; then
|
||||
warn "FolderView3 plugin not installed and PARTNERSHIP_FOLDERVIEW3_URL is empty"
|
||||
warn "Install manually from Community Applications or set PARTNERSHIP_FOLDERVIEW3_URL in master.conf"
|
||||
return 1
|
||||
fi
|
||||
warn "FolderView3 not found — installing from CA..."
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would run: plugin install $PARTNERSHIP_FOLDERVIEW3_URL"
|
||||
return 0
|
||||
fi
|
||||
plugin install "$PARTNERSHIP_FOLDERVIEW3_URL" 2>/dev/null && \
|
||||
log "FolderView3 installed ✅" || {
|
||||
warn "FolderView3 install failed — folder will not be created"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
folderview3_create_partner_folder() {
|
||||
local partner_name="$1"
|
||||
shift
|
||||
local containers=("$@")
|
||||
|
||||
log "FolderView3: creating folder '$partner_name' with ${#containers[@]} container(s)..."
|
||||
|
||||
folderview3_ensure_plugin || return 1
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would create FolderView3 folder: $partner_name"
|
||||
for c in "${containers[@]}"; do
|
||||
[[ -n "$c" ]] && warn " DRY RUN — container: $c"
|
||||
done
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Initialise JSON if missing
|
||||
if [[ ! -f "$FOLDERVIEW3_JSON" ]]; then
|
||||
mkdir -p "$(dirname "$FOLDERVIEW3_JSON")"
|
||||
echo '{}' > "$FOLDERVIEW3_JSON"
|
||||
fi
|
||||
|
||||
# Check folder doesn't already exist
|
||||
local existing
|
||||
existing=$(jq -r --arg name "$partner_name" \
|
||||
'to_entries[] | select(.value.name == $name) | .key' \
|
||||
"$FOLDERVIEW3_JSON" 2>/dev/null)
|
||||
if [[ -n "$existing" ]]; then
|
||||
log "FolderView3: folder '$partner_name' already exists (id: $existing) — skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Build containers JSON array
|
||||
local containers_json
|
||||
containers_json=$(printf '%s\n' "${containers[@]}" | \
|
||||
grep -v '^$' | jq -R . | jq -s .)
|
||||
|
||||
# Generate a stable random ID from timestamp+name
|
||||
local folder_id
|
||||
folder_id=$(echo "${partner_name}$(date +%s%N)" | md5sum | head -c 12)
|
||||
|
||||
jq --arg id "$folder_id" --arg name "$partner_name" \
|
||||
--argjson containers "$containers_json" \
|
||||
'.[$id] = {"name": $name, "containers": $containers, "containerImages": {}}' \
|
||||
"$FOLDERVIEW3_JSON" > "${FOLDERVIEW3_JSON}.tmp" && \
|
||||
mv "${FOLDERVIEW3_JSON}.tmp" "$FOLDERVIEW3_JSON" && \
|
||||
log "FolderView3: folder '$partner_name' created with ${#containers[@]} container(s) ✅" || {
|
||||
warn "FolderView3: failed to write JSON — check $FOLDERVIEW3_JSON"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
folderview3_remove_partner_folder() {
|
||||
local partner_name="$1"
|
||||
|
||||
log "FolderView3: removing folder '$partner_name' and stopping its containers..."
|
||||
|
||||
if [[ ! -f "$FOLDERVIEW3_JSON" ]]; then
|
||||
log "FolderView3: JSON not found — nothing to remove"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
warn "jq not found — cannot manage FolderView3 JSON"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Get containers from this folder
|
||||
local containers_json
|
||||
containers_json=$(jq -r --arg name "$partner_name" \
|
||||
'to_entries[] | select(.value.name == $name) | .value.containers[]' \
|
||||
"$FOLDERVIEW3_JSON" 2>/dev/null)
|
||||
|
||||
if [[ -z "$containers_json" ]]; then
|
||||
log "FolderView3: folder '$partner_name' not found — nothing to remove"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Stop and remove each container in the folder
|
||||
local stopped=0 removed=0 failed=0
|
||||
while IFS= read -r container; do
|
||||
[[ -z "$container" ]] && continue
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would stop + rm: $container"
|
||||
continue
|
||||
fi
|
||||
# Stop if running
|
||||
if timeout "${DOCKER_TIMEOUT:-30}" docker inspect "$container" >/dev/null 2>&1; then
|
||||
if timeout "${DOCKER_TIMEOUT:-30}" docker stop "$container" >/dev/null 2>&1; then
|
||||
log "$container stopped ✅"
|
||||
(( stopped++ ))
|
||||
else
|
||||
warn "$container stop failed"
|
||||
(( failed++ ))
|
||||
fi
|
||||
if timeout "${DOCKER_TIMEOUT:-30}" docker rm "$container" >/dev/null 2>&1; then
|
||||
log "$container removed ✅"
|
||||
(( removed++ ))
|
||||
else
|
||||
warn "$container rm failed (may not exist)"
|
||||
fi
|
||||
else
|
||||
log "$container not found locally — skipping"
|
||||
fi
|
||||
done <<< "$containers_json"
|
||||
|
||||
# Remove folder entry from JSON
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
jq --arg name "$partner_name" \
|
||||
'with_entries(select(.value.name != $name))' \
|
||||
"$FOLDERVIEW3_JSON" > "${FOLDERVIEW3_JSON}.tmp" && \
|
||||
mv "${FOLDERVIEW3_JSON}.tmp" "$FOLDERVIEW3_JSON" && \
|
||||
log "FolderView3: folder '$partner_name' removed ✅" || \
|
||||
warn "FolderView3: failed to remove folder from JSON"
|
||||
else
|
||||
warn "DRY RUN — would remove folder '$partner_name' from $FOLDERVIEW3_JSON"
|
||||
fi
|
||||
|
||||
[[ "$DRY_RUN" == false ]] && \
|
||||
log "FolderView3 cleanup: $stopped stopped, $removed removed, $failed failed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Gather all partner failover containers for this server (all tiers)
|
||||
gather_partner_failover_containers() {
|
||||
local out_var="$1"
|
||||
eval "${out_var}=()"
|
||||
local tier var
|
||||
for tier in TIER1 TIER2 TIER3 TIER4; do
|
||||
var="FAILOVER_${MY_ID}_RUNS_FOR_${REMOTE_ID}_${tier}"
|
||||
# Check if var is set and is an array
|
||||
if declare -p "$var" >/dev/null 2>&1; then
|
||||
local -a _tmp_arr
|
||||
eval "_tmp_arr=(\"\${${var}[@]}\")"
|
||||
for c in "${_tmp_arr[@]}"; do
|
||||
[[ -n "$c" ]] && eval "${out_var}+=(\"\$c\")"
|
||||
done
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_both_healthy() {
|
||||
mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }
|
||||
|
||||
local mirror_ip
|
||||
mirror_ip=$(tailscale ip -4 "$MIRROR" 2>/dev/null)
|
||||
mirror_ip=$(tailscale ip -4 "${MIRROR,,}" 2>/dev/null)
|
||||
[[ -z "$mirror_ip" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; return 1; }
|
||||
|
||||
timeout "$SSH_TIMEOUT" ssh -i "$MIRROR_SSH_KEY" \
|
||||
@@ -387,8 +564,8 @@ update_master_conf() {
|
||||
if [[ "$MODE" == "status" ]]; then
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY PARTNERSHIP STATUS ━━━━━"
|
||||
OWNER_IP=$(tailscale ip -4 "$OWNER" 2>/dev/null || echo "unreachable")
|
||||
MIRROR_IP=$(tailscale ip -4 "$MIRROR" 2>/dev/null || echo "unreachable")
|
||||
OWNER_IP=$(tailscale ip -4 "${OWNER,,}" 2>/dev/null || echo "unreachable")
|
||||
MIRROR_IP=$(tailscale ip -4 "${MIRROR,,}" 2>/dev/null || echo "unreachable")
|
||||
echo " $ICON_HOST My ID: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo " Owner: $OWNER_ID ($OWNER — $OWNER_IP)"
|
||||
echo " Mirror: $MIRROR_ID ($MIRROR — $MIRROR_IP)"
|
||||
@@ -413,7 +590,7 @@ if [[ "$MODE" == "status" ]]; then
|
||||
echo ""
|
||||
|
||||
# Remote state
|
||||
REMOTE_IP=$(tailscale ip -4 "$REMOTE_SERVER_NAME" 2>/dev/null)
|
||||
REMOTE_IP=$(tailscale ip -4 "${REMOTE_SERVER_NAME,,}" 2>/dev/null)
|
||||
if [[ -n "$REMOTE_IP" ]]; then
|
||||
REMOTE_CONTENT=$(read_remote_state "$REMOTE_IP" "$SSH_KEY" "$REMOTE_STATE_FILE")
|
||||
if [[ -n "$REMOTE_CONTENT" ]]; then
|
||||
@@ -437,11 +614,32 @@ if [[ "$MODE" == "status" ]]; then
|
||||
|
||||
echo ""
|
||||
echo " Auth WebUIs:"
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
echo " ${entry%%|*} → port ${entry##*|}"
|
||||
done
|
||||
|
||||
if [[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]]; then
|
||||
echo ""
|
||||
FOLDER_STATUS_NAME=$(derive_partner_folder_name "$REMOTE_SERVER_NAME")
|
||||
if [[ -f "$FOLDERVIEW3_JSON" ]]; then
|
||||
FOLDER_EXISTS=$(jq -r --arg name "$FOLDER_STATUS_NAME" \
|
||||
'to_entries[] | select(.value.name == $name) | .key' \
|
||||
"$FOLDERVIEW3_JSON" 2>/dev/null)
|
||||
if [[ -n "$FOLDER_EXISTS" ]]; then
|
||||
FOLDER_CONTAINERS=$(jq -r --arg name "$FOLDER_STATUS_NAME" \
|
||||
'[to_entries[] | select(.value.name == $name) | .value.containers[]] | join(", ")' \
|
||||
"$FOLDERVIEW3_JSON" 2>/dev/null)
|
||||
echo " FolderView3: $FOLDER_STATUS_NAME ✅"
|
||||
echo " Containers: $FOLDER_CONTAINERS"
|
||||
else
|
||||
echo " FolderView3: $FOLDER_STATUS_NAME — not found in JSON"
|
||||
fi
|
||||
else
|
||||
echo " FolderView3: config not found ($FOLDERVIEW3_JSON)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$OFFLINE_COUNTER" ]]; then
|
||||
OFFLINE_DAYS=$(cat "$OFFLINE_COUNTER" 2>/dev/null || echo 0)
|
||||
[[ "$OFFLINE_DAYS" -gt 0 ]] && \
|
||||
@@ -484,14 +682,21 @@ if [[ "$MODE" == "check" ]]; then
|
||||
fi
|
||||
|
||||
# Read remote state file
|
||||
REMOTE_IP=$(tailscale ip -4 "$REMOTE_SERVER_NAME" 2>/dev/null)
|
||||
REMOTE_IP=$(tailscale ip -4 "${REMOTE_SERVER_NAME,,}" 2>/dev/null)
|
||||
if [[ -z "$REMOTE_IP" ]]; then
|
||||
log "Partnership check — remote unreachable, skipping state check"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REMOTE_CONTENT=$(read_remote_state "$REMOTE_IP" "$SSH_KEY" "$REMOTE_STATE_FILE")
|
||||
[[ -z "$REMOTE_CONTENT" ]] && log "Partnership check — remote state file not found" && exit 0
|
||||
if [[ -z "$REMOTE_CONTENT" ]]; then
|
||||
# IP resolved but SSH returned nothing — could be auth failure, not just missing file
|
||||
if [[ -f "$SCRIPT_DIR/../Initial_run/ssh_setup.sh" ]]; then
|
||||
bash "$SCRIPT_DIR/../Initial_run/ssh_setup.sh" --validate 2>/dev/null || true
|
||||
fi
|
||||
log "Partnership check — remote state file not found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REMOTE_STATE=$(echo "$REMOTE_CONTENT" | grep "^state=" | cut -d= -f2)
|
||||
LOCAL_STATE=$(read_state_file "$LOCAL_STATE_FILE" "state" 2>/dev/null || echo "UNKNOWN")
|
||||
@@ -587,8 +792,8 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
check_remote_array || exit 1
|
||||
check_remote_docker_daemon || exit 1
|
||||
|
||||
OWNER_IP=$(tailscale ip -4 "$OWNER" 2>/dev/null)
|
||||
MIRROR_IP=$(tailscale ip -4 "$MIRROR" 2>/dev/null)
|
||||
OWNER_IP=$(tailscale ip -4 "${OWNER,,}" 2>/dev/null)
|
||||
MIRROR_IP=$(tailscale ip -4 "${MIRROR,,}" 2>/dev/null)
|
||||
|
||||
[[ -z "$OWNER_IP" ]] && { error "Cannot resolve $OWNER Tailscale IP"; exit 1; }
|
||||
[[ -z "$MIRROR_IP" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; exit 1; }
|
||||
@@ -601,7 +806,7 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
echo "━━━ $ICON_CONTAINERS Reconfigure Mirror WebUIs → $OWNER_IP ━━━"
|
||||
|
||||
WEBUI_FAILURES=0
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
container="${entry%%|*}"
|
||||
port="${entry##*|}"
|
||||
@@ -613,7 +818,7 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
if [[ "${PARTNERSHIP_ONBOARD_VERIFY:-true}" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_VERIFY WebUI Connectivity ━━━"
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
container="${entry%%|*}"
|
||||
port="${entry##*|}"
|
||||
@@ -636,6 +841,20 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY"
|
||||
echo "0" > "$OFFLINE_COUNTER"
|
||||
|
||||
# FolderView3 — create partner folder with this server's failover containers for remote
|
||||
if [[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS FolderView3 Integration ━━━"
|
||||
PARTNER_FOLDER_NAME=$(derive_partner_folder_name "$MIRROR")
|
||||
declare -a PARTNER_CONTAINERS=()
|
||||
gather_partner_failover_containers PARTNER_CONTAINERS
|
||||
if [[ ${#PARTNER_CONTAINERS[@]} -gt 0 ]]; then
|
||||
folderview3_create_partner_folder "$PARTNER_FOLDER_NAME" "${PARTNER_CONTAINERS[@]}"
|
||||
else
|
||||
log "No FAILOVER_${MY_ID}_RUNS_FOR_${REMOTE_ID}_TIER* containers configured — skipping folder creation"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY ONBOARD SUMMARY ━━━━━"
|
||||
@@ -692,12 +911,20 @@ if [[ "$MODE" == "offboard" ]]; then
|
||||
echo "━━━ $ICON_CONTAINERS Reconfigure Local WebUIs → localhost ━━━"
|
||||
reconfigure_local_webuis "localhost"
|
||||
|
||||
# FolderView3 — remove partner folder and clean containers
|
||||
if [[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS FolderView3 Cleanup ━━━"
|
||||
PARTNER_FOLDER_NAME=$(derive_partner_folder_name "$OWNER")
|
||||
folderview3_remove_partner_folder "$PARTNER_FOLDER_NAME"
|
||||
fi
|
||||
|
||||
NOW=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
write_state_file "$LOCAL_STATE_FILE" \
|
||||
"INACTIVE" "" "$NOW" "$LOCAL_SERVER_NAME" "$REASON"
|
||||
log "Local state: INACTIVE ✅"
|
||||
|
||||
OWNER_IP=$(tailscale ip -4 "$OWNER" 2>/dev/null)
|
||||
OWNER_IP=$(tailscale ip -4 "${OWNER,,}" 2>/dev/null)
|
||||
if [[ -n "$OWNER_IP" ]]; then
|
||||
push_state_to_remote "$LOCAL_STATE_FILE" "$OWNER_IP" "$MIRROR_SSH_KEY"
|
||||
notify "Partnership offboard requested by $MIRROR — $OWNER will finalise on next check" \
|
||||
@@ -738,7 +965,7 @@ if [[ "$MODE" == "offboard" ]]; then
|
||||
echo "━━━ $ICON_SYNC Final Sync ━━━"
|
||||
do_final_sync
|
||||
|
||||
MIRROR_IP=$(tailscale ip -4 "$MIRROR" 2>/dev/null)
|
||||
MIRROR_IP=$(tailscale ip -4 "${MIRROR,,}" 2>/dev/null)
|
||||
MIRROR_REACHABLE=false
|
||||
[[ -n "$MIRROR_IP" ]] && MIRROR_REACHABLE=true
|
||||
|
||||
@@ -748,7 +975,7 @@ if [[ "$MODE" == "offboard" ]]; then
|
||||
|
||||
WEBUI_FAILURES=0
|
||||
if [[ "$MIRROR_REACHABLE" == true ]]; then
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
container="${entry%%|*}"
|
||||
port="${entry##*|}"
|
||||
@@ -784,6 +1011,14 @@ if [[ "$MODE" == "offboard" ]]; then
|
||||
push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY"
|
||||
fi
|
||||
|
||||
# FolderView3 — remove partner folder and clean containers on this (owner) side
|
||||
if [[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS FolderView3 Cleanup ━━━"
|
||||
PARTNER_FOLDER_NAME=$(derive_partner_folder_name "$MIRROR")
|
||||
folderview3_remove_partner_folder "$PARTNER_FOLDER_NAME"
|
||||
fi
|
||||
|
||||
# Tailscale removal
|
||||
if [[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]]; then
|
||||
echo ""
|
||||
@@ -816,14 +1051,16 @@ if [[ "$MODE" == "offboard" ]]; then
|
||||
# Summary
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY OFFBOARD SUMMARY ━━━━━"
|
||||
echo " Owner: $OWNER_ID ($OWNER)"
|
||||
echo " Mirror: $MIRROR_ID ($MIRROR)"
|
||||
echo " Final sync: complete ✅"
|
||||
echo " Owner: $OWNER_ID ($OWNER)"
|
||||
echo " Mirror: $MIRROR_ID ($MIRROR)"
|
||||
echo " Final sync: complete ✅"
|
||||
echo " WebUI failures: $WEBUI_FAILURES"
|
||||
echo " Critical rsync: disabled ✅"
|
||||
echo " State: INACTIVE ✅"
|
||||
echo " State: INACTIVE ✅"
|
||||
[[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]] && \
|
||||
echo " FolderView3: ${PARTNER_FOLDER_NAME:-} cleaned ✅"
|
||||
[[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]] && \
|
||||
echo " Tailscale: $MIRROR removed ✅"
|
||||
echo " Tailscale: $MIRROR removed ✅"
|
||||
echo ""
|
||||
echo " $MIRROR leaves with:"
|
||||
echo " ✓ Current auth config (final sync)"
|
||||
@@ -912,8 +1149,8 @@ if [[ "$MODE" == "transfer" ]]; then
|
||||
NEW_OWNER_SSH_KEY="${!NEW_OWNER_SSH_KEY_VAR}"
|
||||
NEW_MIRROR_SSH_KEY="${!NEW_MIRROR_SSH_KEY_VAR}"
|
||||
|
||||
NEW_OWNER_IP=$(tailscale ip -4 "$NEW_OWNER" 2>/dev/null)
|
||||
NEW_MIRROR_IP=$(tailscale ip -4 "$NEW_MIRROR" 2>/dev/null)
|
||||
NEW_OWNER_IP=$(tailscale ip -4 "${NEW_OWNER,,}" 2>/dev/null)
|
||||
NEW_MIRROR_IP=$(tailscale ip -4 "${NEW_MIRROR,,}" 2>/dev/null)
|
||||
|
||||
[[ -z "$NEW_OWNER_IP" ]] && { error "Cannot resolve new owner Tailscale IP"; exit 1; }
|
||||
[[ -z "$NEW_MIRROR_IP" ]] && { error "Cannot resolve new mirror Tailscale IP"; exit 1; }
|
||||
@@ -929,7 +1166,7 @@ if [[ "$MODE" == "transfer" ]]; then
|
||||
log "New mirror ($NEW_MIRROR) WebUIs → new owner ($NEW_OWNER_IP)"
|
||||
|
||||
WEBUI_FAILURES=0
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]:-}"; do
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
container="${entry%%|*}"
|
||||
port="${entry##*|}"
|
||||
|
||||
Reference in New Issue
Block a user