HOST1 completes local onboard independently during Phase 1
partnership_manager.sh: - --local-only flag for --onboard: skips remote pre-flight, WebUI reconfig, state push; runs FolderView3 folder creation + writes HOST1_LOCAL_DONE=true to setup.db + pushes it; exits without writing ACTIVE (partnership not yet established until Phase 2) partnership_onboard.sh: - Phase 1 exit now runs partnership_manager.sh --onboard --local-only after SSH + conf push, so HOST1 is fully configured on its own side before Phase 2 include/partnership.php: - vv_pt_nodes() reads HOST1_LOCAL_DONE from setup.db; exposes as local_done on the self node (null for remote nodes) pages/partnership.php: - Self node card shows green "Local setup complete · waiting for partner" badge when local_done=true - Actions: "Complete HOST1 Setup" button appears when HOST1 hasn't run local setup yet but HOST2 is already at phase 1 — lets user trigger it manually - vvPtLocalSetup() top-level function calls partnership_manager.sh via run.php
This commit is contained in:
@@ -200,6 +200,8 @@ REASON="manual"
|
||||
UNBLOCK_HOST=""
|
||||
FILTERED_ARGS=()
|
||||
|
||||
LOCAL_ONLY=false # --onboard --local-only: skips remote steps, runs HOST1-local setup only
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--onboard) MODE="onboard" ;;
|
||||
@@ -208,6 +210,7 @@ for arg in "$@"; do
|
||||
--check) MODE="check" ;;
|
||||
--status) MODE="status" ;;
|
||||
--unblock) MODE="unblock" ;;
|
||||
--local-only) LOCAL_ONLY=true ;;
|
||||
--confirm=*) TRANSFER_CONFIRM_INPUT="${arg#--confirm=}" ;;
|
||||
--remote-seen) REMOTE_SEEN=true ;;
|
||||
--remote-unseen) REMOTE_UNSEEN=true ;;
|
||||
@@ -1408,6 +1411,51 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_FALLBACK Onboard — $(date '+%Y-%m-%d %H:%M:%S') ━━━"
|
||||
|
||||
# ── LOCAL-ONLY PATH ──────────────────────────────────────────────────────────
|
||||
# HOST1-local setup steps that don't need HOST2 present. Called from
|
||||
# partnership_onboard.sh --phase1-only so HOST1 can complete its own side
|
||||
# (FolderView3, setup.db flag) while waiting for HOST2 to install and onboard.
|
||||
if [[ "$LOCAL_ONLY" == true ]]; then
|
||||
log "Mode: local-only — skipping remote pre-flight and WebUI steps"
|
||||
log "Owner: $OWNER_ID ($OWNER) · Mirror: $MIRROR_ID ($MIRROR)"
|
||||
echo ""
|
||||
|
||||
if [[ "${PARTNERSHIP_FOLDERVIEW3:-false}" == true ]]; then
|
||||
echo "FolderView3 Integration"
|
||||
PARTNER_FOLDER_NAME=$(derive_partner_folder_name "$MIRROR")
|
||||
declare -a PARTNER_CONTAINERS=()
|
||||
gather_partner_fallback_containers PARTNER_CONTAINERS
|
||||
if [[ ${#PARTNER_CONTAINERS[@]} -gt 0 ]]; then
|
||||
folderview3_create_partner_folder "$PARTNER_FOLDER_NAME" "${PARTNER_CONTAINERS[@]}"
|
||||
else
|
||||
log "No FALLBACK_${MY_ID}_COVERS_${MIRROR_ID}_TIER* containers — skipping folder"
|
||||
fi
|
||||
else
|
||||
log "FolderView3 not configured — skipping"
|
||||
fi
|
||||
|
||||
local_state_file="/boot/config/varaverk_setup.db"
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
local flag_key="${MY_ID}_LOCAL_DONE"
|
||||
if grep -q "^${flag_key}=" "$local_state_file" 2>/dev/null; then
|
||||
sed -i "s|^${flag_key}=.*|${flag_key}=true|" "$local_state_file"
|
||||
else
|
||||
echo "${flag_key}=true" >> "$local_state_file"
|
||||
fi
|
||||
command -v php &>/dev/null && \
|
||||
php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true
|
||||
log "${MY_ID}_LOCAL_DONE=true written to setup.db ✅"
|
||||
else
|
||||
warn "DRY RUN — would write ${MY_ID}_LOCAL_DONE=true"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "HOST1 local setup complete. Waiting for $MIRROR_ID ($MIRROR) to onboard."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── FULL ONBOARD PATH (requires HOST2) ────────────────────────────────────
|
||||
|
||||
# Check already onboarded
|
||||
if [[ -f "$LOCAL_STATE_FILE" ]]; then
|
||||
CURRENT_STATE=$(read_state_file "$LOCAL_STATE_FILE" "state")
|
||||
|
||||
@@ -606,16 +606,23 @@ exit(\$failed > 0 ? 1 : 0);
|
||||
fi
|
||||
fi
|
||||
|
||||
# HOST1 local setup — runs immediately without needing HOST2
|
||||
echo ""
|
||||
echo "━━━ Phase 1 — HOST1 Local Setup ━━━"
|
||||
bash "$SCRIPT_DIR/partnership_manager.sh" --onboard --local-only "${EXTRA_FLAGS[@]}" || \
|
||||
warn "Local setup had issues — FolderView3 may need manual setup"
|
||||
|
||||
[[ "$DRY_RUN" == false ]] && write_onboard_phase "$MIRROR_ID" 1
|
||||
|
||||
END=$(date +%s)
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY PHASE 1 COMPLETE ━━━━━"
|
||||
echo " SSH keys: $( [[ "$STEP_SSH_OK" == true ]] && echo "ready ✅" || echo "skipped" )"
|
||||
echo " Conf push: $( [[ "$CONF_PUSH_OK" == true ]] && echo "done ✅" || echo "⚠ manual needed" )"
|
||||
echo " Duration: $(format_duration $(( END - START )))"
|
||||
echo " SSH keys: $( [[ "$STEP_SSH_OK" == true ]] && echo "ready ✅" || echo "skipped" )"
|
||||
echo " Conf push: $( [[ "$CONF_PUSH_OK" == true ]] && echo "done ✅" || echo "⚠ manual needed" )"
|
||||
echo " HOST1 setup: done ✅"
|
||||
echo " Duration: $(format_duration $(( END - START )))"
|
||||
echo ""
|
||||
echo " HOST2 ($MIRROR) can now install the Varaverk plugin."
|
||||
echo " HOST1 is fully set up. HOST2 ($MIRROR) can now install the Varaverk plugin."
|
||||
echo " The wizard will detect the pushed conf and take the correct path."
|
||||
echo " When HOST2 completes its onboard, it will automatically trigger Phase 2 here."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
@@ -151,10 +151,13 @@ function vv_pt_nodes(): array {
|
||||
? vv_pt_local_system()
|
||||
: ($ts['online'] && $ts['ip'] && $mySshKey ? vv_pt_remote_system($ts['ip'], $mySshKey) : []);
|
||||
|
||||
// Onboard phase from setup.db — 0=not started, 1=SSH+conf done, 2=fully onboarded
|
||||
$nodeIdUpper = strtoupper($slot);
|
||||
$onboardPhase = ($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
|
||||
: (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0);
|
||||
// Onboard phase from setup.db — null=self, 0=not started, 1=SSH+conf done, 2=fully onboarded
|
||||
$nodeIdUpper = strtoupper($slot);
|
||||
$onboardPhase = $isMe ? null
|
||||
: (($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
|
||||
: (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
|
||||
// For self: local setup complete flag (set by partnership_manager --onboard --local-only)
|
||||
$localDone = $isMe && ($setupDb[$nodeIdUpper . '_LOCAL_DONE'] ?? '') === 'true';
|
||||
|
||||
$nodes[] = [
|
||||
'slot' => $slot,
|
||||
@@ -168,7 +171,8 @@ function vv_pt_nodes(): array {
|
||||
'fallback' => $fbState,
|
||||
'partnership' => $ptDb,
|
||||
'system' => $system,
|
||||
'onboard_phase' => $isMe ? null : $onboardPhase,
|
||||
'onboard_phase' => $onboardPhase,
|
||||
'local_done' => $localDone,
|
||||
];
|
||||
}
|
||||
return $nodes;
|
||||
|
||||
@@ -106,6 +106,16 @@ function vvPtOnboard(btn) {
|
||||
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Onboard (Mirror)'; }, 4000));
|
||||
}
|
||||
|
||||
function vvPtLocalSetup(btn) {
|
||||
if (!confirm('Complete HOST1 local setup?\n\nRuns FolderView3 integration and marks HOST1 as locally ready.\nDoes not require HOST2 to be online.')) return;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⟳ Running…';
|
||||
_vvPtRun('Partnership/partnership_manager.sh', '--onboard --local-only')
|
||||
.then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
|
||||
.catch(e => alert('Error: ' + e))
|
||||
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Complete HOST1 Setup'; }, 4000));
|
||||
}
|
||||
|
||||
function vvPtCancel(btn, hostId) {
|
||||
if (!confirm(`Cancel Phase 1 for ${hostId}?\n\nThis will:\n• Remove HOST1's SSH key from ${hostId}'s authorized_keys\n• Delete the local key pair\n• Reset phase state on both hosts\n\nContinue?`)) return;
|
||||
btn.disabled = true;
|
||||
@@ -227,12 +237,15 @@ function _nodeCard(node) {
|
||||
const [ptCol, ptLbl] = ptStates[ptState] || ['#444', ptState || '—'];
|
||||
const ptUpdated = pt.updated ? _relTime(parseInt(pt.updated)) : null;
|
||||
|
||||
// Onboard phase (null = self, 0 = not started, 1 = SSH done, 2 = fully onboarded)
|
||||
// Onboard phase badge (remote nodes) or local-done badge (self)
|
||||
const phase = node.onboard_phase;
|
||||
const phaseHtml = phase === null ? '' :
|
||||
phase === 2 ? '' :
|
||||
phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting HOST2 onboard</div>` :
|
||||
`<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`;
|
||||
const phaseHtml = node.is_me
|
||||
? (node.local_done
|
||||
? `<div style="font-size:10px;color:#4caf50;margin-bottom:6px;padding:2px 6px;background:#0a1a0a;border:1px solid #2a5a2a;border-radius:3px;">✓ Local setup complete · waiting for partner</div>`
|
||||
: '')
|
||||
: (phase === 2 ? '' :
|
||||
phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting onboard</div>` :
|
||||
`<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`);
|
||||
|
||||
let body = '';
|
||||
|
||||
@@ -286,6 +299,24 @@ function _renderActions(nodes, cfg) {
|
||||
|
||||
let html = '';
|
||||
|
||||
// ── HOST1 local setup button (owner only, before local_done) ──────────────
|
||||
const selfNode = nodes.find(n => n.is_me);
|
||||
if (isOwner && selfNode && !selfNode.local_done && remotes.some(r => (r.onboard_phase ?? 0) >= 1)) {
|
||||
html += `<div style="margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #1e1e1e;">
|
||||
<div style="font-size:11px;color:#555;margin-bottom:6px;">${selfNode.id} — ${selfNode.hostname} <span style="color:#333;">(this server)</span></div>
|
||||
<div class="vv-pt-actions">
|
||||
<button class="vv-pt-action-btn run" onclick="vvPtLocalSetup(this)"
|
||||
title="Run HOST1-local onboard steps (FolderView3, setup state) without needing HOST2 to be present">
|
||||
▶ Complete HOST1 Setup
|
||||
</button>
|
||||
</div>
|
||||
<div style="font-size:10px;color:#444;margin-top:6px;">
|
||||
Configures HOST1's local side independently — FolderView3 folder, state flags.
|
||||
Does not require HOST2 to be online.
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ── Per-partner onboard phase actions (owner only) ─────────────────────────
|
||||
if (isOwner && hasPartner) {
|
||||
for (const remote of remotes) {
|
||||
|
||||
Reference in New Issue
Block a user