Put the join inside the wizard, send a partner to Partnership when it finishes, and say what is left
This commit is contained in:
@@ -51,7 +51,8 @@ $detectedHostname = vv_get_hostname();
|
||||
// Matching is exact and case-insensitive, never a prefix: two hosts called Tower and Tower2 must
|
||||
// not resolve to each other, and there is no ambiguity to tolerate when the value was written by
|
||||
// the very host it names.
|
||||
$vvDetected = ['slot' => '', 'primary' => '', 'role' => '', 'networks' => [], 'networks_src' => ''];
|
||||
$vvDetected = ['slot' => '', 'primary' => '', 'role' => '', 'phase' => 0,
|
||||
'networks' => [], 'networks_src' => ''];
|
||||
$vvMasterRaw = vv_read_conf_raw('master.conf');
|
||||
if ($vvMasterRaw !== '' && $detectedHostname !== '') {
|
||||
preg_match_all('/^\s*(HOST\d+)\s*=\s*"([^"]*)"/m', $vvMasterRaw, $vvHm, PREG_SET_ORDER);
|
||||
@@ -83,6 +84,17 @@ if ($vvMasterRaw !== '' && $detectedHostname !== '') {
|
||||
//
|
||||
// Adopting the owner's list also means docker_network_connect.sh will recreate the network here
|
||||
// after an Unraid update wipes it, which is the whole reason that script exists.
|
||||
// How far this host's own onboarding has got, so the inline join panel can show the SSH step or
|
||||
// the join button rather than both. Same flags api/checklist.php reads, and the same two
|
||||
// spellings, because the state file has been written by both bash and PHP over its life.
|
||||
if ($vvDetected['slot'] !== '') {
|
||||
$vvSt = vv_setup_state_read();
|
||||
$vvSlUp = strtoupper($vvDetected['slot']);
|
||||
$vvDetected['phase'] =
|
||||
(!empty($vvSt[$vvSlUp . '_PHASE2_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase2_done'])) ? 2
|
||||
: ((!empty($vvSt[$vvSlUp . '_PHASE1_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase1_done'])) ? 1 : 0);
|
||||
}
|
||||
|
||||
if ($vvDetected['role'] === 'partner' && $vvDetected['slot'] !== '') {
|
||||
$vvOwnerConf = VV_CONF_RAM_CACHE_DIR . '/host1.conf';
|
||||
if (is_readable($vvOwnerConf)) {
|
||||
@@ -265,7 +277,7 @@ hr.vv-hr { border: none; border-top: 1px solid #1e1e1e; margin: 22px 0; }
|
||||
<button id="vv-key-btn" onclick="vvCreateKey(this)" class="vv-btn" style="flex:1;background:#1a3a1a;border-color:#2e6b2e;color:#6fcf97;">
|
||||
Create API Key
|
||||
</button>
|
||||
<a href="#" onclick="vvGoScheduler(event)" style="font-size:11px;color:#444;text-decoration:none;white-space:nowrap;">Skip →</a>
|
||||
<a href="#" onclick="vvGoNext(event)" style="font-size:11px;color:#444;text-decoration:none;white-space:nowrap;">Skip →</a>
|
||||
</div>
|
||||
<div id="vv-key-status" style="font-size:12px;min-height:14px;margin-bottom:18px;"></div>
|
||||
|
||||
@@ -273,8 +285,11 @@ hr.vv-hr { border: none; border-top: 1px solid #1e1e1e; margin: 22px 0; }
|
||||
<div class="vv-cl-title">Setup checklist</div>
|
||||
<div id="vv-checklist"><div style="font-size:12px;color:#444;">Loading…</div></div>
|
||||
|
||||
<div id="vv-onboard-panel"></div>
|
||||
<div id="vv-done-banner"></div>
|
||||
|
||||
<div style="margin-top:18px;text-align:right;">
|
||||
<a href="#" onclick="vvGoScheduler(event)" style="font-size:12px;color:#444;text-decoration:none;">Go to Scheduler →</a>
|
||||
<a href="#" id="vv-exit-link" onclick="vvGoNext(event)" style="font-size:12px;color:#444;text-decoration:none;">Continue →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -385,11 +400,22 @@ function vvSetBtn(text, disabled) {
|
||||
const b = document.getElementById('vv-main-btn');
|
||||
if (b) { b.textContent = text; b.disabled = disabled; }
|
||||
}
|
||||
function vvGoScheduler(e) {
|
||||
// Named for what it does rather than where it goes — the destination is now the server's
|
||||
// decision (vv_setup_redirect), because a partner and a primary have different next steps.
|
||||
function vvGoNext(e) {
|
||||
if (e) e.preventDefault();
|
||||
window.location.href = _vvRedirect || '?tab=scheduler';
|
||||
}
|
||||
|
||||
// Label the exit after the place it actually leads, so the link is not a surprise. Derived from
|
||||
// the redirect the save returned, not from the role guessed again on this side.
|
||||
function vvLabelExit() {
|
||||
const el = document.getElementById('vv-exit-link');
|
||||
if (!el) return;
|
||||
const target = /tab=partnership/.test(_vvRedirect || '') ? 'Partnership' : 'Scheduler';
|
||||
el.textContent = `Go to ${target} →`;
|
||||
}
|
||||
|
||||
// ── Step 2 ────────────────────────────────────────────────────────────────────
|
||||
function vvShowStep2(redirect, apiKey) {
|
||||
_vvRedirect = redirect || '?tab=scheduler';
|
||||
@@ -414,9 +440,16 @@ function vvRunPopulate() {
|
||||
}).then(r => r.json()).then(d => {
|
||||
if (d.ok) {
|
||||
const found = (d.lines || []).filter(l => /✅|found|detected/i.test(l));
|
||||
// Finding nothing is the expected result on a partner, not a failure: this runs during
|
||||
// first-run setup, which on a fresh mirror is before the owner has deployed anything to
|
||||
// find. Onboard Step 11 re-runs discovery once the stacks exist. Saying so beats a line
|
||||
// that reads like something went wrong on the one screen with no context to judge it.
|
||||
el.textContent = found.length
|
||||
? '✓ Auto-populate: ' + found.length + ' field' + (found.length > 1 ? 's' : '') + ' detected'
|
||||
: '✓ Auto-populate ran — arr keys will fill once services are running';
|
||||
: (vvDetectedIdentity?.role === 'partner'
|
||||
? '✓ Auto-populate ran — nothing to find yet, which is normal here. It runs again '
|
||||
+ 'automatically once ' + (vvDetectedIdentity.primary || 'the primary') + ' deploys your stack'
|
||||
: '✓ Auto-populate ran — arr keys will fill once services are running');
|
||||
el.style.color = '#4a8';
|
||||
} else {
|
||||
el.textContent = 'Auto-populate skipped — run Deployment/conf_populate.sh once your arr containers are up';
|
||||
@@ -466,11 +499,73 @@ function vvDeferItem(btn, id, defer) {
|
||||
});
|
||||
}
|
||||
|
||||
// The join, inline, for a partner — the same panel the Partnership tab renders, from the same
|
||||
// function in Varaverk.page. The wizard used to end by pointing at another tab for the one task
|
||||
// a fresh mirror still had to do; the SSH step in particular is the only genuinely blocking part
|
||||
// of onboarding and it was the part you had to go looking for.
|
||||
//
|
||||
// Only while it is still outstanding: once the mirror has joined, the partnership row in the
|
||||
// checklist above says so and a second copy of the panel is noise.
|
||||
function vvRenderOnboardPanel(d) {
|
||||
const el = document.getElementById('vv-onboard-panel');
|
||||
if (!el) return;
|
||||
const partner = vvDetectedIdentity?.role === 'partner';
|
||||
const pt = (d.items || []).find(i => i.id === 'partnership');
|
||||
if (!partner || !pt || pt.ok) { el.innerHTML = ''; return; }
|
||||
|
||||
// Same derivation the Partnership tab uses: the command runs on THIS machine, so it must carry
|
||||
// this machine's SCRIPTS_DIR, and the terminal link must be this machine's web terminal.
|
||||
el.innerHTML = '<hr class="vv-hr">'
|
||||
+ '<div class="vv-cl-title">Join the partnership</div>'
|
||||
+ vvRenderMirrorOnboard({
|
||||
ownerName: vvDetectedIdentity.primary,
|
||||
termBase: `https://${window.location.hostname}/webterminal/ttyd/`,
|
||||
termCmd: <?= json_encode('bash ' . SCRIPTS_DIR . '/Partnership/partnership_onboard.sh --phase1-only') ?>,
|
||||
phase: vvDetectedIdentity.phase ?? 0,
|
||||
hasPartner: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Setup finishing used to change nothing on screen. The wizard simply stopped appearing on the
|
||||
// next load, because host*.conf now existed — so the last thing the operator saw was the same
|
||||
// list of rows they had been staring at, with no signal that the job was done or what came next.
|
||||
//
|
||||
// Says what is left when it is not complete, rather than only celebrating when it is: the count
|
||||
// of blocking rows is the actual answer to "can I stop now".
|
||||
function vvRenderDone(d) {
|
||||
const el = document.getElementById('vv-done-banner');
|
||||
if (!el) return;
|
||||
const partner = vvDetectedIdentity?.role === 'partner';
|
||||
|
||||
if (d.complete) {
|
||||
const next = partner
|
||||
? `Next: install this server's SSH key on ${vvEscHtml(vvDetectedIdentity.primary || 'the primary')} `
|
||||
+ `and join, both on the Partnership tab.`
|
||||
: 'Next: review the schedule, then onboard a partner when you have one.';
|
||||
el.innerHTML = `<div style="margin-top:16px;padding:10px 12px;border-radius:3px;background:#0d1f0d;
|
||||
border:1px solid #1a3a1a;color:#4caf50;font-size:12px;line-height:1.6;">
|
||||
✓ Setup complete — every required item is done.<br>
|
||||
<span style="color:#3a7a3a;">${next}</span></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const blocking = (d.items || []).filter(i => !i.ok && i.blocking);
|
||||
if (!blocking.length) { el.innerHTML = ''; return; }
|
||||
el.innerHTML = `<div style="margin-top:16px;padding:10px 12px;border-radius:3px;background:#1a1400;
|
||||
border:1px solid #3a2e00;color:#a80;font-size:12px;line-height:1.6;">
|
||||
${blocking.length} required item${blocking.length > 1 ? 's' : ''} left:
|
||||
<span style="color:#886;">${blocking.map(i => vvEscHtml(i.label)).join(', ')}</span><br>
|
||||
<span style="color:#665;">Anything marked optional can be dismissed with “Not now”.</span></div>`;
|
||||
}
|
||||
|
||||
function vvLoadChecklist() {
|
||||
fetch('/plugins/varaverk/api/checklist.php?_=' + Date.now())
|
||||
.then(r => r.json()).then(d => {
|
||||
const el = document.getElementById('vv-checklist');
|
||||
if (!d.ok || !d.items) { el.innerHTML = '<span style="color:#555">Unable to load checklist</span>'; return; }
|
||||
vvLabelExit();
|
||||
vvRenderOnboardPanel(d);
|
||||
vvRenderDone(d);
|
||||
el.innerHTML = d.items.map(item => {
|
||||
// Deferred reads as its own state, not as pass and not as fail: the thing is still not
|
||||
// done, the operator has said not now, and the row has to keep saying both.
|
||||
|
||||
Reference in New Issue
Block a user