python3 isn't installed on stock Unraid (confirmed absent on HOST1), so the JSON-parsing pipeline silently produced zero peers every time, making the Tailscale fallback tier a permanent no-op since it was written. Replaced with plain 'tailscale status' + awk, matching the pattern common.sh already uses.
264 lines
12 KiB
XML
264 lines
12 KiB
XML
<?xml version='1.0' standalone='yes'?>
|
|
<!DOCTYPE PLUGIN [
|
|
<!ENTITY name "varaverk">
|
|
<!ENTITY author "gmer4lfe">
|
|
<!ENTITY version "2026.05.31">
|
|
<!ENTITY sha256 "d588470d6cc7f284601cb56039d5dfea6fb5bb1ee2c800657438a8edacafbd01">
|
|
<!ENTITY launch "varaverk/monitor">
|
|
<!ENTITY github "https://github.com/FailedProxy/Varaverk">
|
|
<!ENTITY branch "main">
|
|
<!ENTITY cfgdir "/boot/config/plugins/varaverk">
|
|
<!ENTITY plugdir "/usr/local/emhttp/plugins/varaverk">
|
|
<!ENTITY pkg "varaverk-&version;-noarch-1.txz">
|
|
]>
|
|
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;"
|
|
support="https://github.com/FailedProxy/Varaverk/issues"
|
|
icon="/plugins/varaverk/icons/varaverk.png">
|
|
|
|
<CHANGES>
|
|
###2026.05.31
|
|
- Packaged release: web files now ship as a .txz that Unraid reinstalls to RAM on every boot
|
|
- Survives reboots with zero manual steps (no symlink, no go script) — fixes plugin vanishing after OS upgrades
|
|
- Scripts are git-cloned to appdata on first install; web files stay on flash (~200KB)
|
|
- Updates handled in-UI (git pull); the plugin no longer pulls on every boot
|
|
|
|
###2026.05.30
|
|
- First-run setup wizard: auto-detects hostname, creates master.conf + host conf from templates
|
|
- Scheduler setup mode: after wizard, master.conf and host conf open sequentially with forced save flow
|
|
- Partnership tab: Onboard button highlighted on arrival from wizard; disabled until partner is configured
|
|
- HOST2 install paths: state-file pull, master.conf push detection, conf-only flow
|
|
- Onboard Step 9: master.conf automatically pushed to all listed hosts on onboard completion
|
|
- Graceful pre-onboard state: neutral banners instead of error warnings before SSH is configured
|
|
- GitHub link in tab bar and Settings page; Community Apps support URL
|
|
|
|
###2026.05.28
|
|
- Initial release: Monitor, Scheduler, Docker, Watchdog, Partnership, Fallback, Arrs tabs
|
|
- Mutual container fallback with tiered escalation and strike-confirmed handback
|
|
- Partnership lifecycle: onboard, offboard, transfer
|
|
- Rsync profile system with per-share container stops and writeback
|
|
- Watchdog: Tier 1 (explicit) + Tier 2 (global scan) container monitoring
|
|
- master.conf push-on-save to all configured partners via SSH
|
|
</CHANGES>
|
|
|
|
<!--
|
|
── 1. Web files symlink (runs on every boot) ──────────────────────────────────
|
|
Instead of extracting a .txz, we symlink the installed plugin web dir directly
|
|
to the workspace on flash. Changes to Plugin/unraid/ are live instantly — no
|
|
package build, no sync step. /boot is always mounted before this runs.
|
|
────────────────────────────────────────────────────────────────────────────────
|
|
-->
|
|
<FILE Run="/bin/bash">
|
|
<INLINE>
|
|
<![CDATA[
|
|
#!/bin/bash
|
|
WEB_DIR="/usr/local/emhttp/plugins/varaverk"
|
|
SRC="/boot/config/plugins/varaverk/Plugin/unraid"
|
|
[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
|
|
[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
|
|
ln -sf "$SRC" "$WEB_DIR"
|
|
echo "[Varaverk] web dir symlinked → $SRC"
|
|
]]>
|
|
</INLINE>
|
|
</FILE>
|
|
|
|
<!--
|
|
── 2. Scripts bootstrap (first install only) ──────────────────────────────────
|
|
Clones the repo directly into the plugin config dir on flash (/boot/config/plugins/varaverk).
|
|
No array dependency — scripts live on flash (64GB NVMe) and are available at boot.
|
|
Uses git init+fetch+reset so the clone works into the non-empty cfgdir (varaverk.cfg,
|
|
varaverk-*.txz etc. are already there). Never auto-pulls — updates via the UI git pull.
|
|
|
|
Clone source priority:
|
|
1. Gitea (local container, then Tailscale peer probe) — reads settings from varaverk.cfg
|
|
2. Gitea (GITEA_DOMAIN) — public domain fallback if local/Tailscale lookup fails
|
|
3. GitHub (public) — HTTPS fallback if Gitea is unreachable by any path
|
|
────────────────────────────────────────────────────────────────────────────────
|
|
-->
|
|
<FILE Run="/bin/bash" Method="install">
|
|
<INLINE>
|
|
<![CDATA[
|
|
#!/bin/bash
|
|
PLUGIN="varaverk"
|
|
CFG_DIR="/boot/config/plugins/$PLUGIN"
|
|
CFG_FILE="$CFG_DIR/varaverk.cfg"
|
|
GITHUB="https://github.com/FailedProxy/Varaverk"
|
|
BRANCH="main"
|
|
LOG="$CFG_DIR/install.log"
|
|
|
|
mkdir -p "$CFG_DIR"
|
|
log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; }
|
|
|
|
# Scripts live in the plugin dir on flash — no array needed.
|
|
SCRIPTS_DIR="$CFG_DIR"
|
|
CONF_DIR="$SCRIPTS_DIR/Configurations"
|
|
DEPLOY_DIR="$SCRIPTS_DIR/Deployment"
|
|
|
|
# ── Boot device check ─────────────────────────────────────────────────────────
|
|
# Warn if /boot is on a USB/removable device. Varaverk is designed for internal
|
|
# NVMe/SSD boot — git repo + state files + data writes on USB will wear it out
|
|
# fast and may run out of space. Install proceeds but user is warned.
|
|
_boot_dev=$(df /boot --output=source 2>/dev/null | tail -1)
|
|
_boot_base=$(lsblk -no pkname "$_boot_dev" 2>/dev/null || basename "${_boot_dev%[0-9p]*}")
|
|
_removable=$(cat "/sys/block/${_boot_base}/removable" 2>/dev/null || echo "0")
|
|
if [[ "$_removable" == "1" ]]; then
|
|
log "WARNING: /boot is on a removable/USB device ($_boot_dev)"
|
|
log "WARNING: Varaverk is designed for internal NVMe/SSD boot."
|
|
log "WARNING: Running from USB risks drive wear and space exhaustion."
|
|
log "WARNING: Strongly recommend migrating boot to an internal NVMe/SSD drive."
|
|
fi
|
|
unset _boot_dev _boot_base _removable
|
|
|
|
# Seed varaverk.cfg with defaults (SCRIPTS_DIR + Gitea settings) if not present.
|
|
# Requires internal NVMe/SSD boot — scripts live on flash, available before array mounts.
|
|
if [[ ! -f "$CFG_FILE" ]]; then
|
|
cat > "$CFG_FILE" <<'CFGEOF'
|
|
SCRIPTS_DIR="/boot/config/plugins/varaverk"
|
|
GITEA_CONTAINER="Gitea"
|
|
GITEA_REPO_PATH="FailedProxy/Varaverk.git"
|
|
GITEA_SSH_KEY="/root/.ssh/unraid_gitea"
|
|
GITEA_DOMAIN=""
|
|
SSH_PORT="221"
|
|
CFGEOF
|
|
log "seeded varaverk.cfg"
|
|
fi
|
|
|
|
# Read Gitea settings from varaverk.cfg (allows override without editing .plg).
|
|
_read_cfg() { grep -oP "(?<=^${1}=\")[^\"]*" "$CFG_FILE" 2>/dev/null || echo "${2}"; }
|
|
GITEA_CONTAINER=$(_read_cfg GITEA_CONTAINER "Gitea")
|
|
GITEA_REPO_PATH=$(_read_cfg GITEA_REPO_PATH "FailedProxy/Varaverk.git")
|
|
GITEA_SSH_KEY=$(_read_cfg GITEA_SSH_KEY "/root/.ssh/unraid_gitea")
|
|
GITEA_DOMAIN=$(_read_cfg GITEA_DOMAIN "")
|
|
SSH_PORT=$(_read_cfg SSH_PORT "221")
|
|
|
|
# Clone on first install only; never auto-pull (updates via the UI git pull).
|
|
if [[ ! -d "$SCRIPTS_DIR/.git" ]]; then
|
|
log "initialising repo in $SCRIPTS_DIR ($BRANCH)..."
|
|
|
|
# Locate Gitea: local container → Tailscale → GITEA_DOMAIN → GitHub.
|
|
GITEA_IP=""
|
|
if command -v docker >/dev/null 2>&1 && \
|
|
docker ps --format "{{.Names}}" 2>/dev/null | grep -q "^${GITEA_CONTAINER}$"; then
|
|
GITEA_IP=$(hostname -I | awk '{print $1}')
|
|
log "Gitea running locally — using $GITEA_IP"
|
|
elif command -v tailscale >/dev/null 2>&1; then
|
|
# Try each known peer until we find one hosting Gitea
|
|
while IFS= read -r peer_ip; do
|
|
if ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
|
|
-o ConnectTimeout=3 -o StrictHostKeyChecking=no \
|
|
-o BatchMode=yes "git@${peer_ip}" info 2>/dev/null | grep -q "varaverk\|Gitea\|gitea"; then
|
|
GITEA_IP="$peer_ip"
|
|
log "Gitea found on Tailscale peer $GITEA_IP"
|
|
break
|
|
fi
|
|
done < <(tailscale status 2>/dev/null | \
|
|
awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$')
|
|
fi
|
|
|
|
if [[ -z "$GITEA_IP" && -n "$GITEA_DOMAIN" ]]; then
|
|
log "local/Tailscale lookup failed — falling back to domain: $GITEA_DOMAIN"
|
|
GITEA_IP="$GITEA_DOMAIN"
|
|
fi
|
|
|
|
# init-in-place — git clone would fail because the dir already has files.
|
|
git -C "$SCRIPTS_DIR" init >> "$LOG" 2>&1
|
|
|
|
CLONED=false
|
|
if [[ -n "$GITEA_IP" && -f "$GITEA_SSH_KEY" ]]; then
|
|
GITEA_URL="ssh://git@${GITEA_IP}:${SSH_PORT}/${GITEA_REPO_PATH}"
|
|
log "trying Gitea: $GITEA_URL"
|
|
git -C "$SCRIPTS_DIR" remote add origin "$GITEA_URL" >> "$LOG" 2>&1
|
|
if GIT_SSH_COMMAND="ssh -i $GITEA_SSH_KEY -p $SSH_PORT -o StrictHostKeyChecking=no" \
|
|
GIT_TERMINAL_PROMPT=0 \
|
|
git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
|
|
git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
|
|
git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
|
|
git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
|
|
log "scripts installed from Gitea ($GITEA_IP)"
|
|
CLONED=true
|
|
else
|
|
log "Gitea fetch failed — falling back to GitHub"
|
|
git -C "$SCRIPTS_DIR" remote remove origin >> "$LOG" 2>&1 || true
|
|
fi
|
|
fi
|
|
|
|
if [[ "$CLONED" == false ]]; then
|
|
log "trying GitHub: $GITHUB"
|
|
git -C "$SCRIPTS_DIR" remote add origin "$GITHUB" >> "$LOG" 2>&1
|
|
if GIT_TERMINAL_PROMPT=0 \
|
|
git -C "$SCRIPTS_DIR" fetch --depth=1 origin "$BRANCH" >> "$LOG" 2>&1; then
|
|
git -C "$SCRIPTS_DIR" reset --hard FETCH_HEAD >> "$LOG" 2>&1
|
|
git -C "$SCRIPTS_DIR" branch -M "$BRANCH" >> "$LOG" 2>&1
|
|
git -C "$SCRIPTS_DIR" branch --set-upstream-to=origin/"$BRANCH" "$BRANCH" >> "$LOG" 2>&1
|
|
log "scripts installed from GitHub"
|
|
CLONED=true
|
|
else
|
|
log "WARNING: both Gitea and GitHub failed — scripts not installed, retry when network is up"
|
|
exit 0
|
|
fi
|
|
fi
|
|
else
|
|
log "repo present — leaving scripts untouched (update from the UI)"
|
|
fi
|
|
|
|
# Seed master.conf from template if absent.
|
|
mkdir -p "$CONF_DIR"
|
|
if [[ ! -f "$CONF_DIR/master.conf" && -f "$DEPLOY_DIR/master.conf.template" ]]; then
|
|
cp "$DEPLOY_DIR/master.conf.template" "$CONF_DIR/master.conf"
|
|
log "seeded master.conf from template"
|
|
fi
|
|
log "install step complete"
|
|
]]>
|
|
</INLINE>
|
|
</FILE>
|
|
|
|
<!--
|
|
── 3. Remove ───────────────────────────────────────────────────────────────────
|
|
Stops background scripts, removes cron, the installed package, and flash config.
|
|
Scripts/conf in appdata are left intact (delete manually for a full wipe).
|
|
────────────────────────────────────────────────────────────────────────────────
|
|
-->
|
|
<FILE Run="/bin/bash" Method="remove">
|
|
<INLINE>
|
|
<![CDATA[
|
|
#!/bin/bash
|
|
PLUGIN="varaverk"
|
|
CFG_DIR="/boot/config/plugins/$PLUGIN"
|
|
CFG_FILE="$CFG_DIR/varaverk.cfg"
|
|
CRON_FILE="$CFG_DIR/varaverk.cron"
|
|
WEB_DIR="/usr/local/emhttp/plugins/$PLUGIN"
|
|
log() { echo "[Varaverk remove] $*"; }
|
|
|
|
[[ -f "$CFG_FILE" ]] && _sd=$(grep -oP '(?<=SCRIPTS_DIR=")[^"]+' "$CFG_FILE" 2>/dev/null)
|
|
SCRIPTS_DIR="${_sd:-$CFG_DIR}"
|
|
|
|
# Stop continuous background scripts.
|
|
if [[ -f "$SCRIPTS_DIR/Fallback/fallback.sh" ]]; then
|
|
bash "$SCRIPTS_DIR/Fallback/fallback.sh" --stop 2>/dev/null && log "fallback.sh stopped" || true
|
|
fi
|
|
pkill -f "run_job.sh" 2>/dev/null || true
|
|
pkill -f "watchdog_orchestrator.sh" 2>/dev/null || true
|
|
|
|
# Remove cron entries.
|
|
if [[ -f "$CRON_FILE" ]]; then
|
|
rm -f "$CRON_FILE"
|
|
/usr/local/sbin/update_cron 2>/dev/null || true
|
|
log "cron removed"
|
|
fi
|
|
rm -f /etc/cron.d/varaverk
|
|
|
|
# Remove the installed package (and its RAM files).
|
|
removepkg "$PLUGIN" 2>/dev/null || true
|
|
[[ -L "$WEB_DIR" ]] && rm -f "$WEB_DIR"
|
|
[[ -d "$WEB_DIR" ]] && rm -rf "$WEB_DIR"
|
|
log "web files removed"
|
|
|
|
# Remove flash config (incl. cached .txz).
|
|
rm -rf "$CFG_DIR"
|
|
log "flash config removed"
|
|
log "done — scripts/conf in $SCRIPTS_DIR preserved (delete manually for full wipe)"
|
|
]]>
|
|
</INLINE>
|
|
</FILE>
|
|
|
|
</PLUGIN>
|