Push master.conf to all partners immediately on raw-editor save

vv_push_master_conf() in config.php: finds all remote HOST* entries,
resolves Tailscale IPs, SCPs master.conf to each.
rawconf.php calls it after a successful write when file=master.conf.
Scheduler save button shows sync status: "✓ Saved · synced to HOST2"
or "✓ Saved · push failed: HOST2" on error.

Also fix path bug in partnership_transfer.sh — was SCPing to
appdata root instead of Configurations/.
This commit is contained in:
Gmer4Lfe
2026-05-30 14:21:02 -04:00
parent 26f3778786
commit da0c303f22
4 changed files with 59 additions and 7 deletions
+2 -2
View File
@@ -294,8 +294,8 @@ if [[ "$DRY_RUN" == false ]]; then
scp -i "$SSH_KEY" \
-o ConnectTimeout="$SSH_TIMEOUT" \
-o StrictHostKeyChecking=no \
"$SCRIPTS_ROOT/master.conf" \
"root@${MIRROR_IP}:/mnt/user/appdata/Varaverk/master.conf" 2>/dev/null && \
"$SCRIPTS_ROOT/Configurations/master.conf" \
"root@${MIRROR_IP}:/mnt/user/appdata/Varaverk/Configurations/master.conf" 2>/dev/null && \
log "master.conf pushed to $NEW_OWNER" || \
error "Failed to push master.conf to $NEW_OWNER — set PARTNERSHIP_OWNER_HOST=\"$NEW_OWNER_ID\" manually"
else
+3 -1
View File
@@ -22,7 +22,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo json_encode(['ok' => false, 'error' => 'Not allowed']);
exit;
}
echo json_encode(['ok' => vv_write_conf_raw($file, $content)]);
$written = vv_write_conf_raw($file, $content);
$push = ($written && $file === 'master.conf') ? vv_push_master_conf() : [];
echo json_encode(['ok' => $written, 'push' => $push]);
exit;
}
+41
View File
@@ -10,6 +10,47 @@ define('CONF_DIR', SCRIPTS_DIR . '/Configurations');
define('LOG_DIR', '/var/log/varaverk');
unset($_vv_cfg);
// Push master.conf to all remote hosts via scp after a local save.
// Returns one result entry per remote found in master.conf.
// Silently returns [] on non-owner hosts (no SSH key, no remote access).
function vv_push_master_conf(): array {
$myHostId = vv_detect_host();
$vars = vv_conf_vars();
$sshKey = $vars[strtoupper($myHostId) . '_SSH_KEY'] ?? '';
if (!$sshKey || !file_exists($sshKey)) return [];
$localPath = CONF_DIR . '/master.conf';
$master = vv_read_conf_raw('master.conf');
preg_match_all('/^\s*(HOST\d+)(?:_NAME)?\s*=\s*["\']?(\S+?)["\']?\s*$/m', $master, $m);
$results = [];
$seen = [];
foreach ($m[1] as $i => $hostKey) {
$hostId = strtolower($hostKey);
if ($hostId === $myHostId || isset($seen[$hostId])) continue;
$seen[$hostId] = true;
$hostname = trim($m[2][$i]);
$ip = vv_resolve_tailscale_ip($hostname);
if (!$ip) {
$results[] = ['host' => $hostKey, 'ok' => false, 'error' => 'Tailscale IP not found'];
continue;
}
$dest = escapeshellarg('root@' . $ip . ':' . CONF_DIR . '/master.conf');
$cmd = 'scp -i ' . escapeshellarg($sshKey)
. ' -o ConnectTimeout=10 -o StrictHostKeyChecking=no'
. ' ' . escapeshellarg($localPath) . ' ' . $dest . ' 2>&1';
exec($cmd, $out, $rc);
$results[] = [
'host' => $hostKey,
'ok' => $rc === 0,
'error' => $rc !== 0 ? implode('; ', $out) : '',
];
}
return $results;
}
function vv_get_hostname(): string {
return trim(shell_exec('hostname -s') ?: '');
}
+13 -4
View File
@@ -2593,13 +2593,22 @@ function vvSaveRawConf() {
vvPost('/plugins/varaverk/api/rawconf.php', {file: vvRawConfFile, content})
.then(d => {
btn.disabled = false;
if (d.ok) {
btn.textContent = '✓ Saved';
setTimeout(() => { btn.textContent = 'Save Conf'; }, 2500);
} else {
if (!d.ok) {
btn.textContent = 'Save Conf';
alert('Save failed: ' + (d.error ?? 'Unknown error'));
return;
}
const push = d.push ?? [];
if (push.length === 0) {
btn.textContent = '✓ Saved';
} else if (push.every(p => p.ok)) {
const hosts = push.map(p => p.host).join(', ');
btn.textContent = '✓ Saved · synced to ' + hosts;
} else {
const failed = push.filter(p => !p.ok).map(p => p.host).join(', ');
btn.textContent = '✓ Saved · push failed: ' + failed;
}
setTimeout(() => { btn.textContent = 'Save Conf'; }, 3500);
})
.catch(() => { btn.disabled = false; btn.textContent = 'Save Conf'; });
}