Add first-run wizard, host.conf template, fix GitHub URL
Plugin/varaverk.plg: URL updated to github.com/FailedProxy/Varaverk Configurations/host.conf.template: Full host conf structure with HOSTN/hostn placeholders. All personal values blank, all sections documented. Covers identity, rsync, docker, fallback, media, monitors, transcodes, arrs, system watchdog, resource manager. Varaverk.page: checks if HOST1 is blank before rendering tabs. If blank → shows setup wizard, returns early (tabs never render). pages/setup.php: first-run wizard UI. Auto-populates hostname from hostname -s. Role selection: primary (HOST1) or partner (HOST2+). Partner slot selector for HOST3+. api/setup.php: handles wizard form POST. Writes HOST1/HOST2 (and HOST3+) into master.conf preserving all other content. Creates host*.conf from template with HOSTN/hostn replaced and SSH key path pre-filled from hostname convention. Never overwrites an existing host*.conf.
This commit is contained in:
@@ -0,0 +1,457 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ==============================================================================================
|
||||||
|
# ========================== HOSTN CONFIGURATION — (hostname) ==================================
|
||||||
|
# ==============================================================================================
|
||||||
|
# HOSTN-specific variables — credentials, container names, share paths, failover lists.
|
||||||
|
# Sourced after master.conf — values here extend shared profile arrays and add HOSTN-specific
|
||||||
|
# identity, credentials, and container configuration.
|
||||||
|
#
|
||||||
|
# Sparse checkout (git) ensures other hosts never receive this file.
|
||||||
|
#
|
||||||
|
# DO NOT put shared config here — thresholds, toggles, profiles belong in master.conf.
|
||||||
|
# DO NOT put other hosts' variables here — they belong in their own host*.conf files.
|
||||||
|
#
|
||||||
|
# ── HOW TO USE THIS TEMPLATE ──────────────────────────────────────────────────────────────────
|
||||||
|
# This file was generated by the Varaverk first-run wizard.
|
||||||
|
# Fill in the sections that apply to your setup — leave unused sections empty.
|
||||||
|
# All scripts self-guard against empty values — safe to leave sections blank until needed.
|
||||||
|
#
|
||||||
|
# ── INDEX ─────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# ── IDENTITY & CONNECTIVITY ────────────────────────────────────────────────────────────────
|
||||||
|
# IDENTITY hostname, SSH key
|
||||||
|
# EMBY container name, URL, API key
|
||||||
|
# NOTIFICATIONS Discord webhook
|
||||||
|
# PARTNERSHIP auth containers, backup paths
|
||||||
|
#
|
||||||
|
# ── RSYNC ──────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# DAILY SYNC SHARES media shares this host owns and pushes
|
||||||
|
# WEEKLY SYNC SHARES appdata shares synced weekly
|
||||||
|
# CRITICAL SYNC SHARES appdata shares synced every 30 minutes
|
||||||
|
# HOSTN RSYNC PROFILE host-specific appdata sync profile
|
||||||
|
#
|
||||||
|
# ── DOCKER ─────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# DOCKER DAILY RESTART containers restarted daily
|
||||||
|
# DOCKER WEEKLY RESTART containers restarted weekly
|
||||||
|
# DOCKER WATCHDOG memory limits, health URLs, required containers
|
||||||
|
# DOCKER NETWORK CONNECT networks and containers for array start
|
||||||
|
#
|
||||||
|
# ── FALLBACK ───────────────────────────────────────────────────────────────────────────────
|
||||||
|
# DDNS DDNS containers managed by this host
|
||||||
|
# INTERNET LOSS containers stopped when internet is lost
|
||||||
|
# FALLBACK TIERS what this host runs for the remote per tier
|
||||||
|
# TIER DELAYS delays before each tier activates
|
||||||
|
# RSYNC WRITEBACK appdata synced back on handback
|
||||||
|
#
|
||||||
|
# ── MEDIA ──────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# MEDIA PERMISSIONS share list for permissions script
|
||||||
|
# MEDIA CLEANER folder lists for media_cleaner.sh
|
||||||
|
#
|
||||||
|
# ── MONITORS ───────────────────────────────────────────────────────────────────────────────
|
||||||
|
# CERTIFICATE MONITOR domains checked for SSL expiry
|
||||||
|
# SMART HEALTH drives to skip in SMART monitoring
|
||||||
|
# ZFS REPORT pools to exclude from ZFS health report
|
||||||
|
#
|
||||||
|
# ── TRANSCODES ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# TRANSCODES ramdisk size, thresholds, SSD path, server array
|
||||||
|
#
|
||||||
|
# ── ARR STACK ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# DOWNLOADERS slskd, SABnzbd, qBittorrent credentials and URLs
|
||||||
|
# LIDARR / SONARR / RADARR URL, API key, path map
|
||||||
|
# ARR RECOVERY per-arr recovery toggles
|
||||||
|
#
|
||||||
|
# ── SYSTEM WATCHDOG ────────────────────────────────────────────────────────────────────────
|
||||||
|
# SYSTEM WATCHDOG per-host check toggles and NIC configuration
|
||||||
|
#
|
||||||
|
# ── RESOURCE MANAGER ───────────────────────────────────────────────────────────────────────
|
||||||
|
# RESOURCE MANAGER containers paused/stopped under memory pressure
|
||||||
|
#
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── IDENTITY & CONNECTIVITY ───────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Identity ━━━
|
||||||
|
# HOSTN hostname lives in master.conf (not a credential — safe for all servers).
|
||||||
|
# SSH key used for all server-to-server operations — rsync, failover, conf sync.
|
||||||
|
# Convention: /root/.ssh/<hostname-lowercase-no-unraid-prefix>_rsync_automation
|
||||||
|
# Must be in /root/.ssh/ and authorised in the partner's /root/.ssh/authorized_keys.
|
||||||
|
# Run Partnership/ssh_setup.sh to generate the key and copy it to the partner.
|
||||||
|
HOSTN_SSH_KEY="" # e.g. /root/.ssh/myserver_rsync_automation
|
||||||
|
HOSTN_OWNER="" # short identifier for this server (e.g. myserver)
|
||||||
|
HOSTN_OWNER_EMAIL=""
|
||||||
|
|
||||||
|
# ━━━ Unraid API ━━━
|
||||||
|
# Used by the Varaverk plugin to query this server's Unraid GraphQL API.
|
||||||
|
# Generate in Unraid: Settings → Management Access → API Keys → + New Key
|
||||||
|
HOSTN_UNRAID_API_KEY=""
|
||||||
|
|
||||||
|
# ━━━ Emby ━━━
|
||||||
|
HOSTN_EMBY_CONTAINER="Emby"
|
||||||
|
HOSTN_EMBY_URL="http://localhost:8096"
|
||||||
|
HOSTN_EMBY_API_KEY="" # Emby Dashboard → API Keys → + New Key
|
||||||
|
|
||||||
|
# ━━━ Jellyfin ━━━
|
||||||
|
HOSTN_JELLYFIN_CONTAINER="Jellyfin"
|
||||||
|
HOSTN_JELLYFIN_URL="http://localhost:8095"
|
||||||
|
HOSTN_JELLYFIN_API_KEY="" # Jellyfin Dashboard → Administration → API Keys
|
||||||
|
|
||||||
|
# ━━━ Gitea ━━━
|
||||||
|
# Personal access token for gitea_ssh_setup.sh.
|
||||||
|
# Create in Gitea: Settings → Applications → Generate Token → scope: write:user
|
||||||
|
HOSTN_GITEA_API_TOKEN=""
|
||||||
|
|
||||||
|
# ━━━ Notifications ━━━
|
||||||
|
# Discord webhook — leave blank to disable.
|
||||||
|
HOSTN_DISCORD_WEBHOOK=""
|
||||||
|
|
||||||
|
# ━━━ Partnership ━━━
|
||||||
|
|
||||||
|
# Auth containers reconfigured on onboard/offboard.
|
||||||
|
# Format: "ContainerName|WebUIPort"
|
||||||
|
HOSTN_PARTNERSHIP_AUTH_WEBUIS=(
|
||||||
|
# "NginxProxyManager|81"
|
||||||
|
# "Authelia|9091"
|
||||||
|
)
|
||||||
|
|
||||||
|
# XML templates pushed to mirror during onboard — auth stack.
|
||||||
|
# Dependencies (databases) must come before apps that depend on them.
|
||||||
|
HOSTN_PARTNERSHIP_AUTH_STACK=(
|
||||||
|
# "my-Authelia.xml"
|
||||||
|
# "my-NginxProxyManager.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
# XML templates pushed to mirror during onboard — arr stack.
|
||||||
|
HOSTN_PARTNERSHIP_ARR_STACK=(
|
||||||
|
# "my-Sonarr.xml"
|
||||||
|
# "my-Radarr.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Paths the partner should collect during the grace window after offboard.
|
||||||
|
HOSTN_PARTNERSHIP_MIRROR_BACKUPS=(
|
||||||
|
# "/mnt/user/appdata-Fallback/Partner-Emby"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Containers parked on this server when partnership is active.
|
||||||
|
HOSTN_PARTNERSHIP_OWN_CONTAINERS=(
|
||||||
|
# "Emby"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Emby admin provisioning — owner controls whether Emby is shared.
|
||||||
|
HOSTN_PARTNERSHIP_PROVISION_EMBY_ADMIN=false
|
||||||
|
HOSTN_PARTNERSHIP_EMBY_PORT=8096
|
||||||
|
HOSTN_PARTNERSHIP_EMBY_ADMIN_USER=""
|
||||||
|
HOSTN_PARTNERSHIP_EMBY_ADMIN_PASS=""
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── RSYNC ─────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Daily Sync Shares ━━━
|
||||||
|
# Media shares this host pushes to all other nodes every night.
|
||||||
|
# Uses DEFAULT_RSYNC_OPTS from master.conf — no profile needed.
|
||||||
|
HOSTN_DAILY_SYNC_SHARES=(
|
||||||
|
# /mnt/user/Movies
|
||||||
|
# /mnt/user/Tv_Shows
|
||||||
|
# /mnt/user/Music
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Weekly Sync Shares ━━━
|
||||||
|
# Appdata shares synced during the weekly maintenance window.
|
||||||
|
# Profiles (emby, critical-data) drive container stops — define in master.conf.
|
||||||
|
HOSTN_WEEKLY_SYNC_SHARES=(
|
||||||
|
# "/mnt/user/Media_Server/Emby" # emby profile
|
||||||
|
# "/mnt/user/appdata-Fallback/Critical-Data" # critical-data profile
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Intermediate Sync Shares ━━━
|
||||||
|
# Shares synced every 4 hours. Leave empty to skip mid-day rsync.
|
||||||
|
HOSTN_INTERMEDIATE_SYNC_SHARES=(
|
||||||
|
# Add shares here to enable mid-day rsync
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Critical Sync Shares ━━━
|
||||||
|
# Appdata shares synced every 30 minutes.
|
||||||
|
# Format: "/path/to/share" or "/path/to/share|profile-name"
|
||||||
|
HOSTN_CRITICAL_SYNC_SHARES=(
|
||||||
|
# "/mnt/user/appdata-Fallback/Critical-Data|critical-fallback"
|
||||||
|
# "/mnt/user/Media_Server/Emby|emby-fallback"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Backup Verify ━━━
|
||||||
|
# Leave empty to use HOSTN_DAILY_SYNC_SHARES automatically.
|
||||||
|
HOSTN_BACKUP_VERIFY_SHARES=(
|
||||||
|
# leave empty to use HOSTN_DAILY_SYNC_SHARES automatically
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ HOSTN Rsync Profile — hostn-appdata ━━━
|
||||||
|
# Host-specific appdata sync profile.
|
||||||
|
PROFILE_RSYNC_OPTS[hostn-appdata]="-av --info=progress2 --bwlimit=${PROFILE_BW_LIMIT[hostn-appdata]:-8000}"
|
||||||
|
PROFILE_BW_LIMIT[hostn-appdata]=8000
|
||||||
|
PROFILE_RETRY_COUNT[hostn-appdata]=3
|
||||||
|
PROFILE_SLEEP[hostn-appdata]=300
|
||||||
|
PROFILE_CRITICAL_CONTAINER_NAMES[hostn-appdata]=""
|
||||||
|
PROFILE_DELAYED_CONTAINERS[hostn-appdata]=""
|
||||||
|
PROFILE_CONTAINER_DELAY[hostn-appdata]=5
|
||||||
|
PROFILE_EXCLUDE_DIRS[hostn-appdata]="logs *.tmp"
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── DOCKER ────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Docker Daily Restart ━━━
|
||||||
|
HOSTN_DAILY_RESTART_CONTAINERS=(
|
||||||
|
# "NginxProxyManager"
|
||||||
|
# "Authelia"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Docker Weekly Restart ━━━
|
||||||
|
HOSTN_WEEKLY_RESTART_CONTAINERS=(
|
||||||
|
# "NextCloud"
|
||||||
|
# "AdGuard-Home"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Docker Watchdog ━━━
|
||||||
|
|
||||||
|
# Memory hard limits in MB — immediate restart if exceeded.
|
||||||
|
# 20GB=20480 16GB=16384 8GB=8192 4GB=4096 2GB=2048 1GB=1024
|
||||||
|
declare -A HOSTN_WATCHDOG_CONTAINERS=(
|
||||||
|
# ["Emby"]=18432
|
||||||
|
)
|
||||||
|
|
||||||
|
# HTTP health check URLs — checked every cycle.
|
||||||
|
declare -A HOSTN_WATCHDOG_CONTAINER_URLS=(
|
||||||
|
# ["Emby"]="http://localhost:8096"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Required containers — must always be running.
|
||||||
|
HOSTN_WATCHDOG_REQUIRED_CONTAINERS=(
|
||||||
|
# "NginxProxyManager"
|
||||||
|
# "Authelia"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Containers to skip in Tier 2 global scan.
|
||||||
|
HOSTN_WATCHDOG_SCAN_IGNORE=(
|
||||||
|
# "my-occasional-container"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dependency ordering — skip restarting a container if its dependency is also down.
|
||||||
|
declare -A HOSTN_WATCHDOG_DEPENDENCIES=(
|
||||||
|
# ["Authelia"]="Mariadb Redis-Authelia"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Per-container appdata growth suppress ceilings in MB.
|
||||||
|
declare -A HOSTN_WATCHDOG_APPDATA_SIZES=(
|
||||||
|
# ["Tdarr"]="25600"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Network Watchdog ━━━
|
||||||
|
HOSTN_NETWORK_WATCHDOG_DDNS_DOMAIN="" # e.g. myserver.com
|
||||||
|
HOSTN_NETWORK_WATCHDOG_DDNS_CONTAINER="" # e.g. MyServer.com
|
||||||
|
HOSTN_NETWORK_WATCHDOG_NPM_URL="" # e.g. https://myserver.com
|
||||||
|
|
||||||
|
# ━━━ Docker Network Connect ━━━
|
||||||
|
HOSTN_NETWORK_CONNECT_CONTAINERS=(
|
||||||
|
# "memcached"
|
||||||
|
)
|
||||||
|
|
||||||
|
HOSTN_NETWORK_CONNECT_NETWORKS=(
|
||||||
|
# "high-availability"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── FALLBACK ──────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ DDNS ━━━
|
||||||
|
# DDNS containers this host manages.
|
||||||
|
HOSTN_DDNS_CONTAINERS=(
|
||||||
|
# "MyServer.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Internet Loss ━━━
|
||||||
|
# Containers stopped immediately when internet is lost.
|
||||||
|
FALLBACK_HOSTN_STOP_ON_NO_NET=(
|
||||||
|
# "MyServer.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Fallback Tiers — HOSTN Runs for Partner ━━━
|
||||||
|
# Containers this host starts when the partner goes down.
|
||||||
|
# Replace REMOTE_ID below with the actual remote host ID (HOST1, HOST2, etc.)
|
||||||
|
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER1=(
|
||||||
|
# "Partner-DDNS-Container"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER2=(
|
||||||
|
# "container-placeholder"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER3=(
|
||||||
|
# "container-placeholder"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_COVERS_REMOTE_ID_TIER4=(
|
||||||
|
# "container-placeholder"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Tier Delays — Partner's Containers on this Host ━━━
|
||||||
|
# How long the partner must be down before each tier activates here — in minutes.
|
||||||
|
# Replace REMOTE_ID with the actual remote host ID (HOST1, HOST2, etc.)
|
||||||
|
REMOTE_ID_TIER2_DELAY=240 # 4 hours
|
||||||
|
REMOTE_ID_TIER3_DELAY=720 # 12 hours
|
||||||
|
REMOTE_ID_TIER4_DELAY=1440 # 24 hours
|
||||||
|
|
||||||
|
# ━━━ Rsync Writeback ━━━
|
||||||
|
HOSTN_TIER1_WRITEBACK_DELAY=60 # skip Emby writeback if outage under 1hr
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_WRITEBACK_TIER1=(
|
||||||
|
# "/mnt/user/Media_Server/Emby"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_WRITEBACK_TIER2=(
|
||||||
|
# "/mnt/user/appdata-Fallback/Important-Data"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_WRITEBACK_TIER3=(
|
||||||
|
# "location-placeholder"
|
||||||
|
)
|
||||||
|
|
||||||
|
FALLBACK_HOSTN_WRITEBACK_TIER4=(
|
||||||
|
# "/mnt/user/appdata-Fallback/Arrs_Stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── MEDIA ─────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Media Permissions ━━━
|
||||||
|
HOSTN_MEDIA_PERMISSION_SHARES=(
|
||||||
|
# /mnt/user/Movies
|
||||||
|
# /mnt/user/Tv_Shows
|
||||||
|
# /mnt/user/Music
|
||||||
|
# /mnt/user/Downloads
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Media Cleaner ━━━
|
||||||
|
HOSTN_ANIME_CLEAN_FOLDERS=(
|
||||||
|
# /mnt/user/Anime_Movies
|
||||||
|
# /mnt/user/Anime_Shows
|
||||||
|
)
|
||||||
|
|
||||||
|
HOSTN_MEDIA_CLEAN_FOLDERS=(
|
||||||
|
# /mnt/user/Movies
|
||||||
|
# /mnt/user/Tv_Shows
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── MONITORS ──────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Certificate Monitor ━━━
|
||||||
|
HOSTN_CERT_MONITOR_DOMAINS=(
|
||||||
|
# "myserver.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ SMART Health ━━━
|
||||||
|
HOSTN_SMART_IGNORE_DRIVES=(
|
||||||
|
"sda" # boot USB — SMART not meaningful on flash drives
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ ZFS Report ━━━
|
||||||
|
HOSTN_ZFS_REPORT_IGNORE_POOLS=(
|
||||||
|
# "disk5"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── TRANSCODES ────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
HOSTN_RAMDISK_SIZE="10G"
|
||||||
|
HOSTN_RAMDISK_WARN_GB=8.5
|
||||||
|
HOSTN_RAMDISK_LOW_GB=7
|
||||||
|
HOSTN_TRANSCODE_SSD="/mnt/cache/Temp_Storage/Emby/Transcodes/"
|
||||||
|
|
||||||
|
HOSTN_TRANSCODE_SERVERS=(
|
||||||
|
"${HOSTN_EMBY_CONTAINER}|${HOSTN_EMBY_URL}|${HOSTN_EMBY_API_KEY}|emby"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── ARR STACK ─────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
# ━━━ Downloaders ━━━
|
||||||
|
HOSTN_SLSKD_URL="http://localhost:8980"
|
||||||
|
HOSTN_SLSKD_API_KEY=""
|
||||||
|
HOSTN_SLSKD_FAILED_IMPORTS_DIR=""
|
||||||
|
|
||||||
|
HOSTN_SABNZBD_URL="http://localhost:8180"
|
||||||
|
HOSTN_SABNZBD_API_KEY=""
|
||||||
|
|
||||||
|
HOSTN_QBIT_URL="http://localhost:8080"
|
||||||
|
HOSTN_QBIT_USERNAME="admin"
|
||||||
|
HOSTN_QBIT_PASSWORD=""
|
||||||
|
|
||||||
|
# ━━━ Lidarr ━━━
|
||||||
|
HOSTN_LIDARR_URL="http://localhost:8686"
|
||||||
|
HOSTN_LIDARR_API_KEY=""
|
||||||
|
HOSTN_LIDARR_MUSIC_ROOT="/mnt/user/Music"
|
||||||
|
HOSTN_FANART_API_KEY=""
|
||||||
|
HOSTN_LASTFM_API_KEY=""
|
||||||
|
|
||||||
|
declare -A HOSTN_LIDARR_PATH_MAP=(
|
||||||
|
# ["/music"]="/mnt/user/Music"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Sonarr ━━━
|
||||||
|
HOSTN_SONARR_URL="http://localhost:8989"
|
||||||
|
HOSTN_SONARR_API_KEY=""
|
||||||
|
HOSTN_SONARR_TV_ROOT="/mnt/user/Tv_Shows"
|
||||||
|
|
||||||
|
declare -A HOSTN_SONARR_PATH_MAP=(
|
||||||
|
# ["/tv"]="/mnt/user/Tv_Shows"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Radarr ━━━
|
||||||
|
HOSTN_RADARR_URL="http://localhost:7878"
|
||||||
|
HOSTN_RADARR_API_KEY=""
|
||||||
|
HOSTN_RADARR_MOVIE_ROOT="/mnt/user/Movies"
|
||||||
|
|
||||||
|
declare -A HOSTN_RADARR_PATH_MAP=(
|
||||||
|
# ["/movies"]="/mnt/user/Movies"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ━━━ Arr Recovery Toggles ━━━
|
||||||
|
HOSTN_LIDARR_RECOVERY=false
|
||||||
|
HOSTN_SONARR_RECOVERY=true
|
||||||
|
HOSTN_RADARR_RECOVERY=true
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── SYSTEM WATCHDOG ───────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_DOCKER=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_ROOTFS=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_RAM=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_LOAD=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_TEMP=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_ZOMBIES=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_LOG=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_TMP=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_FD=true
|
||||||
|
HOSTN_SYS_WATCHDOG_CHECK_RUNAWAY=true
|
||||||
|
|
||||||
|
HOSTN_SYS_WATCHDOG_NIC="" # e.g. eth0 — for network monitoring
|
||||||
|
|
||||||
|
# ==============================================================================================
|
||||||
|
# ── RESOURCE MANAGER ──────────────────────────────────────────────────────────────────────────
|
||||||
|
# ==============================================================================================
|
||||||
|
|
||||||
|
HOSTN_RW_PAUSE_CONTAINERS=(
|
||||||
|
# "Tdarr"
|
||||||
|
# "LidaTube"
|
||||||
|
)
|
||||||
|
|
||||||
|
HOSTN_RW_STOP_CONTAINERS=(
|
||||||
|
# "Tdarr"
|
||||||
|
)
|
||||||
@@ -7,6 +7,17 @@ $plugin = 'varaverk';
|
|||||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||||||
$pluginDir = "$docroot/plugins/$plugin";
|
$pluginDir = "$docroot/plugins/$plugin";
|
||||||
|
|
||||||
|
require_once "$pluginDir/include/config.php";
|
||||||
|
|
||||||
|
// First-run check — show setup wizard if HOST1 is not configured
|
||||||
|
$_master = vv_read_conf_raw('master.conf');
|
||||||
|
preg_match('/^\s*HOST1\s*=\s*"([^"]*)"/m', $_master, $_h1m);
|
||||||
|
if (empty(trim($_h1m[1] ?? ''))) {
|
||||||
|
include "$pluginDir/pages/setup.php";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unset($_master, $_h1m);
|
||||||
|
|
||||||
// Determine active tab
|
// Determine active tab
|
||||||
$tab = $_GET['tab'] ?? 'monitor';
|
$tab = $_GET['tab'] ?? 'monitor';
|
||||||
$validTabs = ['monitor', 'scheduler', 'docker', 'watchdog', 'partnership', 'fallback', 'arrs'];
|
$validTabs = ['monitor', 'scheduler', 'docker', 'watchdog', 'partnership', 'fallback', 'arrs'];
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once dirname(__DIR__) . '/include/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Method not allowed']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host1 = trim($_POST['host1'] ?? '');
|
||||||
|
$host2 = trim($_POST['host2'] ?? '');
|
||||||
|
$mySlot = trim($_POST['my_slot'] ?? 'host1'); // 'host1', 'host2', 'host3', ...
|
||||||
|
$myHostname = trim($_POST['my_hostname'] ?? '');
|
||||||
|
|
||||||
|
if (empty($host1)) {
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'HOST1 hostname is required']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate slot — must be host\d+
|
||||||
|
if (!preg_match('/^host\d+$/', $mySlot)) {
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Invalid slot']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Write HOST1 / HOST2 into master.conf ─────────────────────────────────────────────────────
|
||||||
|
$master = vv_read_conf_raw('master.conf');
|
||||||
|
if ($master === '') {
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'master.conf not found — check SCRIPTS_DIR in varaverk.cfg']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace HOST1 and HOST2 lines (preserving indentation)
|
||||||
|
$master = preg_replace(
|
||||||
|
'/^(\s*HOST1\s*=\s*).*$/m',
|
||||||
|
'${1}"' . addslashes($host1) . '"',
|
||||||
|
$master
|
||||||
|
);
|
||||||
|
$master = preg_replace(
|
||||||
|
'/^(\s*HOST2\s*=\s*).*$/m',
|
||||||
|
'${1}"' . addslashes($host2) . '"',
|
||||||
|
$master
|
||||||
|
);
|
||||||
|
|
||||||
|
// For partner slots beyond HOST2 — ensure the slot line exists in master.conf
|
||||||
|
$slotNum = (int) preg_replace('/\D/', '', $mySlot);
|
||||||
|
if ($slotNum > 2 && !empty($myHostname)) {
|
||||||
|
$hostKey = 'HOST' . $slotNum;
|
||||||
|
if (!preg_match('/^\s*' . $hostKey . '\s*=/m', $master)) {
|
||||||
|
// Append after HOST2 line
|
||||||
|
$master = preg_replace(
|
||||||
|
'/^(\s*HOST2\s*=.*$)/m',
|
||||||
|
'$1' . "\n {$hostKey}=\"" . addslashes($myHostname) . '"',
|
||||||
|
$master
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$master = preg_replace(
|
||||||
|
'/^(\s*' . $hostKey . '\s*=\s*).*$/m',
|
||||||
|
'${1}"' . addslashes($myHostname) . '"',
|
||||||
|
$master
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vv_write_conf_raw('master.conf', $master)) {
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'Failed to write master.conf']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Create host*.conf from template ──────────────────────────────────────────────────────────
|
||||||
|
$hostId = strtoupper($mySlot); // HOST1, HOST2, HOST3 ...
|
||||||
|
$hostIdLow = strtolower($mySlot); // host1, host2, host3 ...
|
||||||
|
$confFile = $hostIdLow . '.conf';
|
||||||
|
|
||||||
|
// Only create if it doesn't already exist — never overwrite an existing conf
|
||||||
|
if (!file_exists(CONF_DIR . '/' . $confFile)) {
|
||||||
|
$template = @file_get_contents(CONF_DIR . '/host.conf.template');
|
||||||
|
if ($template === false) {
|
||||||
|
$template = @file_get_contents(SCRIPTS_DIR . '/Configurations/host.conf.template');
|
||||||
|
}
|
||||||
|
if ($template !== false) {
|
||||||
|
// Derive SSH key path from hostname convention
|
||||||
|
// unRAID-MyServer → /root/.ssh/myserver_rsync_automation
|
||||||
|
$sshOwner = strtolower(preg_replace('/^unraid-/i', '', $myHostname));
|
||||||
|
$sshKeyPath = '/root/.ssh/' . $sshOwner . '_rsync_automation';
|
||||||
|
|
||||||
|
$conf = str_replace('HOSTN', $hostId, $template);
|
||||||
|
$conf = str_replace('hostn', $hostIdLow, $conf);
|
||||||
|
// Pre-fill the SSH key path
|
||||||
|
$conf = preg_replace(
|
||||||
|
'/^(\s*' . $hostId . '_SSH_KEY\s*=\s*)""/m',
|
||||||
|
'${1}"' . $sshKeyPath . '"',
|
||||||
|
$conf
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!vv_write_conf_raw($confFile, $conf)) {
|
||||||
|
echo json_encode(['ok' => false, 'error' => "Failed to write $confFile"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Template missing is non-fatal — user can create the conf manually
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true, 'host_id' => $hostId, 'conf_file' => $confFile]);
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
<?php
|
||||||
|
// First-run setup wizard — shown when HOST1 is blank in master.conf.
|
||||||
|
$detectedHostname = trim(shell_exec('hostname -s') ?: '');
|
||||||
|
?>
|
||||||
|
<link rel="stylesheet" href="/plugins/varaverk/css/varaverk.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#vv-setup {
|
||||||
|
max-width: 560px;
|
||||||
|
margin: 48px auto 0;
|
||||||
|
background: #141414;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 36px 40px 40px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
#vv-setup h1 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-weight: normal;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-sub {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-field {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
#vv-setup label {
|
||||||
|
display: block;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .06em;
|
||||||
|
}
|
||||||
|
#vv-setup input[type=text] {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #0d0d0d;
|
||||||
|
border: 1px solid #333;
|
||||||
|
color: #ddd;
|
||||||
|
padding: 7px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#vv-setup input[type=text]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #555;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #555;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-role {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-role-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 0;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #888;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
transition: border-color .15s, color .15s;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-role-btn.active {
|
||||||
|
border-color: #555;
|
||||||
|
color: #ccc;
|
||||||
|
background: #222;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-conditional {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-conditional.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#vv-setup .vv-setup-divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid #222;
|
||||||
|
margin: 24px 0;
|
||||||
|
}
|
||||||
|
#vv-setup-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 1px solid #444;
|
||||||
|
color: #ccc;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
letter-spacing: .03em;
|
||||||
|
}
|
||||||
|
#vv-setup-btn:hover { border-color: #666; color: #eee; }
|
||||||
|
#vv-setup-btn:disabled { opacity: .45; cursor: default; }
|
||||||
|
#vv-setup-status {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 16px;
|
||||||
|
}
|
||||||
|
#vv-setup-status.ok { color: #4a8; }
|
||||||
|
#vv-setup-status.err { color: #a44; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="vv-setup">
|
||||||
|
<h1>⬡ Varaverk — First Run</h1>
|
||||||
|
<div class="vv-setup-sub">Set up your server identity before the plugin can start.</div>
|
||||||
|
|
||||||
|
<div class="vv-setup-field">
|
||||||
|
<label>This server's hostname</label>
|
||||||
|
<input type="text" id="vv-hostname" value="<?= htmlspecialchars($detectedHostname) ?>"
|
||||||
|
placeholder="unRAID-MyServer" autocomplete="off" spellcheck="false">
|
||||||
|
<div class="vv-setup-hint">Must match Settings → Identification exactly (case-sensitive)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="vv-setup-divider">
|
||||||
|
|
||||||
|
<label style="margin-bottom:10px;display:block;">Server role</label>
|
||||||
|
<div class="vv-setup-role">
|
||||||
|
<div class="vv-setup-role-btn active" id="vv-role-primary" onclick="vvSetRole('primary')">
|
||||||
|
Primary<br><span style="color:#555;font-size:10px;">HOST1 · first to be set up</span>
|
||||||
|
</div>
|
||||||
|
<div class="vv-setup-role-btn" id="vv-role-partner" onclick="vvSetRole('partner')">
|
||||||
|
Partner<br><span style="color:#555;font-size:10px;">HOST2+ · joining an existing primary</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vv-setup-conditional" id="vv-cond-primary">
|
||||||
|
<div class="vv-setup-field">
|
||||||
|
<label>Partner's hostname <span style="color:#444">(optional — can fill in later)</span></label>
|
||||||
|
<input type="text" id="vv-partner-hostname" value=""
|
||||||
|
placeholder="unRAID-PartnerServer" autocomplete="off" spellcheck="false">
|
||||||
|
<div class="vv-setup-hint">Leave blank if setting up standalone or partner isn't ready yet</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vv-setup-conditional" id="vv-cond-partner">
|
||||||
|
<div class="vv-setup-field">
|
||||||
|
<label>Primary server's hostname <span style="color:#a44">*required</span></label>
|
||||||
|
<input type="text" id="vv-primary-hostname" value=""
|
||||||
|
placeholder="unRAID-PrimaryServer" autocomplete="off" spellcheck="false">
|
||||||
|
</div>
|
||||||
|
<div class="vv-setup-field">
|
||||||
|
<label>Your slot</label>
|
||||||
|
<select id="vv-partner-slot" style="width:100%;box-sizing:border-box;background:#0d0d0d;
|
||||||
|
border:1px solid #333;color:#ddd;padding:7px 10px;border-radius:3px;
|
||||||
|
font-family:monospace;font-size:13px;">
|
||||||
|
<option value="host2">HOST2</option>
|
||||||
|
<option value="host3">HOST3</option>
|
||||||
|
<option value="host4">HOST4</option>
|
||||||
|
</select>
|
||||||
|
<div class="vv-setup-hint">Which slot are you? Ask your primary server admin if unsure — usually HOST2.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="vv-setup-btn" onclick="vvDoSetup()">Save and continue →</button>
|
||||||
|
<div id="vv-setup-status"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let vvRole = 'primary';
|
||||||
|
|
||||||
|
function vvSetRole(role) {
|
||||||
|
vvRole = role;
|
||||||
|
document.getElementById('vv-role-primary').classList.toggle('active', role === 'primary');
|
||||||
|
document.getElementById('vv-role-partner').classList.toggle('active', role === 'partner');
|
||||||
|
document.getElementById('vv-cond-primary').classList.toggle('visible', role === 'primary');
|
||||||
|
document.getElementById('vv-cond-partner').classList.toggle('visible', role === 'partner');
|
||||||
|
}
|
||||||
|
|
||||||
|
function vvDoSetup() {
|
||||||
|
const hostname = document.getElementById('vv-hostname').value.trim();
|
||||||
|
const status = document.getElementById('vv-setup-status');
|
||||||
|
const btn = document.getElementById('vv-setup-btn');
|
||||||
|
|
||||||
|
if (!hostname) {
|
||||||
|
status.textContent = '✗ Hostname is required';
|
||||||
|
status.className = 'err';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let host1 = '', host2 = '', mySlot = 'host1';
|
||||||
|
|
||||||
|
if (vvRole === 'primary') {
|
||||||
|
host1 = hostname;
|
||||||
|
host2 = document.getElementById('vv-partner-hostname').value.trim();
|
||||||
|
mySlot = 'host1';
|
||||||
|
} else {
|
||||||
|
const primary = document.getElementById('vv-primary-hostname').value.trim();
|
||||||
|
if (!primary) {
|
||||||
|
status.textContent = '✗ Primary hostname is required for partner setup';
|
||||||
|
status.className = 'err';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mySlot = document.getElementById('vv-partner-slot').value;
|
||||||
|
host1 = primary;
|
||||||
|
// Place this server in the correct slot
|
||||||
|
if (mySlot === 'host2') host2 = hostname;
|
||||||
|
// host3/host4 handled server-side
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = 'Saving…';
|
||||||
|
status.textContent = '';
|
||||||
|
status.className = '';
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
csrf_token: typeof csrf_token !== 'undefined' ? csrf_token : '',
|
||||||
|
host1, host2, my_slot: mySlot,
|
||||||
|
my_hostname: hostname,
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch('/plugins/varaverk/api/setup.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
body: params,
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (d.ok) {
|
||||||
|
status.textContent = '✓ Saved — loading plugin…';
|
||||||
|
status.className = 'ok';
|
||||||
|
setTimeout(() => { window.location.href = '?tab=monitor'; }, 800);
|
||||||
|
} else {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = 'Save and continue →';
|
||||||
|
status.textContent = '✗ ' + (d.error ?? 'Unknown error');
|
||||||
|
status.className = 'err';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = 'Save and continue →';
|
||||||
|
status.textContent = '✗ Request failed';
|
||||||
|
status.className = 'err';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
<!ENTITY author "gmer4lfe">
|
<!ENTITY author "gmer4lfe">
|
||||||
<!ENTITY version "2026.05.28">
|
<!ENTITY version "2026.05.28">
|
||||||
<!ENTITY launch "varaverk/monitor">
|
<!ENTITY launch "varaverk/monitor">
|
||||||
<!ENTITY github "https://github.com/gmer4lfe/Varaverk">
|
<!ENTITY github "https://github.com/FailedProxy/Varaverk">
|
||||||
<!ENTITY branch "main">
|
<!ENTITY branch "main">
|
||||||
<!ENTITY cfgdir "/boot/config/plugins/varaverk">
|
<!ENTITY cfgdir "/boot/config/plugins/varaverk">
|
||||||
<!ENTITY plugdir "/usr/local/emhttp/plugins/varaverk">
|
<!ENTITY plugdir "/usr/local/emhttp/plugins/varaverk">
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<![CDATA[
|
<![CDATA[
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
PLUGIN="varaverk"
|
PLUGIN="varaverk"
|
||||||
GITHUB="https://github.com/gmer4lfe/Varaverk"
|
GITHUB="https://github.com/FailedProxy/Varaverk"
|
||||||
BRANCH="main"
|
BRANCH="main"
|
||||||
CFG_DIR="/boot/config/plugins/$PLUGIN"
|
CFG_DIR="/boot/config/plugins/$PLUGIN"
|
||||||
CFG_FILE="$CFG_DIR/varaverk.cfg"
|
CFG_FILE="$CFG_DIR/varaverk.cfg"
|
||||||
|
|||||||
Reference in New Issue
Block a user