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.
254 lines
7.3 KiB
PHP
254 lines
7.3 KiB
PHP
<?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>
|