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,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>
|
||||
Reference in New Issue
Block a user